> ## 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 Twitter MCP multi-agent guide for Python

> Build a CrewAI Twitter MCP crew for tweet search, profiles, followers, monitors, exports, and reviewed X actions with typed Python agent handoffs safely.

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

Build a CrewAI MCP integration through Xquik's remote Twitter MCP server. Give CrewAI agents controlled tweet searches, profiles, follower exports, monitors, and reviewed X actions. Keep every tweet ID, profile ID, cursor, and job ID.

## Why use CrewAI with MCP for a Twitter API?

CrewAI offers an agent framework for complex tasks. Give each agent one role. Xquik supplies documentation search and Twitter API operations through `docs`, `search`, and `execute`.

| Boundary | CrewAI control | Benefit |
| - | - | - |
| Remote MCP | `MCPServerHTTP` | Reach tweet, profile, follower, and monitor routes |
| Handoff | Pydantic `output_pydantic` | Reject malformed tweets and cursors |
| Sequence | `Process.sequential` | Pass exact tweets between specialists |
| Discovery | Static tool filter | Expose schemas without execution |
| Review | Tool-free task | Review X actions before writes |
| Failures | `has_tool_failures` | Stop incomplete research |

This CrewAI multi agent pattern fits research, verification, and reporting. Use direct REST for deterministic jobs without model decisions.

## CrewAI Twitter API prerequisites

* Python 3.10 through 3.13
* An [Xquik API key](/x-api-quickstart) beginning with `xq_`
* An LLM provider key supported by CrewAI
* A connected X account for private reads or X write actions

Public X reads need no X Developer credentials. Authenticate with Xquik. Connect an X account only for routes that require one.

## Install CrewAI MCP support

CrewAI core includes a native MCP client. This CrewAI Python setup needs no custom adapter.

```bash theme={null}
python -m pip install "crewai>=1.15,<1.16"
```

CrewAI 1.15 requires MCP 1.28. Avoid MCP 2.x with this release. Store secrets outside source control.

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

```text .gitignore theme={null}
.env
xquik-*-handoff.json
```

## Build a typed CrewAI tweet search agent

Start with the expected output, then build the task. CrewAI validates the final handoff against its Pydantic model.

```python theme={null}
import os
from pathlib import Path
from typing import Literal

from crewai import Agent, Crew, Process, Task
from crewai.mcp import MCPServerHTTP
from pydantic import BaseModel


class TweetRow(BaseModel):
    tweet_id: str
    text: str
    author_username: str | None
    created: int | None
    url: str | None


class TweetSearchHandoff(BaseModel):
    query: str
    route_used: Literal["GET /api/v1/x/tweets/search"]
    tweets: list[TweetRow]
    has_more: bool
    next_cursor: str | None
    pages_fetched: int
    stop_reason: Literal[
        "complete",
        "requested_limit",
        "cursor_stalled",
    ]


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

researcher = Agent(
    role="Twitter API Researcher",
    goal="Return exact tweet records and resumable pagination state",
    backstory=(
        "You inspect Twitter conversations through documented Xquik routes. "
        "You preserve source IDs and never invent missing fields."
    ),
    llm="openai/gpt-5",
    mcps=[xquik_mcp],
    allow_delegation=False,
    verbose=False,
)

search_task = Task(
    description=(
        "Use GET /api/v1/x/tweets/search. "
        "Search 50 latest tweets about CrewAI Twitter MCP. "
        "Preserve exact tweet IDs, created timestamps, and cursors. "
        "Stop at the requested limit. Stop if a cursor repeats."
    ),
    expected_output="A validated tweet search handoff with pagination state.",
    agent=researcher,
    output_pydantic=TweetSearchHandoff,
)

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

result = crew.kickoff()
if result.has_tool_failures:
    raise RuntimeError("Twitter MCP tool failed. Inspect result.tool_failures.")

handoff = TweetSearchHandoff.model_validate(result.to_dict())
Path("xquik-crewai-handoff.json").write_text(
    handoff.model_dump_json(indent=2),
    encoding="utf-8",
)
```

`Agent.mcps` discovers CrewAI MCP tools before execution. `MCPServerHTTP` uses Streamable HTTP by default.

The MCP runtime returns normalized snake\_case fields through `xquik.request()`. It maps `createdAt` to the Unix-second field `created`.

Always inspect `has_tool_failures`. Never pass incomplete results into follower exports or X actions.

## Search tweets with focused queries

Send a precise `q`. Keep the exact query in the handoff.

| Intent | Example `q` |
| - | - |
| Framework posts | `"CrewAI" MCP` |
| Account timeline | `from:crewAIInc since:2026-07-01 until:2026-08-01` |
| Twitter API Python posts | `"Twitter API" Python lang:en min_faves:25` |
| Questions | `"multi-agent workflow" ? -filter:retweets` |

Use `queryType=Latest` for real time monitoring. Use `Top` for engagement-ranked research. See the [tweet search API contract](/api-reference/x/search-tweets).

Pass `next_cursor` unchanged. Stop when `has_more` is false or cursors repeat. Continue empty pages with true `has_more`. Deduplicate by `tweet_id`.

## Build a role-based tweet research crew

Give only the researcher access to Twitter MCP search tools. Feed its validated task into a tool-free analyst.

```python theme={null}
class TweetAnalysis(BaseModel):
    query: str
    analyzed_tweet_ids: list[str]
    recurring_topics: list[str]
    top_author_usernames: list[str]
    next_cursor: str | None


analyst = Agent(
    role="Tweet Conversation Analyst",
    goal="Analyze only the supplied tweet rows",
    backstory="You compare exact tweets without fetching extra records.",
    llm="openai/gpt-5",
    allow_delegation=False,
    verbose=False,
)

analysis_task = Task(
    description=(
        "Analyze the supplied tweet rows. "
        "Keep every analyzed tweet_id. Preserve the next_cursor."
    ),
    expected_output="A typed topic analysis tied to source tweet IDs.",
    agent=analyst,
    context=[search_task],
    output_pydantic=TweetAnalysis,
)

research_crew = Crew(
    agents=[researcher, analyst],
    tasks=[search_task, analysis_task],
    process=Process.sequential,
    verbose=False,
)
```

The `context` list passes the first result into the second. The analyst cannot fetch unrelated tweets or profiles.

Choose hierarchical delegation when specialists work independently. Use sequential tasks for cursor-dependent agent collaboration.

## Apply CrewAI MCP integration patterns

Each CrewAI agent with MCP server access gets one permission boundary. Teams of AI agents must not share write-capable keys.

This CrewAI MCP integration connects external APIs through synchronized schemas. Prefer CrewAI tools before building a CrewAI custom tool.

The `tools` tool list shows permitted agent actions. Review agent tools before each tool integration.

`import tool` shortcuts and `def run` wrappers duplicate native MCP behavior.

Avoid broad web searches when tweet IDs matter. Set `verbose=True` only while debugging complex tasks.

Multi agent systems in real world AI applications need one key per agent.

## Keep Twitter actions outside the research crew

The `execute` tool runs every route allowed by its key. Never give write permissions to autonomous research crews. Use guest `paid_reads` for eligible paid-read routes. The [guest wallets guide](/guides/guest-wallets) documents this scope.

```python theme={null}
class TweetWritePlan(BaseModel):
    account_id: str
    text: str
    reply_to_tweet_id: str | None
    media_urls: list[str]
    idempotency_key: str


planner = Agent(
    role="Twitter Action Planner",
    goal="Prepare one reviewable X action without executing it",
    backstory="You preserve approved text, target IDs, and account IDs.",
    llm="openai/gpt-5",
    tools=[],
    allow_delegation=False,
)

plan_task = Task(
    description="Prepare a tweet or reply plan from reviewed source tweets.",
    expected_output="One typed action plan. Do not execute any X request.",
    agent=planner,
    output_pydantic=TweetWritePlan,
    human_input=True,
)
```

`human_input=True` reviews the result, while `tools=[]` blocks execution. After approval, send one REST request. Never retry pending writes automatically.

## Expose endpoint discovery only

Expose only `search` for endpoint discovery.

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


