Menu

AI

OpenAI Agents SDK MCP Tutorial: Hosted, Streamable HTTP and stdio Servers

Connect MCP servers to the OpenAI Agents SDK for Python. Choose hosted, Streamable HTTP or stdio, filter tools, require approvals, and avoid v1/v2 SDK traps.

By: Irina ShvayaNovember 2, 2026

OpenAI Agents SDK MCP Tutorial: Hosted, Streamable HTTP and stdio Servers

This tutorial is part of Build an MCP Server, within The MCP Guide. Want it built for you? See MCP development.

The OpenAI Agents SDK for Python can use MCP servers in four ways. OpenAI's servers can call a public MCP server for you (hosted tools), or your own process can connect over Streamable HTTP, the older SSE transport, or stdio. The right choice depends on where the tool calls should run and which transport the server speaks.

This tutorial picks the most common path, Streamable HTTP, builds a working agent, then adds tool filtering, approvals and multiple servers. Code was checked against the SDK's MCP documentation and openai-agents 0.24.0 on PyPI on 2 November 2026.

Key Takeaways

  • HostedMCPTool lets OpenAI's Responses API call a publicly reachable server; it only works with OpenAI models that support hosted MCP.
  • MCPServerStreamableHttp and MCPServerStdio run the MCP connection in your own process and work with the agent's mcp_servers list.
  • The SDK supports both major versions of the mcp Python package (>=1.19.0,<3) and negotiates the protocol version with each server.
  • Use tool_filter to expose only the tools you need, and require_approval to pause before risky calls.
  • MCP tool listing and tool calls show up in the SDK's tracing automatically.

Choose your integration

The docs give a simple decision table:

What you needUse
OpenAI's Responses API calls a public MCP server on the model's behalfHostedMCPTool
You connect to a Streamable HTTP server, local or remoteMCPServerStreamableHttp
You connect to a legacy SSE serverMCPServerSse
You launch a local server as a subprocessMCPServerStdio

The docs warn that "the MCP project has deprecated the Server-Sent Events transport." Keep SSE only for old servers. Our SSE vs Streamable HTTP page explains why.

Prerequisites

  • Python 3.10 or later.
  • pip install openai-agents
  • OPENAI_API_KEY set in your environment.
  • An MCP server. For a local test server, use the FastMCP notes server from our langchain-mcp-adapters tutorial, which serves http://localhost:8000/mcp, or see our FastMCP tutorial.

Minimal working example (Streamable HTTP)

This is the docs' example, trimmed. It connects to a local server and asks a question that needs a tool:

import asyncio
import os

from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp


async def main() -> None:
    async with MCPServerStreamableHttp(
        name="Notes server",
        params={
            "url": "http://localhost:8000/mcp",
            "headers": {"Authorization": f"Bearer {os.environ.get('MCP_SERVER_TOKEN', '')}"},
            "timeout": 10,
        },
        cache_tools_list=True,
        max_retry_attempts=3,
    ) as server:
        agent = Agent(
            name="Assistant",
            instructions="Use the MCP tools to answer questions.",
            mcp_servers=[server],
        )
        result = await Runner.run(agent, "What does the welcome note say?")
        print(result.final_output)


asyncio.run(main())

Notes on the options:

  • cache_tools_list=True skips a tools/list round trip on every run. Turn it off while you are still changing the server.
  • max_retry_attempts adds automatic retries for list_tools() and call_tool().
  • client_session_timeout_seconds defaults to 5 seconds. Raise it for slow servers.
  • Put tokens in headers, not URLs. The docs say to keep "access tokens in authorization fields or headers rather than URLs."

The stdio variant

For a server that runs as a local command, swap the class. This is the docs' filesystem example:

from pathlib import Path

from agents import Agent, Runner
from agents.mcp import MCPServerStdio

samples_dir = Path(__file__).parent / "sample_files"

async with MCPServerStdio(
    name="Filesystem Server via npx",
    params={
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", str(samples_dir)],
    },
    client_session_timeout_seconds=30,
) as server:
    agent = Agent(
        name="Assistant",
        instructions="Use the files in the sample directory to answer questions.",
        mcp_servers=[server],
    )
    result = await Runner.run(agent, "List the files available to you.")
    print(result.final_output)

The docs flag a cold-start trap: npx -y and uvx "may download packages before the MCP server starts." With MCP Python SDK v2, a longer session timeout does not cover the first server/discover request, so preinstall the package instead. Our filesystem server profile covers that server's tools and risks.

The hosted variant

With HostedMCPTool, your Python code never connects to the server. The tool config is forwarded to the Responses API:

from agents import Agent, HostedMCPTool, Runner

agent = Agent(
    name="Assistant",
    instructions="Use the DeepWiki hosted MCP server to inspect openai/openai-agents-python.",
    tools=[
        HostedMCPTool(
            tool_config={
                "type": "mcp",
                "server_label": "deepwiki",
                "server_url": "https://mcp.deepwiki.com/mcp",
                "require_approval": "never",
            }
        )
    ],
)

