Skip to content

Commit 94665cf

Browse files
authored
Merge pull request #246 from druellan/disabledTools
Feat: Tools can be filtered out using the `disabledTools` configuration option in the server declaration
2 parents 9c99c93 + cfa9206 commit 94665cf

File tree

4 files changed

+45
-1
lines changed

4 files changed

+45
-1
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ All notable changes to this project will be documented in this file.
1212
The format is based on Keep a Changelog,
1313
and this project adheres to Semantic Versioning.
1414

15+
## [0.0.18] - 2025-09-07
16+
17+
### Added
18+
19+
- 🔧 **Support for disabling specific tools**: Tools can be filtered out using the `disabledTools` configuration option in the server declaration.
20+
1521
## [0.0.17] - 2025-07-22
1622

1723
### Added

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ Example config.json:
103103
},
104104
"time": {
105105
"command": "uvx",
106-
"args": ["mcp-server-time", "--local-timezone=America/New_York"]
106+
"args": ["mcp-server-time", "--local-timezone=America/New_York"],
107+
"disabledTools": ["convert_time"] // Disable specific tools if needed
107108
},
108109
"mcp_sse": {
109110
"type": "sse", // Explicitly define type

src/mcpo/main.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ def validate_server_config(server_name: str, server_cfg: Dict[str, Any]) -> None
7171
# Fallback for old SSE config without explicit type
7272
pass
7373
else:
74+
raise ValueError(f"Server '{server_name}' must have either 'command' for stdio or 'type' and 'url' for remote servers")
75+
76+
# Validate disabledTools
77+
disabled_tools = server_cfg.get("disabled_tools")
78+
if disabled_tools is not None:
79+
if not isinstance(disabled_tools, list):
80+
raise ValueError(f"Server '{server_name}' 'disabledTools' must be a list")
81+
for tool_name in disabled_tools:
82+
if not isinstance(tool_name, str):
83+
raise ValueError(f"Server '{server_name}' 'disabledTools' must contain only strings")
7484
raise ValueError(
7585
f"Server '{server_name}' must have either 'command' for stdio or 'type' and 'url' for remote servers"
7686
)
@@ -167,6 +177,10 @@ def create_sub_app(
167177
sub_app.state.api_dependency = api_dependency
168178
sub_app.state.connection_timeout = connection_timeout
169179

180+
# Store list of tools to be disabled, if present
181+
sub_app.state.disabled_tools = server_cfg.get("disabled_tools", [])
182+
183+
170184
# Store client header forwarding configuration
171185
sub_app.state.client_header_forwarding = server_cfg.get(
172186
"client_header_forwarding", {"enabled": False}
@@ -175,6 +189,7 @@ def create_sub_app(
175189
# Store OAuth configuration if present
176190
sub_app.state.oauth_config = server_cfg.get("oauth")
177191

192+
178193
return sub_app
179194

180195

@@ -346,6 +361,15 @@ async def create_dynamic_endpoints(app: FastAPI, api_dependency=None):
346361

347362
tools_result = await session.list_tools()
348363
tools = tools_result.tools
364+
365+
# Filter out disabled tools
366+
disabled_tools = getattr(app.state, "disabled_tools", [])
367+
if disabled_tools:
368+
original_count = len(tools)
369+
tools = [tool for tool in tools if tool.name not in disabled_tools]
370+
filtered_count = original_count - len(tools)
371+
if filtered_count > 0:
372+
logger.info(f"Filtered out {filtered_count} tool(s) for server '{app.title}': {disabled_tools}")
349373

350374
for tool in tools:
351375
endpoint_name = tool.name

src/mcpo/tests/test_hot_reload.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ def test_validate_server_config_missing_url():
4242
validate_server_config("test_server", config)
4343

4444

45+
def test_validate_server_config_disabled_tools_valid():
46+
"""Test validation of server configuration with a valid disabledTools."""
47+
config = {"command": "echo", "args": ["hello"], "disabledTools": ["search-web"]}
48+
validate_server_config("test_server", config)
49+
50+
51+
def test_validate_server_config_disabled_tools_invalid():
52+
"""Test validation fails for an invalid disabledTools."""
53+
config = {"command": "echo", "args": ["hello"], "disabledTools": "not-a-list"}
54+
with pytest.raises(ValueError, match="'disabledTools' must be a list"):
55+
validate_server_config("test_server", config)
56+
57+
4558
def test_load_config_valid():
4659
"""Test loading a valid config file."""
4760
config_data = {

0 commit comments

Comments
 (0)