> ## 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.

# Microsoft Agent Framework Twitter MCP Python guide

> Build a Microsoft Agent Framework Twitter MCP agent for tweet search, profiles, followers, monitors, exports, and approved X actions with typed Python output.

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

Use this Microsoft Agent Framework tutorial to build a Twitter
MCP agent. This Microsoft Agent Framework Python example connects to Xquik's
remote MCP server. Search tweets, inspect profiles, export followers, and replay
monitors. Review X actions before execution. Keep every tweet ID, profile
ID, cursor, and job identifier unchanged.

Connect the Microsoft Agent Framework MCP server client through Streamable HTTP.

## Why use the Microsoft AI agent framework with a Twitter API?

The Microsoft open-source agent framework provides agents, sessions, tools,
middleware, and workflows. It supports Python and .NET. Use this Microsoft AI
agent framework when Microsoft chat clients fit your stack.

These parts support single-agent tasks and multi-agent systems. Xquik
publishes Twitter API operations through 3 MCP tools: `docs`, `search`, and `execute`.

Use each boundary for one responsibility.

| Boundary | Framework control | Twitter agent benefit |
| - | - | - |
| Remote MCP | `MCPStreamableHTTPTool` | Connect agents to tweet, profile, follower, and monitor routes |
| Final response | Pydantic `response_format` | Reject malformed tweet rows and pagination state |
| Tenant context | `header_provider` | Resolve one Xquik key for each agent run |
| X actions | MCP `approval_mode` | Pause before the shared `execute` tool runs |
| Agent handoff | `AgentSession` and typed models | Keep IDs without copying whole transcripts |
| Tool scope | `allowed_tools` | Expose documentation, contract search, or execution tools |

Choose it for Python services requiring model-selected tool calls. Choose direct
REST for fixed jobs without model choices.

## Microsoft Agent Framework getting started

* Python 3.10 or later
* An [Xquik API key](/x-api-quickstart) beginning with `xq_`
* An LLM provider key supported by Microsoft Agent Framework
* Connect an X account for private reads or X write actions

Public X reads do not require X Developer credentials. Authenticate with
Xquik. Connect an X account only when the route requires it.

Keep provider keys and Xquik keys in separate environment variables. Version
1.13.0 protects initialization requests and later tool invocations. Pin it when
building AI agents with tenant credentials.

Choose only AI models supported by the configured chat client.

## Install Microsoft Agent Framework MCP support

Install the tested Python release.

```bash theme={null}
python -m pip install "agent-framework==1.13.0"
```

Store secrets outside source control.

```bash .env theme={null}
XQUIK_READ_ONLY_API_KEY=xq_YOUR_PAID_READS_KEY
XQUIK_API_KEY=xq_YOUR_WRITE_CAPABLE_KEY
OPENAI_API_KEY=YOUR_OPENAI_KEY
OPENAI_CHAT_MODEL=gpt-5
```

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

## Microsoft Agent Framework Python example for tweet search

Define the handoff before creating the agent. This example uses
Model Context Protocol (MCP) tool calls. The framework parses the final response
into the Pydantic model.

Use a guest `paid_reads` key for this autonomous example. Never give an
unattended research agent a write-capable key.

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

from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.openai import OpenAIChatClient
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",
    ]


async def main() -> None:
    xquik_mcp = MCPStreamableHTTPTool(
        name="xquik-twitter-api",
        url="https://xquik.com/mcp",
        description="Search tweets, profiles, followers, monitors, and exports.",
        allowed_tools=["docs", "search", "execute"],
        header_provider=lambda kwargs: {
            "x-api-key": kwargs["xquik_api_key"],
        },
    )

    async with Agent(
        client=OpenAIChatClient(model=os.environ["OPENAI_CHAT_MODEL"]),
        name="xquik_tweet_search_agent",
        instructions=(
            "Use GET /api/v1/x/tweets/search. "
            "Preserve exact tweet IDs, created timestamps, and cursors. "
            "Never invent missing tweet fields. "
            "Stop at the requested limit. Stop if a cursor repeats."
        ),
        tools=xquik_mcp,
    ) as agent:
        result = await agent.run(
            "Search 50 latest tweets about Microsoft Agent Framework MCP.",
            options={"response_format": TweetSearchHandoff},
            function_invocation_kwargs={
                "xquik_api_key": os.environ["XQUIK_READ_ONLY_API_KEY"],
            },
        )

        if not isinstance(result.value, TweetSearchHandoff):
            raise RuntimeError("Tweet handoff invalid. Inspect the agent response.")

        Path("xquik-microsoft-agent-handoff.json").write_text(
            result.value.model_dump_json(indent=2),
            encoding="utf-8",
        )


asyncio.run(main())
```

`allowed_tools` names the available remote tools. The agent receives only
`docs`, `search`, and `execute` from Xquik's MCP server.

The agent framework import loads `Agent` and `MCPStreamableHTTPTool`.

Agent framework agents use only the selected Xquik MCP tools.

`response_format` asks the model for the Pydantic shape. Read the validated
object from `result.value`. Never store `result.text` as a typed handoff.

The MCP runtime returns normalized snake\_case fields through
`xquik.request()`. It normalizes `createdAt` to the Unix-second field
`created`. Keep the Pydantic model aligned with that contract.

## Search tweets with focused queries

Send a precise `q` to the tweet search route. Keep it in the handoff.

| Search intent | Example `q` |
| - | - |
| Framework discussions | `"Microsoft Agent Framework" MCP` |
| One account's timeline | `from:Microsoft since:2026-07-01 until:2026-08-01` |
| Popular Python posts | `"Twitter API" Python lang:en min_faves:25` |
| Questions without reposts | `"tweet search agent" ? -filter:retweets` |
| Customer support mentions | `@brand (help OR issue) -filter:retweets` |
| Exact tweet | A numeric Tweet ID or X status URL |

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

Pass `next_cursor` back unchanged. Never rebuild a cursor. Stop when
`has_more` becomes false. Also stop when a cursor repeats.

Continue through an empty page when `has_more` remains true. Deduplicate
resumed results by `tweet_id`.

## Add human-in-the-loop approval for Twitter actions

Xquik exposes one `execute` tool. It runs routes authorized by the API
key.

Tool-name checks cannot distinguish routes inside `execute`. Apply approval to
`execute` whenever a key authorizes writes.

Ask a human in the loop to inspect every write-capable request.

```python theme={null}
from agent_framework import Agent, MCPStreamableHTTPTool, Message

approved_mcp = MCPStreamableHTTPTool(
    name="xquik-twitter-api",
    url="https://xquik.com/mcp",
    allowed_tools=["docs", "search", "execute"],
    approval_mode={
        "never_require_approval": ["search"],
        "always_require_approval": ["execute"],
    },
    header_provider=lambda kwargs: {
        "x-api-key": kwargs["xquik_api_key"],
    },
)

async with Agent(
    client=OpenAIChatClient(model=os.environ["OPENAI_CHAT_MODEL"]),
    name="approved_xquik_agent",
    instructions="Show exact routes and arguments before every Xquik request.",
    tools=approved_mcp,
) as agent:
    session = agent.create_session()
    run_options = {"response_format": TweetSearchHandoff}
    run_context = {"xquik_api_key": os.environ["XQUIK_API_KEY"]}

    result = await agent.run(
        "Search tweets about Python Twitter MCP agents.",
        session=session,
        options=run_options,
        function_invocation_kwargs=run_context,
    )

    while result.user_input_requests:
        responses = []
        for request in result.user_input_requests:
            if request.function_call is None:
                responses.append(
                    request.to_function_approval_response(approved=False)
                )
                continue

            print("Function:", request.function_call.name)
            print("Arguments:", request.function_call.arguments)
            answer = await asyncio.to_thread(input, "Approve this call? [y/N] ")
            responses.append(
                request.to_function_approval_response(
                    approved=answer.strip().lower() == "y"
                )
            )

        result = await agent.run(
            Message(role="user", contents=responses),
            session=session,
            options=run_options,
            function_invocation_kwargs=run_context,
        )
```

Show the route, method, arguments, text, and selected X account. Reject any
request whose method, arguments, text, or account changed.

For public research, a guest `paid_reads` key supplies a hard read-only
boundary. It exposes only eligible paid-read routes. Review [guest wallet
permissions](/guides/guest-wallets) before granting autonomous access.

## Scope Microsoft Agent Framework tools for discovery

Expose only `search` when an agent should inspect endpoint schemas.

```python theme={null}
discovery_mcp = MCPStreamableHTTPTool(
    name="xquik-route-discovery",
    url="https://xquik.com/mcp",
    allowed_tools=["search"],
    header_provider=lambda kwargs: {
        "x-api-key": kwargs["xquik_api_key"],
    },
)
```

This configuration cannot execute Twitter API operations. Adding `execute`
enables every operation authorized by the key.

Use `search` before an unfamiliar operation. It returns routes, parameters,
and response fields. Discovery never executes an X operation.

## Authenticate the Microsoft Agent Framework MCP client

Resolve one Xquik key for every run. Pass it through runtime context.

```python theme={null}
def tenant_headers(kwargs: dict[str, object]) -> dict[str, str]:
    tenant_id = str(kwargs["tenant_id"])
    return {"x-api-key": secret_store.get_xquik_key(tenant_id)}


tenant_mcp = MCPStreamableHTTPTool(
    name="tenant-xquik-twitter-api",
    url="https://xquik.com/mcp",
    allowed_tools=["docs", "search", "execute"],
    header_provider=tenant_headers,
)

async with Agent(
    client=OpenAIChatClient(model=os.environ["OPENAI_CHAT_MODEL"]),
    name="tenant_twitter_agent",
    instructions="Preserve exact profile IDs and usernames.",
    tools=tenant_mcp,
) as tenant_agent:
    result = await tenant_agent.run(
        "Look up the profile for microsoft.",
        function_invocation_kwargs={"tenant_id": authenticated_tenant_id},
    )
```

The `secret_store` represents your existing secret manager. Never include the
returned key in prompts, sessions, handoffs, or logs.

Microsoft Agent Framework 1.13.0 applies runtime headers during initialization
and tool calls. It limits those headers to matching-origin requests. Create an
isolated `httpx.AsyncClient` for each authenticated origin.

## Build Microsoft multi-agent framework workflows

Teams evaluating a Microsoft multi-agent framework should isolate every agent's
tools. Give only the researcher access to Twitter MCP. Pass its validated
handoff to a tool-free reviewer.

```python theme={null}
researcher = Agent(
    client=client,
    name="tweet_researcher",
    instructions="Return validated tweet rows and pagination state.",
    tools=xquik_mcp,
)

reviewer = Agent(
    client=client,
    name="tweet_reviewer",
    instructions="Analyze only supplied tweets. Preserve every tweet_id.",
)

research = await researcher.run(
    "Search latest tweets about Python agent frameworks.",
    options={"response_format": TweetSearchHandoff},
    function_invocation_kwargs={"xquik_api_key": tenant_api_key},
)

if not isinstance(research.value, TweetSearchHandoff):
    raise RuntimeError("Tweet research invalid. Stop the handoff.")

review = await reviewer.run(research.value.model_dump_json())
```

The reviewer receives no MCP tools. These multi-agent workflows prevent
unauthorized Twitter API execution. The reviewer cannot fetch unrelated tweets
or post X actions.

## Handle tweet search errors and rate limits

The tweet search contract documents these responses. Handle each status
independently.

| Status | Meaning | Agent action |
| - | - | - |
| `400` | The search query is missing or invalid | Fix `q`. Do not retry unchanged |
| `401` | Authentication cannot complete this request | Provide an Xquik key or connect X |
| `402` | The account lacks credits | Stop and request account action |
| `424` | The upstream X dependency failed | Retry with bounded backoff |
| `429` | The Twitter API rate limit applies | Wait for reset guidance, then resume the cursor |
| `502` | The X dependency returned an invalid response | Retry later without changing IDs |

Do not restart pagination after `429`. Keep `next_cursor` and completed
tweet IDs. Deduplicate by `tweet_id` after recovery.

POST and DELETE routes document different statuses. Derive retry behavior from
each route's documented statuses. See [error handling](/guides/error-handling).

## Store Microsoft Agent Framework workflow handoffs

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

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

  <Card title="Monitor and webhook state" icon="radio">
    Store `monitor_id`, `event_types`, `next_billing_at`, `webhook_id`, and `url`. Keep the one-time webhook `secret` in a secret manager.
  </Card>

  <Card title="Stored event replay" icon="activity">
    Store `event_id`, `type`, `monitor_id`, `occurred_at`, `has_more`, `next_cursor`, and the requested `cursor` value.
  </Card>

  <Card title="Follower export jobs" icon="file-spreadsheet">
    Store `extraction_id`, `status`, `poll`, and `export_after_complete`. Poll before loading CSV, JSON, or XLSX rows.
  </Card>

  <Card title="Tweets and replies" icon="send">
    Store `tweet_id` or `write_action_id`, `reply_to_tweet_id`, `status`, `charged_credits`, and `poll`. Never resend a pending write.
  </Card>
</CardGroup>

## Choose MCP or the direct REST API

| Requirement | Microsoft Agent Framework with MCP | Direct REST API |
| - | - | - |
| Natural-language tweet research | Strong fit | Write route selection yourself |
| Microsoft agent sessions | Native fit | Add session orchestration |
| Strict scheduled follower export | Extra model step | Strong fit |
| Human-approved tweet posting | Function approval flow | Build approval state yourself |
| Predictable latency and cost | Less predictable | More predictable |
| Typed final handoff | Pydantic `response_format` | SDK or Pydantic model |

Use MCP for real-time research across related Twitter API operations. Use REST
for fixed routes, scheduled exports, or latency-sensitive services.

## Migrate older Microsoft Agent Framework code

Use current 1.13 APIs. Several preview-era examples use removed names.
Microsoft Agent Framework succeeds Semantic Kernel and AutoGen.

| Preview-era pattern | Current pattern |
| - | - |
| Preview agent class | `Agent(...)` |
| Legacy client keyword | `client=` |
| Legacy model identifier | Provider client's `model=` |
| Dedicated streaming method | `run(..., stream=True)` |
| Prompt-only JSON | Pydantic `response_format` and `result.value` |
| Static shared auth headers | `header_provider` and `function_invocation_kwargs` |

Current releases also scope runtime MCP headers to matching origins. Keep the
framework updated for those authentication fixes.

## Verified Python package versions

Xquik checked these versions on August 3, 2026.

| Package | Checked compatible version | Supported range used here |
| - | - | - |
| `agent-framework` | 1.13.0 | `==1.13.0` |
| `agent-framework-core` | 1.13.0 | Installed by the meta-package |
| `agent-framework-openai` | 1.12.0 | Installed by the meta-package |
| `mcp` | 1.29.0 | `>=1.24,<2` |
| `pydantic` | 2.13.4 | `>=2,<3` |

Pin a tested release. Review Microsoft Agent Framework release notes before
widening the range.

## Microsoft Agent Framework Twitter API questions

### Does Microsoft Agent Framework support MCP?

Yes. Python agents connect through `MCPStreamableHTTPTool`. Xquik publishes
`docs`, `search`, and `execute` at `https://xquik.com/mcp`.

