Connecting the Claude Agent SDK to MCP Servers
The Model Context Protocol (MCP) is a standard way for an agent to call tools that live outside the SDK itself, on a local process or a remote server.
The Claude Agent SDK has first-class MCP client support: you register a server, stdio for a local process or HTTP for a remote one, and its tools become callable by the tool-use loop the same way built-in tools are.
Summary
Built-in tools cover file editing, bash, and the web; MCP is how you extend the loop to anything else, a database, an internal API, a ticketing system, a third-party SaaS product.
A stdio MCP server runs as a subprocess on the same machine as your agent and communicates over standard input/output.
An HTTP MCP server runs remotely and communicates over HTTP, useful when the tool lives on infrastructure you don't control directly or want to share across multiple agents.
This page covers registering both kinds of server and how their tools interact with the rest of the loop once connected.
Recipe
Quick-reference recipe card - copy-paste ready.
from claude_agent_sdk import query, AgentOptions, McpServerConfig
options = AgentOptions(
allowed_tools=["file_edit"],
mcp_servers=[
McpServerConfig(
name="internal-search",
transport="stdio",
command=["python", "-m", "internal_search_mcp"],
),
McpServerConfig(
name="ticketing",
transport="http",
url="https://mcp.internal.example.com/ticketing",
),
],
)
async for message in query(
prompt="Find related past incidents and open a ticket summarizing this bug.",
options=options,
):
print(message)When to reach for this:
- A task needs to call a system that isn't covered by file edit, bash, or web tools.
- You already have (or can stand up) an MCP server for that system.
- You want to reuse the same tool across multiple different agents or projects without rewriting integration code each time.
- You need a remote tool (HTTP) versus a local one (stdio) based on where the capability actually lives.
Working Example
import asyncio
from claude_agent_sdk import query, AgentOptions, McpServerConfig
async def triage_incident(incident_description: str) -> None:
options = AgentOptions(
allowed_tools=["file_edit"],
mcp_servers=[
McpServerConfig(
name="logs",
transport="stdio",
command=["node", "logs-mcp-server.js"],
# stdio servers inherit no special network access by default;
# they only do what the local process is built to do.
),
McpServerConfig(
name="pagerduty",
transport="http",
url="https://mcp.example.com/pagerduty",
headers={"Authorization": "Bearer ${PAGERDUTY_MCP_TOKEN}"},
),
],
# MCP tools are scoped through allowed_tools like anything else;
# naming them explicitly here keeps this agent from calling every
# tool either server happens to expose.
allowed_mcp_tools=["logs.search", "pagerduty.create_incident"],
)
prompt = (
f"An incident was reported: {incident_description}. Search recent logs "
"for related errors, then create a PagerDuty incident summarizing what "
"you found, only if you find a genuine match."
)
async for message in query(prompt=prompt, options=options):
if message.get("type") == "text":
print(message["text"], end="", flush=True)
elif message.get("type") == "tool_call":
print(f"\n[tool call: {message['tool_name']}]")
asyncio.run(triage_incident("Checkout API returning 500s intermittently since 14:02 UTC"))What this demonstrates:
- Registering one local (stdio) server for log search and one remote (HTTP) server for paging, matched to where each capability actually lives.
- Passing an auth header on the HTTP server registration, since a remote MCP server typically needs its own authentication independent of the SDK's own API key.
- Narrowing which specific MCP tools are callable (
allowed_mcp_tools) instead of exposing every tool a server happens to offer. - A prompt that explicitly conditions the destructive action (opening an incident) on a real finding, reducing the chance of a false-positive page.
Deep Dive
How It Works
- Registering an MCP server with
mcp_serverstells the SDK how to reach it (a subprocess command for stdio, a URL and headers for HTTP) and to fetch its tool definitions at startup. - Once fetched, the server's tools are added to the loop's available tool set alongside built-in tools; from the model's perspective during the decide step, an MCP tool and a built-in tool look the same, a name, a description, and an argument schema.
- A stdio server communicates over the subprocess's standard input/output streams; the SDK manages starting, talking to, and eventually terminating that subprocess for the duration of the run.
- An HTTP server communicates over ordinary HTTP requests; it can be shared across multiple concurrent agent runs and lives independently of any single agent's process lifecycle.
- Tool scoping still applies:
allowed_tools/allowed_mcp_toolscontrol which of the server's exposed tools the loop can actually call, the same layered scoping model used for built-in tools.
stdio vs. HTTP at a Glance
| Transport | Runs Where | Good For | Consideration |
|---|---|---|---|
| stdio | Local subprocess, same machine as the agent | Local file access, local dev tools, no network hop needed | Process lifecycle tied to the agent run |
| HTTP | Remote server | Shared infrastructure, systems already exposed as a service | Needs its own auth, network reliability becomes a factor |
Python Notes
# stdio servers should be started with an explicit, minimal command -
# avoid relying on ambient PATH resolution that might differ between
# your dev machine and a deployment environment.
McpServerConfig(
name="internal-search",
transport="stdio",
command=["python3", "/opt/mcp-servers/internal_search/main.py"],
)Parameters & Return Values
| Parameter | Type | Description |
|---|---|---|
name | str | Identifier used to reference this server's tools |
transport | str | "stdio" or "http" |
command | list[str] | Subprocess command, stdio only |
url | str | Server endpoint, HTTP only |
headers | dict | Auth or other headers sent with HTTP requests |
allowed_mcp_tools | list[str] | Fine-grained allowlist of specific tools across registered servers |
Gotchas
- Registering a server and exposing every tool it offers. A server built for internal use might expose more tools than a given agent actually needs. Fix: use
allowed_mcp_toolsto name exactly which tools this agent can call. - Hardcoding secrets in a server's
headersorcommand. Committing an API token directly intoAgentOptionsrisks leaking it through logs or version control. Fix: read secrets from environment variables at call time, not literal strings in code. - Assuming a stdio server's subprocess survives across separate
query()calls. A stdio server's process lifecycle is generally tied to the run that started it. Fix: don't rely on stdio server state persisting between unrelated runs; use an HTTP server if you need a long-lived shared process. - Not handling an unreachable HTTP MCP server. If the remote server is down, tool calls to it will fail, and the loop needs to be able to reason about that failure. Fix: test failure behavior deliberately, and consider whether the prompt should account for a tool being unavailable.
- Trusting an MCP server's tool descriptions blindly. A misleading or malicious tool description could steer the model toward unintended calls. Fix: only register MCP servers you trust, and review their tool descriptions the same way you'd review a built-in tool's scope.
- Mixing up which transport a given integration needs. Choosing HTTP for something that's really a local dev tool (or vice versa) adds unnecessary complexity. Fix: match the transport to where the capability genuinely lives.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Built-in tools only | The task fits entirely within file/bash/web capabilities | The task needs a specific external system MCP can reach |
| Custom tool wired directly into your app | You need one very specific, tightly coupled integration and won't reuse it elsewhere | You want the same tool reusable across multiple agents or projects |
| A subagent that itself uses MCP tools | The MCP-backed work is a self-contained subtask worth isolating | The MCP call is a small, one-off step in a larger flow |
FAQs
What's the practical difference between stdio and HTTP MCP servers?
- stdio runs as a local subprocess, communicating over standard input/output.
- HTTP runs remotely, communicating over ordinary HTTP requests.
- Choose based on where the capability actually lives, not availability of one transport over the other.
Do MCP tools count against allowed_tools the same way built-in tools do?
Yes, MCP tools are scoped through the same allowlist mechanism (often via a dedicated allowed_mcp_tools list), so you control exactly which registered tools the loop can call.
Can I register more than one MCP server in the same run?
Yes, mcp_servers accepts a list and can mix stdio and HTTP servers together in a single agent configuration.
How does the model know an MCP tool exists?
At startup, the SDK fetches each registered server's tool definitions (names, descriptions, argument schemas) and adds them to the loop's available tool set, the same way built-in tool definitions are presented.
What happens if an MCP server is unreachable?
Calls to that server's tools fail, and that failure feeds back into the loop as an observation the model has to reason about, the same as any other failed tool call.
Do I need to write my own MCP server to use this?
Only if a suitable one doesn't already exist for the system you need; MCP is a protocol, so servers built by third parties or your own team both work as long as they speak it.
Can subagents use different MCP servers than their parent?
Yes, mcp_servers and allowed_mcp_tools are set per AgentOptions, so a subagent's own options can register a different (or narrower) set than its parent's.
Is authentication handled by the SDK for HTTP servers?
The SDK passes along whatever headers you configure; the actual authentication scheme (bearer token, API key, etc.) is defined by the server and supplied through headers on your registration.
Should I checkpoint MCP tool calls too?
If an MCP tool performs a destructive or irreversible action, yes; checkpoint configuration (checkpoint_tools) applies to MCP tools the same way it applies to built-in ones.
Can one MCP server expose tools that overlap with built-in tools?
It can, in principle, since MCP tools are just named tools with schemas. Be deliberate about scoping if you register a server whose tools might conflict or overlap in purpose with a built-in tool.
Related
- The Claude Agent SDK Mental Model - how MCP tools fit into the loop
- Enabling File Edit, Bash, and Web Tools in the Agent SDK - built-in tool scoping, the same model applied to MCP
- Claude Agent SDK Configuration Options Reference - MCP-related option shapes
- Adding Human-in-the-Loop Checkpoints to an Agent SDK Workflow - checkpointing destructive MCP calls
- Claude Agent SDK Deployment Patterns Reference - how deployment shape affects stdio vs. HTTP choices
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the Claude Agent SDK (latest release, Python and TypeScript). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.