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
|
- results.md
|
||||||
- streaming.md
|
- streaming.md
|
||||||
- tools.md
|
- tools.md
|
||||||
|
- mcp.md
|
||||||
- handoffs.md
|
- handoffs.md
|
||||||
- tracing.md
|
- tracing.md
|
||||||
- context.md
|
- context.md
|
||||||
|
|
@ -60,6 +61,8 @@ nav:
|
||||||
- ref/models/interface.md
|
- ref/models/interface.md
|
||||||
- ref/models/openai_chatcompletions.md
|
- ref/models/openai_chatcompletions.md
|
||||||
- ref/models/openai_responses.md
|
- ref/models/openai_responses.md
|
||||||
|
- ref/mcp/server.md
|
||||||
|
- ref/mcp/util.md
|
||||||
- Tracing:
|
- Tracing:
|
||||||
- ref/tracing/index.md
|
- ref/tracing/index.md
|
||||||
- ref/tracing/create.md
|
- ref/tracing/create.md
|
||||||
|
|
@ -107,6 +110,8 @@ plugins:
|
||||||
show_signature_annotations: true
|
show_signature_annotations: true
|
||||||
# Makes the font sizes nicer
|
# Makes the font sizes nicer
|
||||||
heading_level: 3
|
heading_level: 3
|
||||||
|
# Show inherited members
|
||||||
|
inherited_members: true
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
# Remove material generation message in footer
|
# 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.
|
"""Create a new MCP server based on the stdio transport.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
params: The params that configure the server. This includes:
|
params: The params that configure the server. This includes the command to run to
|
||||||
- The command (e.g. `python` or `node`) that starts the server.
|
start the server, the args to pass to the command, the environment variables to
|
||||||
- The args to pass to the server command (e.g. `foo.py` or `server.js`).
|
set for the server, the working directory to use when spawning the process, and
|
||||||
- The environment variables to set for the server.
|
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
|
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
|
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
|
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.
|
"""Create a new MCP server based on the HTTP with SSE transport.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
params: The params that configure the server. This includes:
|
params: The params that configure the server. This includes the URL of the server,
|
||||||
- The URL of the server.
|
the headers to send to the server, the timeout for the HTTP request, and the
|
||||||
- The headers to send to the server.
|
timeout for the SSE connection.
|
||||||
- The timeout for the HTTP request.
|
|
||||||
- The timeout for the SSE connection.
|
|
||||||
|
|
||||||
cache_tools_list: Whether to cache the tools list. If `True`, the tools list will be
|
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
|
cached and only fetched from the server once. If `False`, the tools list will be
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue