feat(router): support runtime disabling of tools#809
feat(router): support runtime disabling of tools#809lutz-grex wants to merge 3 commits intomodelcontextprotocol:mainfrom
Conversation
Add methods to disable/enable tools at runtime. Disabled tools are hidden from listing, lookup, and execution, including in composed routers. Closes modelcontextprotocol#477
There was a problem hiding this comment.
@lutz-grex How should clients learn about runtime disable/enable? I'm worried that the list_tools results will go stale until the client re-lists. I believe the MCP spec has notifications/tools/list_changed for this.
https://modelcontextprotocol.io/specification/2025-11-25/server/tools#list-changed-notification
|
Thanks for addressing my inline comments, @lutz-grex but I think you might've missed my concern in #809 (review) I'm curious about your thoughts on the |
|
Yes, I overlooked these - good catch. I’ve now added automatic tools/list_changed notifications. When using Router, this is handled transparently: Router::new() registers a deferred notifier that activates after InitializedNotification. From that point on, enable_route and disable_route emit a notification whenever the visible tool set actually changes. The router also advertises tools.listChanged: true in its capabilities. |
Add methods to disable/enable tools at runtime.
Disabled tools are hidden from listing, lookup,
and execution, including in composed routers.
Closes #477
Motivation and Context
Users of the
#[tool]+#[tool_handler]macro system had no way to disable specific tools at runtime. The only workaround was manually implementingServerHandler, losing all macro convenience, or usingremove_routewhich permanently destroys the tool.This change adds reversible disable/enable support directly to
ToolRouter, composing naturally with the existing macro workflow:How Has This Been Tested?
test_tool_routers.rscovering:disable, enable, builder pattern, merge preservation,
remove cleanup, pre-disable before add, iterator
filtering, and full invisibility across all query methods
cargo test -p rmcp)disable_routeandenable_routedo not compile on the baseline and workcorrectly after the change
cargo clippy --all-targets --all-featurescleancargo +nightly fmt --allcleanBreaking Changes
has_routenow returnsfalsefor disabled tools (previouslyit only checked map membership). Code relying on
has_routeto test structural presence regardless of enabled state should
use
is_disabledinstead. TheToolRouterstruct is#[non_exhaustive], so adding the privatedisabledfield isnot a breaking change.
Types of changes
Checklist
Additional context
Three files changed:
crates/rmcp/src/handler/server/router/tool.rs— corefeature:
disabledHashSet field, disable/enable/is_disabled/with_disabled methods, updated list_all/call/get/has_route
/merge/remove_route/IntoIterator
crates/rmcp/src/handler/server/router.rs— one-line fix:transparent_when_not_founddispatch now checksis_disabledso disabled tools cannot leak through to the inner service
crates/rmcp/tests/test_tool_routers.rs— 9 new tests