### Is Microsoft Agent Framework an alternative to MCP?

No. Microsoft Agent Framework runs agents and workflows. MCP standardizes
remote tool access. This guide uses both layers together.

### How do I connect Microsoft Agent Framework to a Twitter API?

Create `MCPStreamableHTTPTool` with the Xquik MCP URL. Resolve the `x-api-key`
header through `header_provider`. Pass the tool to `Agent.tools`.

### How does Microsoft Agent Framework MCP authentication work?

Xquik reads the `x-api-key` header. Resolve it through `header_provider` for
each run. Pass its value through `function_invocation_kwargs`.

### Does the Xquik MCP server require OAuth?

No. Xquik uses an API key. Do not add OAuth for this MCP server.
Other MCP servers can require OAuth.

### Where is the Microsoft Agent Framework documentation?

Microsoft Learn documents agents, MCP tools, sessions, approvals, and
workflows. This guide applies those APIs to Xquik's Twitter operations.

### What does the Microsoft Agent Framework SDK provide?

The SDK provides agents, chat clients, sessions, middleware, tools, and typed
workflows. Xquik supplies remote Twitter operations through MCP.

### How do I search tweets with a Python agent?

Ask the agent to call `GET /api/v1/x/tweets/search`. Supply an exact `q`,
`queryType`, and limit. Keep `tweet_id`, `created`, and `next_cursor`.

### Can Microsoft Agent Framework post tweets and replies?

Yes. Connect an X account first. Apply approval to every `execute` request.
Validate the selected account, text, reply ID, and media.

See [create tweet](/api-reference/x-write/create-tweet) for the exact contract.

### How do I handle Twitter API rate limits in Python?

Treat `429` separately from dependency errors. Save the cursor and completed
tweet IDs. Resume after reset guidance. Never retry in a tight loop.

### Can a Microsoft agent export Twitter followers?

Yes. Use the [followers API](/api-reference/x/followers) for paginated profile
rows. Use an extraction job for larger CSV, JSON, or XLSX exports.

### Can the agent monitor tweets without repeated searches?

Yes. Create an account or keyword monitor. Replay stored events by cursor.
Verify signatures and deduplicate deliveries before connecting a webhook.

### Can the agent triage customer support mentions?

Yes. Search brand mentions or create an account monitor. Keep tweet IDs
before classifying urgency. Keep every reply behind human approval.

### Does tool filtering make the Twitter API read-only?

Only when `allowed_tools` contains `search` without `execute`. The `execute` tool
can run every operation authorized by its API key.

### Why does my MCP agent lose authentication between calls?

Pass runtime values through `function_invocation_kwargs` on every resumed run.
Keep one `AgentSession` during approvals. Never store the key in session state.


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