Merge pull request #246 from druellan/disabledTools

Feat: Tools can be filtered out using the `disabledTools` configuration option in the server declaration
This commit is contained in:
Tim Jaeryang Baek
2025-10-14 15:17:26 -05:00
committed by GitHub
4 changed files with 45 additions and 1 deletions
+6
View File
@@ -12,6 +12,12 @@ All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
## [0.0.18] - 2025-09-07
### Added
- 🔧 **Support for disabling specific tools**: Tools can be filtered out using the `disabledTools` configuration option in the server declaration.
## [0.0.17] - 2025-07-22
### Added
+2 -1
View File
@@ -103,7 +103,8 @@ Example config.json:
},
"time": {
"command": "uvx",
"args": ["mcp-server-time", "--local-timezone=America/New_York"]
"args": ["mcp-server-time", "--local-timezone=America/New_York"],
"disabledTools": ["convert_time"] // Disable specific tools if needed
},
"mcp_sse": {
"type": "sse", // Explicitly define type
+24
View File
@@ -71,6 +71,16 @@ def validate_server_config(server_name: str, server_cfg: Dict[str, Any]) -> None
# Fallback for old SSE config without explicit type
pass
else:
raise ValueError(f"Server '{server_name}' must have either 'command' for stdio or 'type' and 'url' for remote servers")
# Validate disabledTools
disabled_tools = server_cfg.get("disabled_tools")
if disabled_tools is not None:
if not isinstance(disabled_tools, list):
raise ValueError(f"Server '{server_name}' 'disabledTools' must be a list")
for tool_name in disabled_tools:
if not isinstance(tool_name, str):
raise ValueError(f"Server '{server_name}' 'disabledTools' must contain only strings")
raise ValueError(
f"Server '{server_name}' must have either 'command' for stdio or 'type' and 'url' for remote servers"
)
@@ -167,6 +177,10 @@ def create_sub_app(
sub_app.state.api_dependency = api_dependency
sub_app.state.connection_timeout = connection_timeout
# Store list of tools to be disabled, if present
sub_app.state.disabled_tools = server_cfg.get("disabled_tools", [])
# Store client header forwarding configuration
sub_app.state.client_header_forwarding = server_cfg.get(
"client_header_forwarding", {"enabled": False}
@@ -175,6 +189,7 @@ def create_sub_app(
# Store OAuth configuration if present
sub_app.state.oauth_config = server_cfg.get("oauth")
return sub_app
@@ -346,6 +361,15 @@ async def create_dynamic_endpoints(app: FastAPI, api_dependency=None):
tools_result = await session.list_tools()
tools = tools_result.tools
# Filter out disabled tools
disabled_tools = getattr(app.state, "disabled_tools", [])
if disabled_tools:
original_count = len(tools)
tools = [tool for tool in tools if tool.name not in disabled_tools]
filtered_count = original_count - len(tools)
if filtered_count > 0:
logger.info(f"Filtered out {filtered_count} tool(s) for server '{app.title}': {disabled_tools}")
for tool in tools:
endpoint_name = tool.name
+13
View File
@@ -42,6 +42,19 @@ def test_validate_server_config_missing_url():
validate_server_config("test_server", config)
def test_validate_server_config_disabled_tools_valid():
"""Test validation of server configuration with a valid disabledTools."""
config = {"command": "echo", "args": ["hello"], "disabledTools": ["search-web"]}
validate_server_config("test_server", config)
def test_validate_server_config_disabled_tools_invalid():
"""Test validation fails for an invalid disabledTools."""
config = {"command": "echo", "args": ["hello"], "disabledTools": "not-a-list"}
with pytest.raises(ValueError, match="'disabledTools' must be a list"):
validate_server_config("test_server", config)
def test_load_config_valid():
"""Test loading a valid config file."""
config_data = {