Skip to content

6. MCP in agent frameworks

Intermediate · 11 min read

You rarely write the host loop from page 4 by hand in production. Every major agent framework can load MCP tools and treat them like its own. The pattern is always the same:

  1. Describe how to reach each server (a command for stdio, a URL for HTTP).
  2. The framework connects, lists the tools and converts them into its own tool type.
  3. Your agent uses them like any other tool.

Adapters move fast

Checked with langchain-mcp-adapters 0.3, openai-agents 0.24 and mcp 2.3 in October 2026. These adapters still depend on mcp 1.x, so install them in their own environment, separate from servers built on mcp 2.x. That's fine because MCP is a wire protocol: a 1.x client talks to a 2.x server through the older initialize handshake, which 2.x servers still support. All the outputs below are from 1.x clients calling the 2.x server from page 3.

6.1 LangChain and LangGraph

langchain-mcp-adapters connects to one or more servers and returns ordinary LangChain tools:

# no-run — pip install langchain-mcp-adapters langchain langgraph
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "orders": {"command": "python", "args": ["/abs/path/orders_server.py"], "transport": "stdio"},
    "docs": {"url": "https://mcp.example.com/mcp", "transport": "http",
             "headers": {"Authorization": "Bearer ..."}},
})

async def main():
    tools = await client.get_tools()            # LangChain BaseTool objects, one per MCP tool
    print([t.name for t in tools])
    status = next(t for t in tools if t.name == "get_order_status")
    print(await status.ainvoke({"order_id": "A100"}))

asyncio.run(main())
Output (orders server only)
['get_order_status', 'refund_order']
[{'type': 'text', 'text': '{\n  "order_id": "A100",\n  "status": "shipped",\n  "eta": "Friday"\n}', 'id': 'lc_0dce8450-eaa3-4361-91a7-b3a9934cb5dd'}]

Hand them to an agent exactly like the tools in LangChain:

# no-run — needs ANTHROPIC_API_KEY
from langchain.agents import create_agent

agent = create_agent(
    model="anthropic:claude-opus-5-5",
    tools=tools,
    system_prompt="You are a support assistant. Use tools; never guess order details.",
)
result = await agent.ainvoke({"messages": [{"role": "user", "content": "Where is order A100?"}]})
print(result["messages"][-1].content)

Useful options on MultiServerMCPClient:

Option What it does
tool_name_prefix=True Names tools orders_get_order_status, avoiding clashes between servers
handle_tool_errors=True (default) An isError result goes back to the model as an error ToolMessage so it can retry, instead of crashing the run
tool_interceptors=[...] Hook every call: add headers, log, block, or rewrite arguments
client.session("orders") + load_mcp_tools(session) Keep one session open for many calls. By default each tool call opens a fresh connection, which is slow for stdio servers

Since the tools are plain LangChain tools, everything from Agents in LangGraph applies: put them in a ToolNode, add guard nodes, add interrupt() before destructive ones.

6.2 OpenAI Agents SDK

The OpenAI Agents SDK takes MCP servers directly on the agent:

# no-run — pip install openai-agents ; the run needs OPENAI_API_KEY
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStdio, MCPServerStreamableHttp, create_static_tool_filter

async def main():
    async with MCPServerStdio(
        name="orders",
        params={"command": "python", "args": ["/abs/path/orders_server.py"]},
        cache_tools_list=True,                                    # don't re-list tools every turn
        tool_filter=create_static_tool_filter(allowed_tool_names=["get_order_status"]),
    ) as orders:
        print([t.name for t in await orders.list_tools()])
        agent = Agent(name="Support", instructions="Answer order questions with tools.", mcp_servers=[orders])
        result = await Runner.run(agent, "Where is order A100?")
        print(result.final_output)

asyncio.run(main())
Output (list_tools without the filter)
['get_order_status', 'refund_order']

tool_filter is least privilege at the framework level: this agent can look orders up but can't see refund_order at all. For remote servers use MCPServerStreamableHttp(params={"url": ..., "headers": {...}}), and the require_approval option lets you mark tools that need a human "yes" before they run.

