Skip to content

feat: add MCP tool filtering support #861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Conversation

devtalker
Copy link

@devtalker devtalker commented Jun 13, 2025

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:

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:

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.

@devtalker
Copy link
Author

devtalker commented Jun 13, 2025

Hi @rm-openai! 👋
I've implemented a comprehensive MCP tool filtering feature that addresses the community requests in issues #376, #830, #851, #863. Based on the community discussions, this seems to be a feature many users have been eagerly waiting for! Would you be able to take a look when you have a moment? I'd really appreciate your feedback and guidance on this implementation. Thanks! 🙏

Copy link
Collaborator

@rm-openai rm-openai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this. Would however prefer an interface that looked something like this:

ToolFilterCallable = Callable[[ToolFilterContext, Tool], MaybeAwaitable[bool]]
class ToolFilterStatic(TypedDict):
  allowed_tool_names: NotRequired[list[str]]
  blocked_tool_names: NotRequired[list[str]]

and then the MCPServer class would take tool_filter: ToolFilterCallable | ToolFilterStatic | None

thoughts?

@devtalker
Copy link
Author

@rm-openai Thanks for your suggestions!
I like this approach! It's very similar to the is_enabled pattern we use in function tools, which makes the API more consistent and intuitive. Supporting a callable for dynamic filtering gives us much more flexibility - we can make runtime decisions based on various conditions, just like we do with function tools. I'll update the PR to implement this design. 🙏

@devtalker devtalker force-pushed the main branch 2 times, most recently from a873952 to 3307f96 Compare June 17, 2025 07:05
@devtalker
Copy link
Author

devtalker commented Jun 17, 2025

Hi @rm-openai! 👋

Thank you for the detailed feedback! I've completely redesigned the implementation following your suggested interface pattern.

🔄 Major Changes in This Update:

1. Implemented Your Suggested Interface:

  • Added ToolFilterCallable = Callable[[ToolFilterContext, Tool], MaybeAwaitable[bool]]
  • Created ToolFilterStatic TypedDict with allowed_tool_names and blocked_tool_names
  • MCPServer classes now accept tool_filter: ToolFilterCallable | ToolFilterStatic | None

2. Enhanced Dynamic Filtering Support:
The new interface supports powerful dynamic filtering capabilities:

# Context-aware filtering
def context_filter(context: ToolFilterContext, tool) -> bool:
    # Access agent information
    agent_name = context.agent.name
    # Access server information  
    server_name = context.server_name
    # Custom logic based on runtime context
    return some_dynamic_logic(agent_name, server_name, tool)

# Async filtering for complex operations
async def async_filter(context: ToolFilterContext, tool) -> bool:
    result = await some_async_check(context, tool)
    return result

server = MCPServerStdio(
    params={"command": "npx", "args": [...]},
    tool_filter=context_filter  # or async_filter
)

3. Simplified Static Filtering:

from agents.mcp import create_static_tool_filter

server = MCPServerStdio(
    tool_filter=create_static_tool_filter(
        allowed_tool_names=["read_file", "write_file"],
        blocked_tool_names=["delete_file"]
    )
)

4. Architecture Improvements:

  • Removed Agent-level filtering complexity as discussed
  • Moved all filtering logic to the MCP server layer for better separation of concerns
  • Maintains full backward compatibility

Would you be able to take another look when you have a moment? Thanks again for the great input!

@devtalker devtalker requested a review from rm-openai June 17, 2025 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants