Last updated: August 31, 2026

Give a Pydantic AI agent a Twitter tool

Attach the OpenTweet MCP server with MCPServerStreamableHTTP and your agent gets 36 tools for posting, scheduling, threads, and articles on X. Or write one typed tool over the REST endpoint when the agent should only ever publish. No X developer account.

Pydantic AI has native MCP support, so this is four lines rather than an integration project. One caveat worth knowing before you design around tool metadata is in the #6613 section below.

7-day free trial. No X developer account needed.

What your agent can do on X

The whole toolset over MCP, or exactly one action you define.

Post now

opentweet_create_tweet publishes the moment the model calls it.

Schedule

opentweet_schedule_tweet and opentweet_batch_schedule queue posts and let the process exit.

Threads and articles

opentweet_create_thread and opentweet_create_article for long-form.

36 tools

Schemas come from the server, so you write no tool definitions.

Analytics

The agent reads results and adapts the next run.

No X API

No developer account, no OAuth flow to implement.

Option 1: MCPServerStreamableHTTP

The server ships its own schemas, so you write no tool definitions and get scheduling, threads, articles, evergreen, and analytics for the same four lines.

x_agent.py
import asyncio
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPServerStreamableHTTP

opentweet = MCPServerStreamableHTTP(
    "https://mcp.opentweet.io/mcp",
    headers={"Authorization": "Bearer ot_your_key"},
)

agent = Agent(
    "openai:gpt-4o",
    toolsets=[opentweet],
    instructions="You publish and schedule on X. Prefer scheduling unless told to post now.",
)

async def main():
    async with agent:
        result = await agent.run(
            "Write a 3-post thread about the 2.0 release and schedule it for 9am UTC tomorrow."
        )
        print(result.output)

asyncio.run(main())

Version gotcha

Current releases take toolsets=[server] and async with agent:. Older ones used mcp_servers=[server] and async with agent.run_mcp_servers():. A TypeError on the Agent constructor almost always means you are reading a guide written for the other one.

Every tool name and argument is in the MCP docs.

What issue #6613 changes about your design

The open issue pydantic/pydantic-ai#6613 tracks gaps in the MCP Apps client surface. Three things are dropped rather than surfaced: _meta on tool results, mimeType on resources, and capabilities.extensions.

The practical rule that follows: do not build anything that depends on metadata travelling next to a tool result. If a server hides the important part of its answer in _meta, your agent will not see it, and you will spend an afternoon deciding whether the bug is yours.

The OpenTweet tools return the status, the post id, and the URL in the result text, which is the format the model reads anyway. Nothing you need to act on rides in _meta, so this gap does not affect posting or scheduling.

Option 2: one typed tool over REST

For an unattended agent, narrow the surface. A function with a docstring and typed arguments is the whole tool definition, and the key never leaves the process environment.

x_tool.py
import os
import httpx
from pydantic_ai import Agent

agent = Agent("openai:gpt-4o", instructions="You write and publish short posts on X.")

@agent.tool_plain
async def post_to_x(text: str, scheduled_date: str | None = None) -> str:
    """Publish a post to X, or schedule it. scheduled_date is ISO 8601, for example 2026-09-02T09:00:00Z."""
    body = {"text": text}
    if scheduled_date:
        body["scheduled_date"] = scheduled_date
    else:
        body["publish_now"] = True

    async with httpx.AsyncClient(timeout=30) as client:
        res = await client.post(
            "https://opentweet.io/api/v1/posts",
            headers={"Authorization": f"Bearer {os.environ['OPENTWEET_API_KEY']}"},
            json=body,
        )
    res.raise_for_status()
    return res.text

And the same call outside Python, for a cron job or a deploy hook:

post.sh
curl -X POST https://opentweet.io/api/v1/posts \
  -H "Authorization: Bearer ot_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text":"The agent wrote this in Python and the scheduler sent it.","scheduled_date":"2026-09-02T09:00:00Z"}'

The route is /api/v1/posts, not /api/v1/tweets. Fields and error codes in the API docs, and post to Twitter from Python covers the non-agent version.

Good fits

Research agents that publish findings

The agent finishes a run, writes the summary, and schedules it rather than dumping it at 3am.

Data and ML teams already on Pydantic

The tool contract is a Pydantic model like everything else in the codebase.

Typed, testable posting logic

A narrow tool is easy to unit test against a mock, unlike a prompt that hopefully picks the right tool.

Scheduled content pipelines

Queue a week with batch scheduling in one run and let the process exit.

Other frameworks: Mastra, LangGraph, CrewAI, Google ADK.

Frequently asked questions

How does a Pydantic AI agent post to X?

Create an MCPServerStreamableHTTP pointed at https://mcp.opentweet.io/mcp with an Authorization header, pass it to your Agent in toolsets, and open the agent as an async context manager. The model then has all 36 OpenTweet tools for posting, scheduling, threads, articles, and analytics.

Is it toolsets or mcp_servers?

Current Pydantic AI takes toolsets=[server] and you enter the agent with async with agent:. Older releases used mcp_servers=[server] with async with agent.run_mcp_servers():. If a tutorial you are following uses the older names and your code raises a TypeError on the Agent constructor, that version difference is why.

What does issue #6613 mean for me?

The open issue pydantic/pydantic-ai#6613 tracks MCP Apps client gaps: tool-result _meta, resource mimeType, and capabilities.extensions are dropped rather than surfaced. In practice, do not design an integration that depends on metadata riding alongside a tool result. The OpenTweet tools return the post id, status, and URL in the result text itself, so nothing you need is carried in _meta.

Do I need an X developer account?

No. OpenTweet holds the X connection, OAuth, and token refresh. Your agent carries one ot_ bearer key. You never register an X developer app and you never pay the X pay-per-use rates.

Can the agent schedule instead of posting immediately?

Yes. opentweet_schedule_tweet takes an ISO 8601 time and opentweet_batch_schedule queues many posts in one call. Over REST, send scheduled_date instead of publish_now. The queue lives on OpenTweet servers, so the Python process can exit.

Should I give the model all 36 tools?

For a supervised agent, yes, it is less code and the model picks well. For an unattended one, write a narrow tool instead. A typed function with a Pydantic model for its arguments is easier to reason about than a system prompt telling the model which of 36 tools it must not touch.

What does it cost?

Pro is $11.99/month with the API included and a 7-day free trial. The X API charges $0.015 per post and $0.20 per post containing a link, so an agent that posts links crosses the break-even around 60 posts a month.

Ship a Python agent that posts to X

Four lines for the full toolset, or one function for a narrow one.

$11.99/month, API included. Cancel anytime.