Skip to content

Commit 4450339

Browse files
authored
feat: add MCP tool filtering support (#861)
## Add MCP Tool Filtering Support This PR implements tool filtering capabilities for MCP servers, addressing multiple community requests for this feature. ### Problem Currently, Agent SDK automatically fetches all available tools from MCP servers without the ability to select specific tools. This creates several issues: - Unwanted tools occupy LLM context and affect tool selection accuracy - Security concerns when limiting tool access scope - Tool name conflicts between multiple servers - Need for different tool subsets across different agents ### Solution Implements a two-level filtering system: **Server-level filtering:** ```python server = MCPServerStdio( params={"command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"]}, allowed_tools=["read_file", "write_file"], # whitelist excluded_tools=["delete_file"] # blacklist ) ``` **Agent-level filtering:** ```python agent = Agent( name="Assistant", mcp_servers=[server1, server2], mcp_config={ "allowed_tools": {"server1": ["read_file", "write_file"]}, "excluded_tools": {"server2": ["dangerous_tool"]} } ) ``` ### Features - ✅ Server-level `allowed_tools`/`excluded_tools` parameters for all MCP server types - ✅ Agent-level filtering via `mcp_config` - ✅ Hierarchical filtering (server-level first, then agent-level) - ✅ Comprehensive test coverage (8 test cases) - ✅ Updated documentation with examples ### Related Issues #376, #851, #830, #863 ### Testing All existing tests pass + 8 new test cases covering various filtering scenarios.
1 parent 0475450 commit 4450339

File tree

12 files changed

+675
-45
lines changed

12 files changed

+675
-45
lines changed

docs/ja/mcp.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,20 @@ Agents SDK は MCP をサポートしており、これにより幅広い MCP
2323
たとえば、[公式 MCP filesystem サーバー](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem)を利用する場合は次のようになります。
2424

2525
```python
26+
from agents.run_context import RunContextWrapper
27+
2628
async with MCPServerStdio(
2729
params={
2830
"command": "npx",
2931
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
3032
}
3133
) as server:
32-
tools = await server.list_tools()
34+
# 注意:実際には通常は MCP サーバーをエージェントに追加し、
35+
# フレームワークがツール一覧の取得を自動的に処理するようにします。
36+
# list_tools() への直接呼び出しには run_context と agent パラメータが必要です。
37+
run_context = RunContextWrapper(context=None)
38+
agent = Agent(name="test", instructions="test")
39+
tools = await server.list_tools(run_context, agent)
3340
```
3441

3542
## MCP サーバーの利用

docs/mcp.md

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@ You can use the [`MCPServerStdio`][agents.mcp.server.MCPServerStdio], [`MCPServe
1919
For example, this is how you'd use the [official MCP filesystem server](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem).
2020

2121
```python
22+
from agents.run_context import RunContextWrapper
23+
2224
async with MCPServerStdio(
2325
params={
2426
"command": "npx",
2527
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
2628
}
2729
) as server:
28-
tools = await server.list_tools()
30+
# Note: In practice, you typically add the server to an Agent
31+
# and let the framework handle tool listing automatically.
32+
# Direct calls to list_tools() require run_context and agent parameters.
33+
run_context = RunContextWrapper(context=None)
34+
agent = Agent(name="test", instructions="test")
35+
tools = await server.list_tools(run_context, agent)
2936
```
3037

3138
## Using MCP servers
@@ -41,6 +48,93 @@ agent=Agent(
4148
)
4249
```
4350

51+
## Tool filtering
52+
53+
You can filter which tools are available to your Agent by configuring tool filters on MCP servers. The SDK supports both static and dynamic tool filtering.
54+
55+
### Static tool filtering
56+
57+
For simple allow/block lists, you can use static filtering:
58+
59+
```python
60+
from agents.mcp import create_static_tool_filter
61+
62+
# Only expose specific tools from this server
63+
server = MCPServerStdio(
64+
params={
65+
"command": "npx",
66+
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
67+
},
68+
tool_filter=create_static_tool_filter(
69+
allowed_tool_names=["read_file", "write_file"]
70+
)
71+
)
72+
73+
# Exclude specific tools from this server
74+
server = MCPServerStdio(
75+
params={
76+
"command": "npx",
77+
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
78+
},
79+
tool_filter=create_static_tool_filter(
80+
blocked_tool_names=["delete_file"]
81+
)
82+
)
83+
84+
```
85+
86+
**When both `allowed_tool_names` and `blocked_tool_names` are configured, the processing order is:**
87+
1. First apply `allowed_tool_names` (allowlist) - only keep the specified tools
88+
2. Then apply `blocked_tool_names` (blocklist) - exclude specified tools from the remaining tools
89+
90+
For example, if you configure `allowed_tool_names=["read_file", "write_file", "delete_file"]` and `blocked_tool_names=["delete_file"]`, only `read_file` and `write_file` tools will be available.
91+
92+
### Dynamic tool filtering
93+
94+
For more complex filtering logic, you can use dynamic filters with functions:
95+
96+
```python
97+
from agents.mcp import ToolFilterContext
98+
99+
# Simple synchronous filter
100+
def custom_filter(context: ToolFilterContext, tool) -> bool:
101+
"""Example of a custom tool filter."""
102+
# Filter logic based on tool name patterns
103+
return tool.name.startswith("allowed_prefix")
104+
105+
# Context-aware filter
106+
def context_aware_filter(context: ToolFilterContext, tool) -> bool:
107+
"""Filter tools based on context information."""
108+
# Access agent information
109+
agent_name = context.agent.name
110+
111+
# Access server information
112+
server_name = context.server_name
113+
114+
# Implement your custom filtering logic here
115+
return some_filtering_logic(agent_name, server_name, tool)
116+
117+
# Asynchronous filter
118+
async def async_filter(context: ToolFilterContext, tool) -> bool:
119+
"""Example of an asynchronous filter."""
120+
# Perform async operations if needed
121+
result = await some_async_check(context, tool)
122+
return result
123+
124+
server = MCPServerStdio(
125+
params={
126+
"command": "npx",
127+
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
128+
},
129+
tool_filter=custom_filter # or context_aware_filter or async_filter
130+
)
131+
```
132+
133+
The `ToolFilterContext` provides access to:
134+
- `run_context`: The current run context
135+
- `agent`: The agent requesting the tools
136+
- `server_name`: The name of the MCP server
137+
44138
## Caching
45139

46140
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 [`MCPServerStdio`][agents.mcp.server.MCPServerStdio], [`MCPServerSse`][agents.mcp.server.MCPServerSse], and [`MCPServerStreamableHttp`][agents.mcp.server.MCPServerStreamableHttp]. You should only do this if you're certain the tool list will not change.

src/agents/agent.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,14 +256,18 @@ async def get_prompt(
256256
"""Get the prompt for the agent."""
257257
return await PromptUtil.to_model_input(self.prompt, run_context, self)
258258

259-
async def get_mcp_tools(self) -> list[Tool]:
259+
async def get_mcp_tools(
260+
self, run_context: RunContextWrapper[TContext]
261+
) -> list[Tool]:
260262
"""Fetches the available tools from the MCP servers."""
261263
convert_schemas_to_strict = self.mcp_config.get("convert_schemas_to_strict", False)
262-
return await MCPUtil.get_all_function_tools(self.mcp_servers, convert_schemas_to_strict)
264+
return await MCPUtil.get_all_function_tools(
265+
self.mcp_servers, convert_schemas_to_strict, run_context, self
266+
)
263267

264268
async def get_all_tools(self, run_context: RunContextWrapper[Any]) -> list[Tool]:
265269
"""All agent tools, including MCP tools and function tools."""
266-
mcp_tools = await self.get_mcp_tools()
270+
mcp_tools = await self.get_mcp_tools(run_context)
267271

268272
async def _check_tool_enabled(tool: Tool) -> bool:
269273
if not isinstance(tool, FunctionTool):

src/agents/mcp/__init__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,14 @@
1111
except ImportError:
1212
pass
1313

14-
from .util import MCPUtil
14+
from .util import (
15+
MCPUtil,
16+
ToolFilter,
17+
ToolFilterCallable,
18+
ToolFilterContext,
19+
ToolFilterStatic,
20+
create_static_tool_filter,
21+
)
1522

1623
__all__ = [
1724
"MCPServer",
@@ -22,4 +29,9 @@
2229
"MCPServerStreamableHttp",
2330
"MCPServerStreamableHttpParams",
2431
"MCPUtil",
32+
"ToolFilter",
33+
"ToolFilterCallable",
34+
"ToolFilterContext",
35+
"ToolFilterStatic",
36+
"create_static_tool_filter",
2537
]

0 commit comments

Comments
 (0)