commit
362a9dc078
6 changed files with 69 additions and 103 deletions
51
docs/mcp.md
Normal file
51
docs/mcp.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# Model context protocol
|
||||
|
||||
The [Model context protocol](https://modelcontextprotocol.io/introduction) (aka MCP) is a way to provide tools and context to the LLM. From the MCP docs:
|
||||
|
||||
> MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.
|
||||
|
||||
The Agents SDK has support for MCP. This enables you to use a wide range of MCP servers to provide tools to your Agents.
|
||||
|
||||
## MCP servers
|
||||
|
||||
Currently, the MCP spec defines two kinds of servers, based on the transport mechanism they use:
|
||||
|
||||
1. **stdio** servers run as a subprocess of your application. You can think of them as running "locally".
|
||||
2. **HTTP over SSE** servers run remotely. You connect to them via a URL.
|
||||
|
||||
You can use the [`MCPServerStdio`][agents.mcp.server.MCPServerStdio] and [`MCPServerSse`][agents.mcp.server.MCPServerSse] classes to connect to these servers.
|
||||
|
||||
For example, this is how you'd use the [official MCP filesystem server](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem).
|
||||
|
||||
```python
|
||||
async with MCPServerStdio(
|
||||
params={
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
|
||||
}
|
||||
) as server:
|
||||
tools = await server.list_tools()
|
||||
```
|
||||
|
||||
## Using MCP servers
|
||||
|
||||
MCP servers can be added to Agents. The Agents SDK will call `list_tools()` on the MCP servers each time the Agent is run. This makes the LLM aware of the MCP server's tools. When the LLM calls a tool from an MCP server, the SDK calls `call_tool()` on that server.
|
||||
|
||||
```python
|
||||
|
||||
agent=Agent(
|
||||
name="Assistant",
|
||||
instructions="Use the tools to achieve the task",
|
||||
mcp_servers=[mcp_server_1, mcp_server_2]
|
||||
)
|
||||
```
|
||||
|
||||
## Caching
|
||||
|
||||
Every time an Agent runs, it calls `list_tools()` on the MCP server. This can be a latency hit, especially if the server is a remote server. To automatically cache the list of tools, you can pass `cache_tools_list=True` to both [`MCPServerStdio`][agents.mcp.server.MCPServerStdio] and [`MCPServerSse`][agents.mcp.server.MCPServerSse]. You should only do this if you're certain the tool list will not change.
|
||||
|
||||
If you want to invalidate the cache, you can call `invalidate_tools_cache()` on the servers.
|
||||
|
||||
## End-to-end example
|
||||
|
||||
View complete working examples at [examples/mcp](https://github.com/openai/openai-agents-python/tree/main/examples/mcp).
|
||||
3
docs/ref/mcp/server.md
Normal file
3
docs/ref/mcp/server.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# `MCP Servers`
|
||||
|
||||
::: agents.mcp.server
|
||||
3
docs/ref/mcp/util.md
Normal file
3
docs/ref/mcp/util.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# `MCP Util`
|
||||
|
||||
::: agents.mcp.util
|
||||
|
|
@ -28,6 +28,7 @@ nav:
|
|||
- results.md
|
||||
- streaming.md
|
||||
- tools.md
|
||||
- mcp.md
|
||||
- handoffs.md
|
||||
- tracing.md
|
||||
- context.md
|
||||
|
|
@ -60,6 +61,8 @@ nav:
|
|||
- ref/models/interface.md
|
||||
- ref/models/openai_chatcompletions.md
|
||||
- ref/models/openai_responses.md
|
||||
- ref/mcp/server.md
|
||||
- ref/mcp/util.md
|
||||
- Tracing:
|
||||
- ref/tracing/index.md
|
||||
- ref/tracing/create.md
|
||||
|
|
@ -107,6 +110,8 @@ plugins:
|
|||
show_signature_annotations: true
|
||||
# Makes the font sizes nicer
|
||||
heading_level: 3
|
||||
# Show inherited members
|
||||
inherited_members: true
|
||||
|
||||
extra:
|
||||
# Remove material generation message in footer
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
import functools
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
from mcp.types import Tool as MCPTool
|
||||
|
||||
from .. import _debug
|
||||
from ..exceptions import AgentsException, ModelBehaviorError, UserError
|
||||
from ..logger import logger
|
||||
from ..run_context import RunContextWrapper
|
||||
from ..tool import FunctionTool, Tool
|
||||
from .server import MCPServer
|
||||
|
||||
|
||||
class MCPUtil:
|
||||
"""Set of utilities for interop between MCP and Agents SDK tools."""
|
||||
|
||||
@classmethod
|
||||
async def get_all_function_tools(cls, servers: list[MCPServer]) -> list[Tool]:
|
||||
"""Get all function tools from a list of MCP servers."""
|
||||
tools = []
|
||||
tool_names: set[str] = set()
|
||||
for server in servers:
|
||||
server_tools = await cls.get_function_tools(server)
|
||||
server_tool_names = {tool.name for tool in server_tools}
|
||||
if len(server_tool_names & tool_names) > 0:
|
||||
raise UserError(
|
||||
f"Duplicate tool names found across MCP servers: "
|
||||
f"{server_tool_names & tool_names}"
|
||||
)
|
||||
tool_names.update(server_tool_names)
|
||||
tools.extend(server_tools)
|
||||
|
||||
return tools
|
||||
|
||||
@classmethod
|
||||
async def get_function_tools(cls, server: MCPServer) -> list[Tool]:
|
||||
"""Get all function tools from a single MCP server."""
|
||||
tools = await server.list_tools()
|
||||
return [cls.to_function_tool(tool, server) for tool in tools]
|
||||
|
||||
@classmethod
|
||||
def to_function_tool(cls, tool: MCPTool, server: MCPServer) -> FunctionTool:
|
||||
"""Convert an MCP tool to an Agents SDK function tool."""
|
||||
invoke_func = functools.partial(cls.invoke_mcp_tool, server, tool)
|
||||
return FunctionTool(
|
||||
name=tool.name,
|
||||
description=tool.description or "",
|
||||
params_json_schema=tool.inputSchema,
|
||||
on_invoke_tool=invoke_func,
|
||||
strict_json_schema=False,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def invoke_mcp_tool(
|
||||
cls, server: MCPServer, tool: MCPTool, context: RunContextWrapper[Any], input_json: str
|
||||
) -> str:
|
||||
"""Invoke an MCP tool and return the result as a string."""
|
||||
try:
|
||||
json_data: dict[str, Any] = json.loads(input_json) if input_json else {}
|
||||
except Exception as e:
|
||||
if _debug.DONT_LOG_TOOL_DATA:
|
||||
logger.debug(f"Invalid JSON input for tool {tool.name}")
|
||||
else:
|
||||
logger.debug(f"Invalid JSON input for tool {tool.name}: {input_json}")
|
||||
raise ModelBehaviorError(
|
||||
f"Invalid JSON input for tool {tool.name}: {input_json}"
|
||||
) from e
|
||||
|
||||
if _debug.DONT_LOG_TOOL_DATA:
|
||||
logger.debug(f"Invoking MCP tool {tool.name}")
|
||||
else:
|
||||
logger.debug(f"Invoking MCP tool {tool.name} with input {input_json}")
|
||||
|
||||
try:
|
||||
result = await server.call_tool(tool.name, json_data)
|
||||
except Exception as e:
|
||||
logger.error(f"Error invoking MCP tool {tool.name}: {e}")
|
||||
raise AgentsException(f"Error invoking MCP tool {tool.name}: {e}") from e
|
||||
|
||||
if _debug.DONT_LOG_TOOL_DATA:
|
||||
logger.debug(f"MCP tool {tool.name} completed.")
|
||||
else:
|
||||
logger.debug(f"MCP tool {tool.name} returned {result}")
|
||||
|
||||
# The MCP tool result is a list of content items, whereas OpenAI tool outputs are a single
|
||||
# string. We'll try to convert.
|
||||
if len(result.content) == 1:
|
||||
return result.content[0].model_dump_json()
|
||||
elif len(result.content) > 1:
|
||||
return json.dumps([item.model_dump() for item in result.content])
|
||||
else:
|
||||
logger.error(f"Errored MCP tool result: {result}")
|
||||
return "Error running tool."
|
||||
|
|
@ -175,10 +175,10 @@ class MCPServerStdio(_MCPServerWithClientSession):
|
|||
"""Create a new MCP server based on the stdio transport.
|
||||
|
||||
Args:
|
||||
params: The params that configure the server. This includes:
|
||||
- The command (e.g. `python` or `node`) that starts the server.
|
||||
- The args to pass to the server command (e.g. `foo.py` or `server.js`).
|
||||
- The environment variables to set for the server.
|
||||
params: The params that configure the server. This includes the command to run to
|
||||
start the server, the args to pass to the command, the environment variables to
|
||||
set for the server, the working directory to use when spawning the process, and
|
||||
the text encoding used when sending/receiving messages to the server.
|
||||
cache_tools_list: Whether to cache the tools list. If `True`, the tools list will be
|
||||
cached and only fetched from the server once. If `False`, the tools list will be
|
||||
fetched from the server on each call to `list_tools()`. The cache can be
|
||||
|
|
@ -235,11 +235,9 @@ class MCPServerSse(_MCPServerWithClientSession):
|
|||
"""Create a new MCP server based on the HTTP with SSE transport.
|
||||
|
||||
Args:
|
||||
params: The params that configure the server. This includes:
|
||||
- The URL of the server.
|
||||
- The headers to send to the server.
|
||||
- The timeout for the HTTP request.
|
||||
- The timeout for the SSE connection.
|
||||
params: The params that configure the server. This includes the URL of the server,
|
||||
the headers to send to the server, the timeout for the HTTP request, and the
|
||||
timeout for the SSE connection.
|
||||
|
||||
cache_tools_list: Whether to cache the tools list. If `True`, the tools list will be
|
||||
cached and only fetched from the server once. If `False`, the tools list will be
|
||||
|
|
|
|||
Loading…
Reference in a new issue