If MCPServer is the control path and FastMCPToolset is the convenience path, MCPServerTool is the offload path.
That one distinction clears up most of the confusion.
People see the name and assume MCPServerTool is just one more way for their app to speak MCP. It is not. When you use it, your app is no longer the MCP client. The model provider is. That changes almost everything: where the server must live, which API you need, how auth is passed, what features you give up, and why performance can improve.
As of April 1, 2026, the official Pydantic AI docs still describe MCPServerTool as the built-in tool for remote MCP servers, with communication handled by the provider. The same docs also warn that this path does not support many of the more advanced agent-side MCP features, but can give you better context use, caching, and lower overhead because the request does not bounce back through your app.
That is a very specific tradeoff. Sometimes it is exactly what you want. Sometimes it is the wrong tool entirely.
This guide focuses on the practical side:
- using
builtin_tools=[MCPServerTool(...)] - choosing a supported provider and model path
- configuring auth, allowed tools, and headers
- using OpenAI connector URLs
- knowing when to stop and use
MCPServerinstead
If you want the big-picture comparison first, read our guide to MCPServer, FastMCPToolset, and MCPServerTool. If you want the richer agent-side client path, the companion guide on connecting Pydantic AI to MCP servers with MCPServer covers that route.
What you’ll learn:
- what
MCPServerToolactually does inside Pydantic AI - which providers support it and which do not
- how to wire a remote MCP server with OpenAI Responses or Anthropic
- how to scope access with
allowed_tools, auth tokens, and headers - when provider-side MCP is cleaner than agent-side MCP
Time required: 20-30 minutes
Difficulty level: Intermediate
Step 1: Understand What MCPServerTool Really Is
MCPServerTool is a built-in tool, not a toolset.
That means you register it like this:
from pydantic_ai import Agent, MCPServerTool
agent = Agent(
"openai-responses:gpt-5.2",
builtin_tools=[
MCPServerTool(
id="deepwiki",
url="https://mcp.deepwiki.com/mcp",
)
],
)
Not like this:
agent = Agent(
"...",
toolsets=[...], # wrong path for MCPServerTool
)
That detail matters because built-in tools run on the provider side. The Pydantic AI docs phrase it pretty plainly: MCPServerTool is for remote MCP servers with communication handled by the model provider.
So the mental model is:
- your app sends a model request
- the provider decides to use the built-in MCP tool
- the provider reaches the remote MCP server directly
- the MCP exchange happens there, not inside your Python process
Once you see it that way, a lot of downstream rules stop feeling arbitrary.
One side note from the same docs: if you want a more model-agnostic route with automatic local fallback, they point you to the higher-level MCP capability instead of MCPServerTool directly.
Step 2: Start with the Two Hard Constraints
Before you write a line of code, check these two things.
Constraint 1: The MCP server must be reachable by the provider
If your MCP server only lives on localhost, inside a private Docker network, or behind infrastructure the provider cannot see, MCPServerTool is not the right choice.
The docs explicitly say this path requires the MCP server to live at a public URL the provider can reach.
So this is a bad fit:
- local stdio servers
http://localhost:3001/mcp- internal-only VPN endpoints
- private staging URLs that only your app can access
Those are MCPServer or FastMCPToolset problems, not MCPServerTool problems.
Constraint 2: You need a provider that supports the built-in tool
As of April 1, 2026, the Pydantic AI built-in tools docs list support for MCPServerTool on:
- OpenAI Responses
- Anthropic
- xAI
The same docs list these as unsupported:
- Groq
- OpenAI Chat Completions
- Bedrock
- Mistral
- Cohere
- HuggingFace
That OpenAI split is easy to miss. If you are on OpenAI, this is a Responses API feature, not a Chat Completions feature.
Step 3: Wire the Simplest Working Example First
I would start with the smallest possible remote server example before touching auth or connectors.
The official docs use DeepWiki as the minimal public example:
from pydantic_ai import Agent, MCPServerTool
agent = Agent(
"openai-responses:gpt-5.2",
builtin_tools=[
MCPServerTool(
id="deepwiki",
url="https://mcp.deepwiki.com/mcp",
)
],
)
result = agent.run_sync("Tell me about the pydantic/pydantic-ai repo.")
print(result.output)
If you prefer Anthropic, the same docs show the same pattern with a Claude model:
from pydantic_ai import Agent, MCPServerTool
agent = Agent(
"anthropic:claude-sonnet-4-6",
builtin_tools=[
MCPServerTool(
id="deepwiki",
url="https://mcp.deepwiki.com/mcp",
)
],
)
result = agent.run_sync("Tell me about the pydantic/pydantic-ai repo.")
print(result.output)
That is the key thing to notice: the transport details are not in your app. You are just declaring a remote MCP server as a provider-native tool.
Step 4: Use id Like It Actually Matters
The id field is not decorative.
According to the API reference, MCPServerTool uses id as the unique identifier for the server, and derives:
unique_idasmcp_server:<id>labelasMCP: <id>
That is a small detail, but it is worth treating seriously. Give each server a stable, descriptive id, especially if you plan to register more than one built-in MCP server.
Good examples:
deepwikigithubgoogle-calendarbilling-docs
Bad examples:
server1testfoo
You may not care on day one. You will care once traces, logs, or debugging screenshots start piling up.
Step 5: Lock Down Auth and Tool Scope Early
The next step is where MCPServerTool gets more interesting.
The docs show that you can configure:
authorization_tokenallowed_toolsdescriptionheaders
Here is the official GitHub-style example pattern:
import os
from pydantic_ai import Agent, MCPServerTool
agent = Agent(
"openai-responses:gpt-5.2",
builtin_tools=[
MCPServerTool(
id="github",
url="https://api.githubcopilot.com/mcp/",
authorization_token=os.getenv("GITHUB_ACCESS_TOKEN"),
allowed_tools=["search_repositories", "list_commits"],
description="GitHub MCP server",
headers={"X-Custom-Header": "custom-value"},
)
],
)
This is a much better starting point than exposing the entire server surface by default.
I would treat allowed_tools as the first real safety lever:
- keep the list tight at the start
- only expose the tools your prompt actually needs
- widen later if logs show the agent is boxed in
That is not just good security hygiene. It also reduces tool-selection noise.
Parameter support is not identical across providers
The built-in tools docs include a parameter support table for MCPServerTool:
| Parameter | OpenAI Responses | Anthropic | xAI |
|---|---|---|---|
authorization_token | Yes | Yes | Yes |
allowed_tools | Yes | Yes | Yes |
description | Yes | No | Yes |
headers | Yes | No | Yes |
That means two easy mistakes to avoid:
- do not rely on
headersif you need Anthropic - do not assume
descriptionis portable across every supported provider
If you need the broadest compatibility, stick to id, url, authorization_token, and allowed_tools.
Step 6: Use OpenAI Connectors When the Server URL Is Really a Connector
This is where the OpenAI route becomes more specialized.
The Pydantic AI docs say OpenAI Responses can use connectors by passing a special URL in this format:
x-openai-connector:<connector_id>
Example:
import os
from pydantic_ai import Agent, MCPServerTool
agent = Agent(
"openai-responses:gpt-5.2",
builtin_tools=[
MCPServerTool(
id="google-calendar",
url="x-openai-connector:connector_googlecalendar",
authorization_token=os.getenv("GOOGLE_API_KEY"),
)
],
)
result = agent.run_sync("What do I have on my calendar today?")
print(result.output)
This is OpenAI-specific. It is not a generic MCP URL trick.
If you are already using OpenAI-managed connectors, this can be the cleanest reason to pick MCPServerTool over agent-side MCP. Your app stays thin, and the provider handles the remote tool bridge directly.
Step 7: Add Dynamic Built-in Configuration When One Server Is Not Always Needed
The generic built-in tools docs say builtin_tools can accept a function that receives RunContext and returns an AbstractBuiltinTool or None.
That section is shown with WebSearchTool, but the API is generic. So the same pattern can be applied to MCPServerTool. This is an inference from the built-in tool API, not a separate MCPServerTool-specific example in the docs.
Here is a practical version:
from pydantic_ai import Agent, MCPServerTool, RunContext
async def prepare_github_mcp(ctx: RunContext[dict]) -> MCPServerTool | None:
if not ctx.deps.get("enable_repo_search"):
return None
token = ctx.deps.get("github_token")
if not token:
return None
return MCPServerTool(
id="github",
url="https://api.githubcopilot.com/mcp/",
authorization_token=token,
allowed_tools=["search_repositories", "list_commits"],
)
agent = Agent(
"openai-responses:gpt-5.2",
builtin_tools=[prepare_github_mcp],
deps_type=dict,
)
I like this pattern when:
- some users have connector access and others do not
- you only want the MCP server available for a specific workflow
- you want to skip the tool entirely when auth is missing
That keeps the agent prompt cleaner than registering every possible remote server all the time.
Step 8: Know What You Are Giving Up
This is the part that trips people up.
The docs say MCPServerTool does not support many of the advanced features of Pydantic AI’s agent-side MCP support. They do not frame it as a universal replacement for MCPServer.
So if I know I need any of the following, I stop and reach for the standard client path first:
- localhost or stdio servers
- private infrastructure only my app can reach
- explicit agent-side transport control
- richer MCP client behavior such as sampling or elicitation
For things like resource access, custom TLS control, or agent-side tool-call hooks, the standard MCPServer docs are the richer documented path. That is an inference from the documented feature split across the two systems, and in practice it is the safer way to choose.
In other words, MCPServerTool is excellent when the provider should own the remote exchange. It is not the tool I would pick if MCP behavior itself is part of my application’s contract.
Step 9: Choose MCPServerTool for the Right Reasons
Here is when I think MCPServerTool is genuinely the better answer:
- your MCP server already lives at a public remote URL
- your provider supports built-in MCP tools
- you want less app-side plumbing
- you want the provider to handle context packing and caching directly
- you are using OpenAI connectors and want the shortest path to them
And here is when I would not force it:
- your server is local
- your team needs full agent-side control
- you expect interactive MCP features to matter
- you are still on an unsupported provider
This is why the three Pydantic AI MCP paths exist in the first place. They are not cosmetic variations. They solve different deployment shapes.
Step 10: The Fast Decision Rule
If you want a quick rule you can use in code review, use this:
- choose
MCPServerToolwhen the provider should talk to a public remote MCP server directly - choose
MCPServerwhen your app should remain the MCP client - choose
FastMCPToolsetwhen you still want agent-side execution but prefer FastMCP ergonomics
That is the real map.
Common Mistakes
Using toolsets= instead of builtin_tools=
If you register MCPServerTool like a toolset, you are mixing two different Pydantic AI integration layers.
Pointing to localhost
http://localhost:3001/mcp may be reachable from your machine. That says nothing about whether OpenAI or Anthropic can reach it.
Using OpenAI Chat Completions
The built-in tools docs are explicit: MCPServerTool support is on OpenAI Responses, not Chat Completions.
Assuming all config parameters work everywhere
They do not. description and headers are not supported on Anthropic according to the current docs.
Reaching for it just because it sounds newer
Provider-side execution is not automatically better. Sometimes it is simpler. Sometimes it is just less controllable.
Final Takeaway
MCPServerTool makes sense once you stop thinking of it as “another MCP client wrapper” and start thinking of it as “provider-native remote MCP access.”
That is why it shines in a narrow set of cases:
- public remote servers
- supported providers
- lighter app integration
- connector-style workflows
Outside that lane, MCPServer is usually the more capable tool. And that is fine. Pydantic AI is giving you three MCP paths because real deployments are messy, not because the docs wanted extra nouns.
Pick the one that matches where the connection should live.
Discussion
Leave a comment
No comments yet
Be the first to start the conversation.