6.3 The Claude API's MCP connector

For remote servers you don't need a client at all: the Claude API can connect to an MCP server itself during a request. You pass the server and a toolset that points at it:

# no-run — needs ANTHROPIC_API_KEY; pip install anthropic
import anthropic

client = anthropic.Anthropic()
response = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=16000,
    betas=["mcp-client-2025-11-20"],
    mcp_servers=[{
        "type": "url",
        "url": "https://mcp.example.com/mcp",
        "name": "orders",
        "authorization_token": "<oauth access token for this server>",
    }],
    tools=[{"type": "mcp_toolset", "mcp_server_name": "orders"}],     # both halves are required
    messages=[{"role": "user", "content": "Where is order A100?"}],
)
for block in response.content:
    print(block.type)          # mcp_tool_use and mcp_tool_result blocks appear inline, then the text answer
Use the connector when Use your own client (page 4) when
The server is reachable on the public internet over HTTPS The server is local (stdio) or on a private network
You only need its tools You need resources, prompts or elicitation
You're happy for Anthropic's servers to call it You want to approve, log or modify each call yourself

The Anthropic Python SDK also has helpers that convert MCP tools from your own client session for its tool runner (pip install "anthropic[mcp]"), which covers the right-hand column.

6.4 Claude Code, Cursor and other coding agents

Coding agents are MCP hosts too, and MCP is how teams give them company context: the issue tracker, the internal docs, staging databases, feature flags.

claude mcp add orders -- python /abs/path/orders_server.py              # local server
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp       # remote server (OAuth login on first use)
claude mcp add --scope project orders -- python orders_server.py        # saved in .mcp.json, shared via git

A project-scoped .mcp.json checked into the repo means every developer's agent gets the same tools. Treat it like code: review changes to it, because adding a server gives it a place in every developer's agent.

6.5 Choosing how to connect

Situation Approach
Already on LangChain / LangGraph langchain-mcp-adapters
Already on the OpenAI Agents SDK MCPServerStdio / MCPServerStreamableHttp on the Agent
Claude API, public remote server, tools only The MCP connector (mcp_servers + mcp_toolset)
Need full control (approvals, logging, resources, elicitation) Your own mcp.Client loop (page 4)
Developers' coding agents Claude Code / Cursor config, project-scoped and reviewed

Interview questions

How do you use MCP tools in a LangGraph agent?

Use langchain-mcp-adapters: create a MultiServerMCPClient with each server's command or URL, call get_tools() to get LangChain tools, and pass them to create_agent or a ToolNode. Keep a session open for repeated calls, prefix tool names if servers overlap, and keep handle_tool_errors on so tool failures go back to the model.

Can an MCP client on an old SDK talk to a server on a new SDK?

Yes, usually. MCP is a wire protocol with version negotiation: a client on the handshake era sends initialize, and a new server that still supports those versions answers it. A new client probes server/discover and falls back to initialize for old servers. Problems only appear when one side has dropped every version the other supports.

When would you use the Claude API's MCP connector instead of your own MCP client?

When the server is a public HTTPS endpoint and you only need its tools: Claude calls it directly, so there's no client code. Use your own client when the server is local or private, when you need resources, prompts or elicitation, or when you want to approve, log or change each call before it runs.

How do you limit what an agent can do with an MCP server that has 40 tools?

Filter tools on the client (an allow-list such as the OpenAI Agents SDK's tool_filter, or only passing some LangChain tools to the agent), require approval for write tools, and use credentials or OAuth scopes that only permit what the agent needs. Filtering also improves tool-selection accuracy and saves prompt tokens.

Practice

  • Load the page 3 server with langchain-mcp-adapters with tool_name_prefix=True and print the names.
  • Add require_approval={"refund_order": "always"} to the OpenAI Agents example and read what happens when the agent tries a refund.
  • Decide which approach from 6.5 fits a project you're working on, and why.

Next: 7. Security and production Back to: MCP overview · Notes overview