discovery_mcp = MCPServerHTTP(
    url="https://xquik.com/mcp",
    headers={"x-api-key": os.environ["XQUIK_API_KEY"]},
    tool_filter=create_static_tool_filter(
        allowed_tool_names=["search"],
    ),
    cache_tools_list=True,
)
```

This filter cannot execute Twitter API calls. `search` returns methods, parameters, and response fields. Adding `execute` enables authorized execution.

## Use one MCP configuration per tenant

Resolve the tenant first. Then create its `MCPServerHTTP` configuration.

```python theme={null}
def build_tenant_mcp(user_api_key: str) -> MCPServerHTTP:
    return MCPServerHTTP(
        url="https://xquik.com/mcp",
        headers={"x-api-key": user_api_key},
        streamable=True,
        cache_tools_list=True,
    )
```

Never share agents across tenant keys. Keep keys outside prompts, output, memory, traces, and handoffs. Reuse one configuration per crew run.

## Store a resumable CrewAI handoff

Store identifiers and checkpoints outside task prose.

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

  <Card title="Follower exports" icon="users">
    Store `id` as `user_id`. Keep `username`, `followers`, `has_more`, and `next_cursor`.
  </Card>

  <Card title="Monitor events" icon="radio">
    Store `monitor_id`, `event_id`, `occurred_at`, `next_cursor`, and `cursor`.
  </Card>

  <Card title="Webhook deliveries" icon="webhook">
    Store `webhook_id`, `delivery_id`, and `stream_event_id`. Protect `secret` separately.
  </Card>

  <Card title="Extraction jobs" icon="database">
    Store `extraction_id`, `status`, `poll`, and `export_after_complete`.
  </Card>

  <Card title="Approved X actions" icon="send">
    Store `tweet_id`, `write_action_id`, `status`, `charged_credits`, and `poll`. Never resend pending writes.
  </Card>
</CardGroup>

Store `result.pydantic` or `result.to_dict()`. Avoid free-form `result.raw`.

## Handle Twitter API errors and tool failures

The tweet search contract documents these responses. Keep their meanings separate.

| Status | Meaning | Crew action |
| - | - | - |
| `400` | Missing or invalid query | Fix `q`. Never retry unchanged |
| `401` | Guest authentication failed | Provide an Xquik key or connect X |
| `402` | Credits are unavailable | Stop and request account action |
| `424` | X dependency failed | Retry with bounded backoff |
| `429` | Twitter rate limit applies | Wait, then resume the cursor |
| `502` | Invalid X response | Retry later without changing IDs |

Inspect `result.tool_failures` after failures. Log the route, safe message, and task index.

After `429`, keep `next_cursor` and completed tweet IDs. Deduplicate by `tweet_id` after recovery.

POST and DELETE routes use different statuses. Read each route before retrying. See [error handling](/guides/error-handling).

## Verified CrewAI package versions

Xquik checked these versions on August 2, 2026.

| Package | Checked compatible version | Compatible range used here |
| - | - | - |
| `crewai` | 1.15.10 | `>=1.15,<1.16` |
| `mcp` | 1.28.1 | `>=1.28.1,<1.29` through CrewAI |
| `pydantic` | 2.12.5 | `>=2.11.9,<2.13` through CrewAI |

CrewAI 1.15 supports Python 3.10 through 3.13. Review release notes before
widening these ranges.

## CrewAI Twitter MCP questions

### What does the CrewAI MCP server do?

It exposes Xquik route discovery and execution. Agents search tweets, export followers, replay monitors, and plan reviewed writes.

### How do I search tweets with Python and CrewAI?

Call `GET /api/v1/x/tweets/search`. Keep `q`, `tweet_id`, `created`, and `next_cursor`.

### Can CrewAI export Twitter followers?

Use the [followers API](/api-reference/x/followers). Choose extraction jobs for CSV, JSON, or XLSX exports.

### How should CrewAI handle agent roles and permissions?

Give researchers MCP access. Give planners no tools. Review writes through the [create tweet contract](/api-reference/x-write/create-tweet).

### How should CrewAI handle Twitter API rate limits?

Save the cursor and completed tweet IDs. Resume after reset guidance. Never restart pagination.

### Why are CrewAI MCP tools missing?

Check the URL, key, versions, and transport. Review [CrewAI GitHub issues](https://github.com/crewAIInc/crewAI/issues) for current defects.

### CrewAI MCP or direct REST?

Choose CrewAI MCP for role-based decisions and typed handoffs. Choose REST for fixed routes, scheduled exports, and predictable latency.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.