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

> Connect Xquik's X (Twitter) tools to Microsoft Agent Framework agents via MCP

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

Build a Microsoft Agent Framework agent that can search tweets, hand off IDs and cursors, post tweets, replay stored monitor events, and run extraction jobs - all through Xquik's MCP server.

## Prerequisites

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

## Install

```bash theme={null}
pip install agent-framework mcp
```

The `agent-framework` meta-package installs all core and provider sub-packages. The `mcp` package enables `MCPStreamableHTTPTool` support.

## Full Example

```python theme={null}
import asyncio
from pathlib import Path
from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework.openai import OpenAIChatClient


async def main():
    mcp_tool = MCPStreamableHTTPTool(
        name="xquik",
        url="https://xquik.com/mcp",
        headers={"x-api-key": "xq_YOUR_KEY_HERE"},
        description="X (Twitter) data platform with 120 API endpoints",
    )

    async with mcp_tool:
        agent = ChatAgent(
            chat_client=OpenAIChatClient(model_id="gpt-4o"),
            name="xquik_agent",
            instructions="You help users interact with X (Twitter) via the Xquik API.",
            tools=[mcp_tool],
        )

        handoff_prompt = (
            "Search for tweets about AI agents. Return compact JSON with "
            "query, route_used, tweets[{tweet_id,text,author_username,created_at}], "
            "has_more, next_cursor."
        )
        response = await agent.run(handoff_prompt)
        Path("xquik-agent-handoff.json").write_text(response.text, encoding="utf-8")


asyncio.run(main())
```

The agent auto-discovers all Xquik tools (`explore` + `xquik`) and can call any of the 120 API endpoints. The MCP runtime returns normalized snake\_case fields through `xquik.request()`, so keep prompts 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>

## Streaming Responses

Use `run_stream` for real-time token streaming:

```python theme={null}
async with mcp_tool:
    agent = ChatAgent(
        chat_client=OpenAIChatClient(model_id="gpt-4o"),
        name="xquik_agent",
        instructions="You help users interact with X (Twitter) via the Xquik API.",
        tools=[mcp_tool],
    )

    async for update in agent.run_stream("What are the trending topics on X right now?"):
        if update.text:
            print(update.text, end="")
```

## Multi-Agent Orchestration

Use `GroupChatBuilder` to coordinate specialized agents sharing the same MCP tools:

```python theme={null}
import asyncio
from pathlib import Path
from agent_framework import ChatAgent, MCPStreamableHTTPTool, GroupChatBuilder
from agent_framework.openai import OpenAIChatClient


async def main():
    client = OpenAIChatClient(model_id="gpt-4o")

    mcp_tool = MCPStreamableHTTPTool(
        name="xquik",
        url="https://xquik.com/mcp",
        headers={"x-api-key": "xq_YOUR_KEY_HERE"},
        description="X (Twitter) data platform",
    )

    async with mcp_tool:
        researcher = ChatAgent(
            chat_client=client,
            name="researcher",
            instructions=(
                "Search X for tweets and user profiles. Return compact JSON with "
                "tweet_id, author_username, text, created_at, has_more, next_cursor, "
                "and route_used."
            ),
            tools=[mcp_tool],
        )

        analyst = ChatAgent(
            chat_client=client,
            name="analyst",
            instructions="Analyze tweet data and identify trends, sentiment, and key influencers.",
        )

        workflow = (
            GroupChatBuilder()
            .with_orchestrator(
                ChatAgent(
                    chat_client=client,
                    name="coordinator",
                    instructions="Coordinate research tasks. Delegate data collection to the researcher and analysis to the analyst.",
                )
            )
            .participants([researcher, analyst])
            .build()
        )

        result = await workflow.run(
            "Find the top 10 most engaged tweets about AI agents and analyze the sentiment."
        )
        Path("xquik-agent-workflow-handoff.json").write_text(
            result.text,
            encoding="utf-8",
        )


asyncio.run(main())
```

## Per-Run Headers

Pass API keys dynamically via `tool_resources` for multi-tenant apps where each user has their own Xquik API key:

```python theme={null}
mcp_tool = MCPStreamableHTTPTool(
    name="xquik",
    url="https://xquik.com/mcp",
    description="X (Twitter) data platform",
)

async with mcp_tool:
    agent = ChatAgent(
        chat_client=OpenAIChatClient(model_id="gpt-4o"),
        name="xquik_agent",
        instructions="You help users interact with X (Twitter) via the Xquik API.",
        tools=[mcp_tool],
    )

    response = await agent.run(
        "Search for tweets about AI agents",
        tool_resources={"xquik": {"headers": {"x-api-key": user_api_key}}},
    )
```

Headers passed via `tool_resources` are available only for the current run and are not persisted.

## Azure OpenAI

Swap `OpenAIChatClient` for `AzureOpenAIChatClient` to use Azure-hosted models:

```python theme={null}
from agent_framework.azure import AzureOpenAIChatClient

client = AzureOpenAIChatClient(
    endpoint="https://your-resource.openai.azure.com",
    deployment_name="gpt-4o",
    api_key="your-azure-key",
)

agent = ChatAgent(
    chat_client=client,
    name="xquik_agent",
    instructions="You help users interact with X (Twitter) via the Xquik API.",
    tools=[mcp_tool],
)
```

## Environment Variables

Store your API keys in a `.env` file instead of hardcoding them:

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

```python theme={null}
import os
from dotenv import load_dotenv
from agent_framework import MCPStreamableHTTPTool

load_dotenv()

mcp_tool = MCPStreamableHTTPTool(
    name="xquik",
    url="https://xquik.com/mcp",
    headers={"x-api-key": os.environ["XQUIK_API_KEY"]},
    description="X (Twitter) data platform",
)
```

## Package Versions

| Package                | Version |
| ---------------------- | ------- |
| `agent-framework`      | 1.0.0+  |
| `agent-framework-core` | 1.0.0+  |
| `mcp`                  | 1.9.2+  |
