> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xquik.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CrewAI

> Build a Twitter research crew with Xquik's MCP tools and CrewAI

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

Build a CrewAI crew that can search tweets, hand off IDs and cursors, monitor accounts, replay stored monitor events, and run extraction jobs - all powered by Xquik's MCP server.

## Prerequisites

* Python 3.10+
* [Xquik API key](/quickstart) (`xq_...`)
* An LLM API key (OpenAI, Anthropic, or any CrewAI-supported provider)

## Install

```bash theme={null}
pip install crewai
```

MCP support is built into CrewAI core - no extra packages needed.

## Full Example

```python theme={null}
from pathlib import Path
from crewai import Agent, Task, Crew, Process
from crewai.mcp import MCPServerHTTP

researcher = Agent(
    role="X Research Analyst",
    goal="Find and analyze trending conversations on X",
    backstory="Expert social media analyst with deep experience in trend analysis",
    mcps=[
        MCPServerHTTP(
            url="https://xquik.com/mcp",
            headers={"x-api-key": "xq_YOUR_KEY_HERE"},
        ),
    ],
)

task = Task(
    description="Search for the latest tweets about AI agents. Return compact JSON with query, route_used, tweets[{tweet_id,text,author_username,created_at}], has_more, next_cursor, and key influencers.",
    expected_output="Compact JSON with tweet rows, pagination state, and influencer notes",
    agent=researcher,
)

crew = Crew(
    agents=[researcher],
    tasks=[task],
    process=Process.sequential,
    verbose=True,
)

result = crew.kickoff()
Path("xquik-crewai-handoff.json").write_text(str(result), encoding="utf-8")
```

The `mcps` field on `Agent` auto-discovers all Xquik tools and makes them available to the agent. No manual tool wiring needed.

The MCP runtime returns normalized snake\_case fields through `xquik.request()`, so keep CrewAI tasks aligned with `tweet_id`, `has_more`, `next_cursor`, and returned job IDs.

## Handoff Checklist

<CardGroup cols={2}>
  <Card title="Tweet search rows" icon="search">
    Store `tweet_id`, `text`, `author_username`, `created_at`, `has_more`, `next_cursor`, and the original `q`.
  </Card>

  <Card title="User profile rows" icon="users">
    Store source `id` as `user_id`, plus `username`, `name`, `followers`, `verified`, `profile_picture`, `has_more`, `next_cursor`, and the source lookup or search query.
  </Card>

  <Card title="Trend rows" icon="trending-up">
    Store each trend `name`, `rank`, `query`, and `description`. Keep response `count`, `woeid`, and the requested region with the run checkpoint.
  </Card>

  <Card title="Monitor and webhook setup" icon="radio">
    Store the returned monitor `id` as `monitor_id`, `event_types`, `next_billing_at`, the returned webhook `id` as `webhook_id`, `url`, and the one-time `secret` in a secret manager. On production deliveries, store `delivery_id` for receiver retry de-dupe and `stream_event_id` when one monitor event should process once across endpoint changes.
  </Card>

  <Card title="Stored event replay" icon="activity">
    Store `event_id`, `type`, `monitor_id`, `monitor_type`, `occurred_at`, `has_more`, `next_cursor`, and the `after` query for the next page.
  </Card>

  <Card title="Extraction jobs" icon="database">
    Store `extraction_id`, `status`, `poll`, and `export_after_complete`; poll before loading CSV, JSON, or XLSX rows.
  </Card>

  <Card title="Writes" icon="send">
    Store `tweet_id` or `write_action_id`, `reply_to_tweet_id`, `status`, `charged_credits`, and `poll`; do not resend pending writes.
  </Card>

  <Card title="Media attachments" icon="image">
    For tweets or replies, pass public URLs in `media` and store `tweet_id` or `write_action_id`. For DMs, upload first, pass one `media_id` in `media_ids`, store `message_id`, and leave `reply_to_message_id` unset.
  </Card>
</CardGroup>

## Multi-Agent Crew

Use multiple agents with different roles sharing the same MCP server:

```python theme={null}
from pathlib import Path
from crewai import Agent, Task, Crew, Process
from crewai.mcp import MCPServerHTTP

xquik_mcp = MCPServerHTTP(
    url="https://xquik.com/mcp",
    headers={"x-api-key": "xq_YOUR_KEY_HERE"},
)

researcher = Agent(
    role="X Researcher",
    goal="Gather compact JSON handoff rows from X about a given topic",
    backstory="Data collection specialist who finds relevant tweets and profiles",
    mcps=[xquik_mcp],
)

analyst = Agent(
    role="Engagement Analyst",
    goal="Analyze engagement patterns and identify trends",
    backstory="Data analyst who turns structured social data into actionable insights",
    mcps=[xquik_mcp],
)

writer = Agent(
    role="Report Writer",
    goal="Write a concise executive summary from the analysis",
    backstory="Technical writer who creates clear, data-driven reports",
)

research_task = Task(
    description="Search X for tweets about '{topic}' from the last 24 hours. Collect the top 20 by engagement.",
    expected_output="Compact JSON with tweet_id, author_username, text, created_at, has_more, next_cursor, and route_used",
    agent=researcher,
)

analysis_task = Task(
    description="Analyze the collected tweets. Identify sentiment distribution, peak posting times, and top contributors.",
    expected_output="Structured analysis with charts-ready data",
    agent=analyst,
)

report_task = Task(
    description="Write a 3-paragraph executive summary of the findings.",
    expected_output="Executive summary in markdown format",
    agent=writer,
)

crew = Crew(
    agents=[researcher, analyst, writer],
    tasks=[research_task, analysis_task, report_task],
    process=Process.sequential,
    verbose=True,
)

result = crew.kickoff(inputs={"topic": "AI agents"})
Path("xquik-crewai-workflow-handoff.json").write_text(
    str(result),
    encoding="utf-8",
)
```

## Tool Filtering

Restrict which Xquik tools an agent can access:

```python theme={null}
from crewai.mcp import MCPServerHTTP
from crewai.mcp.filters import create_static_tool_filter

# Only allow read operations - no posting, liking, or following
researcher = Agent(
    role="Read-Only Researcher",
    goal="Gather data without modifying anything",
    backstory="Researcher with read-only access",
    mcps=[
        MCPServerHTTP(
            url="https://xquik.com/mcp",
            headers={"x-api-key": "xq_YOUR_KEY_HERE"},
            tool_filter=create_static_tool_filter(
                allowed_tool_names=["explore"],
            ),
        ),
    ],
)
```

## Environment Variables

```bash .env theme={null}
XQUIK_API_KEY=xq_YOUR_KEY_HERE
OPENAI_API_KEY=sk-...
```

```python theme={null}
import os

xquik_mcp = MCPServerHTTP(
    url="https://xquik.com/mcp",
    headers={"x-api-key": os.environ["XQUIK_API_KEY"]},
)
```

## Package Versions

| Package  | Version |
| -------- | ------- |
| `crewai` | 1.14.1+ |
| `mcp`    | 1.26.0+ |