The hosted server goes in tools, not mcp_servers. It must be reachable from the public internet, and the docs say hosted tools "currently work with OpenAI models that support the Responses API's hosted MCP integration." Use "require_approval": "always" plus an on_approval_request callback for anything that writes data.

Add a tool

Tools live on the server. Add a function to your server, restart it, and the agent sees the new tool on its next list_tools() call. If you set cache_tools_list=True, restart your agent process too, or the old list stays cached.

Write each tool description for the model. The agent picks tools by reading names and descriptions, so "Read one note by its exact title" beats "Gets data."

Limit what the agent can do

Filter tools

create_static_tool_filter gives you allow and block lists:

from agents.mcp import MCPServerStdio, create_static_tool_filter

filesystem_server = MCPServerStdio(
    params={
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/files"],
    },
    tool_filter=create_static_tool_filter(allowed_tool_names=["read_file", "list_directory"]),
)

If you supply both lists, the SDK applies the allow list first, then removes blocked tools.

Require approval

Local MCP servers accept require_approval as "always", "never", a per-tool map, a grouped object, or a callable:

async with MCPServerStreamableHttp(
    name="Filesystem MCP",
    params={"url": "http://localhost:8000/mcp"},
    require_approval={"always": {"tool_names": ["delete_file"]}},
) as server:
    ...

The docs make two points worth repeating. A callable policy "does not receive the specific call's arguments," so it cannot approve "delete only drafts." And server-provided annotations are "advisory hints." Our tool permission planner helps you decide which tools need approval.

Avoid name collisions

Two servers can both have a search tool. Set mcp_config={"include_server_in_tool_names": True} on the agent and each local MCP tool gets a server prefix. The SDK still calls the original name on the original server.

Several servers at once

MCPServerManager connects a list of servers up front and gives the agent the ones that connected:

from agents import Agent, Runner
from agents.mcp import MCPServerManager, MCPServerStreamableHttp

servers = [
    MCPServerStreamableHttp(name="calendar", params={"url": "http://localhost:8000/mcp"}),
    MCPServerStreamableHttp(name="docs", params={"url": "http://localhost:8001/mcp"}),
]

async with MCPServerManager(servers) as manager:
    agent = Agent(
        name="Assistant",
        instructions="Use MCP tools when they help.",
        mcp_servers=manager.active_servers,
    )
    result = await Runner.run(agent, "Which MCP tools are available?")
    print(result.final_output)

Test with the MCP Inspector

Check the server alone before you debug the agent:

npx @modelcontextprotocol/inspector --cli http://localhost:8000/mcp --transport http --method tools/list

Then use the SDK's tracing, which "automatically captures MCP activity," to see which tools the agent listed and called. Our Inspector lesson shows how to script these checks.

MCP SDK v1 vs v2 pitfalls

The Agents SDK works with mcp>=1.19.0,<3, which spans both major versions of the MCP Python SDK. Most apps can let the resolver choose. Three things change with v2, per the docs:

  • Custom params["auth"] objects must be httpx2.Auth, not httpx.Auth. A plain Authorization header works with both.
  • A custom httpx_client_factory must return httpx2.AsyncClient on v2.
  • ignore_initialized_notification_failure=True is rejected on v2.

With v2 installed, the client first sends server/discover at the newest protocol version and falls back to the legacy initialize handshake for older servers. Installing v2 "does not force every connection to use the newest MCP protocol version."

Next step

New to the protocol? The free MCP course explains servers, tools and transports in order. If you want an MCP server built for your OpenAI agents, with scoped auth and approvals, see our MCP development service.

More in the MCP Guide

Free tools: server.json Validator · MCP Error Code Lookup

Latest research: State of MCP: October 2026

Frequently asked questions

Should I use HostedMCPTool or MCPServerStreamableHttp?

Use HostedMCPTool when the server is public and you want OpenAI to handle the round trip. Use MCPServerStreamableHttp when the server is private, when you need your own approval logic in Python, or when you use non-OpenAI models.

Does the Agents SDK support the 2026-07-28 MCP spec?

The docs say that with MCP Python SDK v2 installed, the client probes with server/discover at the newest version the SDK supports and falls back to initialize for older servers. The negotiated version depends on the server.

Why does my stdio server time out on first run?

npx -y and uvx may download the package before the server starts. Install it ahead of time, and raise client_session_timeout_seconds if the server is slow to answer.

Can I see which MCP tools the agent called?

Yes. The MCP docs say tracing captures calls to list tools and MCP-related information on tool calls. The SDK's tracing guide explains where traces are sent and how to change that.

You Might Also like to Read

Get a FREE Website Audit

Enter your website and get a 20–30 page SEO, GEO, AEO & CRO audit within 24 hours. Completely free.

Don't have a site yet? Get in touch →

Ready to speak with a marketing expert?
Give us a call! (916) 866-7893

Websites Launched
1407
Websites Launched
Marketing Experts
29
Marketing Experts
Hours of Dedicated Work
204533
Hours of Dedicated Work
Irina S.Benjamin G.Jeanette M.David G.