How to Integrate MCP Tools and Hosted MCP Tools into Agents in openai-agents-python
The openai-agents-python SDK enables MCP tool integration by passing MCPServer instances to an Agent's mcp_servers parameter for automatic discovery, or by wrapping specific tools in HostedMCPTool objects for direct invocation.
The OpenAI Agents SDK (openai-agents-python) ships with native support for the Model Context Protocol (MCP), allowing agents to discover and invoke external tools hosted on MCP servers. This integration enables your agents to leverage specialized capabilities from external processes or remote services without hardcoding tool implementations. Whether you need automatic tool discovery from a running server or prefer to explicitly wrap specific MCP tools for direct agent use, the SDK provides dedicated utilities in the agents.mcp package.
MCP Architecture and Core Components
The MCP implementation in openai-agents-python centers on several key classes that handle server lifecycle, tool conversion, and execution flow.
MCPServer– Abstract base class insrc/agents/mcp/server.pydefining the interface for MCP backends, with concrete implementations likeMCPServerStdiofor local processes.MCPServerManager– Context manager insrc/agents/mcp/manager.pythat starts and stops one or more MCP servers during an agent run.HostedMCPTool– Wrapper class insrc/agents/tool.pythat converts an MCP tool definition into a standard function tool the LLM can invoke directly.MCPUtil– Utility class insrc/agents/mcp/util.pyprovidingto_function_tool()andto_mcp_tool()for converting between MCP definitions and SDK objects.MCPListToolsItem– Run item type insrc/agents/items.pyrepresenting the initial tool discovery request sent to MCP servers.
Method 1: Automatic Tool Discovery via MCPServer
The simplest way to integrate MCP tools is to pass a list of MCPServer instances to the Agent constructor via the mcp_servers parameter. The SDK automatically discovers available tools before the first model turn.
When you configure an agent with mcp_servers, the run-loop performs the following actions:
- Server initialization – The
MCPServerManagerinitializes all configured servers before the first agent turn. - Tool discovery – The agent emits a
MCPListToolsItemto enumerate available tools from each server. - Conversion – The
MCPUtil.to_function_tool()helper transforms eachMcpTooldefinition (defined insrc/agents/tool.py) into aFunctionToolthat gets injected into the model request payload. - Invocation – When the LLM calls an MCP tool, the SDK creates an
MCPApprovalRequestItem, routes it throughHostedMCPTool, and forwards the request to the appropriateMCPServer.
from agents import Agent
from agents.mcp.server import MCPServerStdio
# Configure a local MCP server via stdio
server = MCPServerStdio(
command="python -m my_mcp_server",
env={}
)
# Create an agent with automatic MCP tool discovery
agent = Agent(
name="MCPAgent",
model="gpt-4o-mini",
mcp_servers=[server], # Tools discovered automatically
)
response = agent.run("What tools are available?")
Method 2: Direct Integration with HostedMCPTool
For scenarios requiring explicit tool control or when you want to expose a specific MCP tool without running discovery, use the HostedMCPTool class. This approach wraps a single MCP tool as a standard function tool that you append directly to the agent's tools list.
The HostedMCPTool constructor requires:
tool_config: AnMcpTooldefinition created viaMCPUtil.to_mcp_tool().server_name: The identifier tying the tool to a specificMCPServerinstance.
from agents import Agent, HostedMCPTool
from agents.mcp.server import MCPServerStdio
from agents.mcp.util import MCPUtil
server = MCPServerStdio(command="python -m my_mcp_server")
# Define the MCP tool metadata explicitly
tool_config = MCPUtil.to_mcp_tool(
name="list_documents",
description="Return a list of document IDs available to the agent.",
parameters={"type": "object", "properties": {}}
)
# Wrap as a hosted tool
hosted_tool = HostedMCPTool(
tool_config=tool_config,
server_name=server.server_name
)
# Add to agent's tools list
agent = Agent(
name="HostedToolAgent",
model="gpt-4o-mini",
mcp_servers=[server],
tools=[hosted_tool]
)
Understanding the MCP Execution Flow
When an agent invokes an MCP tool, the SDK orchestrates a specific request-response lifecycle defined in src/agents/run_internal/run_steps.py and src/agents/items.py.
Approval Request Phase
The LLM generates a tool call targeting an MCP-hosted function. The SDK creates a ToolRunMCPApprovalRequest (internal step) and surfaces it as an MCPApprovalRequestItem. The HostedMCPTool instance associated with the specific server handles the approval callback.
Execution Phase
Upon approval, the SDK forwards the request to the MCPServer implementation. For stdio servers, this involves writing the JSON-RPC request to the process's stdin and parsing the response from stdout.
Response Phase
The MCP server returns the tool result, which the SDK wraps as an MCPApprovalResponseItem in src/agents/items.py. This item feeds back into the conversation history for the next model turn.
Complete Example: Building an MCP Server and Client
Below is a complete workflow demonstrating both server implementation and agent integration.
Step 1: Create a Simple MCP Server
This example server implements the required list_tools and call_tool methods via stdio:
# my_mcp_server.py
import json
import sys
def handle_request(req: dict) -> dict:
if req["type"] == "list_tools":
return {
"type": "list_tools_response",
"tools": [
{
"name": "echo",
"description": "Return the supplied text unchanged.",
"parameters": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"]
}
}
]
}
if req["type"] == "call_tool" and req["tool"] == "echo":
return {
"type": "call_tool_response",
"output": req["arguments"]["text"]
}
return {"type": "error", "message": "unknown request"}
# Process stdin/stdout loop
for line in sys.stdin:
payload = json.loads(line)
response = handle_request(payload)
print(json.dumps(response), flush=True)
Step 2: Register for Automatic Discovery
from agents import Agent
from agents.mcp.server import MCPServerStdio
server = MCPServerStdio(command="python -m my_mcp_server")
agent = Agent(
name="AutoDiscoveryAgent",
model="gpt-4o-mini",
mcp_servers=[server]
)
result = agent.run("Please call the echo tool with text 'Hello MCP'")
print(result.content) # Output: Hello MCP
Step 3: Use HostedMCPTool for Explicit Registration
from agents import Agent, HostedMCPTool
from agents.mcp.util import MCPUtil
from agents.mcp.server import MCPServerStdio
# Reuse the same server configuration
server = MCPServerStdio(command="python -m my_mcp_server")
# Explicitly define the echo tool
echo_config = MCPUtil.to_mcp_tool(
name="echo",
description="Echo back the provided text.",
parameters={
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"]
}
)
hosted_echo = HostedMCPTool(
tool_config=echo_config,
server_name=server.server_name
)
agent = Agent(
name="ExplicitToolAgent",
model="gpt-4o-mini",
mcp_servers=[server], # Still required for connection management
tools=[hosted_echo]
)
Summary
- Automatic discovery requires passing
MCPServerinstances to themcp_serversparameter when constructing anAgent; the SDK handles tool enumeration viaMCPListToolsItemand conversion throughMCPUtil.to_function_tool()insrc/agents/mcp/util.py. - Direct tool exposure utilizes
HostedMCPToolfromsrc/agents/tool.pyto wrap specific MCP tools as regular function tools, bypassing the discovery step while requiring the server inmcp_serversfor connection lifecycle management. - Server lifecycle is managed by
MCPServerManagerinsrc/agents/mcp/manager.py, which orchestrates startup and shutdown of stdio or SSE servers. - Execution flow involves
MCPApprovalRequestItemandMCPApprovalResponseItemfromsrc/agents/items.py, with the internal step represented byToolRunMCPApprovalRequestinsrc/agents/run_internal/run_steps.py.
Frequently Asked Questions
What is the difference between using mcp_servers and HostedMCPTool?
Passing servers to mcp_servers enables automatic tool discovery, where the agent queries the server for all available tools on the first turn using MCPListToolsItem. In contrast, HostedMCPTool allows you to manually expose specific tools by defining the tool schema upfront with MCPUtil.to_mcp_tool() and adding the wrapper directly to the agent's tools list. Both methods require the server to be present in mcp_servers to manage the connection lifecycle.
How does the SDK handle MCP tool approvals?
When an LLM invokes an MCP tool, the SDK creates an MCPApprovalRequestItem (defined in src/agents/items.py) and pauses the model turn. The HostedMCPTool instance associated with the target server processes the request. Once approved (either by default policy or callback), the SDK forwards the request to the MCPServer implementation and wraps the result in an MCPApprovalResponseItem for the next conversation turn.
Can I use remote MCP servers with the openai-agents-python SDK?
Yes. While MCPServerStdio in src/agents/mcp/server.py handles local subprocesses, the abstract MCPServer base class supports custom implementations for remote connections. You can subclass MCPServer to implement SSE (Server-Sent Events) or HTTP-based MCP servers, then pass these instances to the agent's mcp_servers parameter exactly like local stdio servers.
Where does the automatic tool discovery happen in the source code?
Automatic discovery is orchestrated in src/agents/mcp/util.py via MCPUtil.to_function_tool(), which converts McpTool definitions into FunctionTool objects. The discovery request itself originates as a MCPListToolsItem from src/agents/items.py, processed during the agent run initialization phase managed by MCPServerManager in src/agents/mcp/manager.py.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →