Skip to content

feat(router): support runtime disabling of tools#809

Open
lutz-grex wants to merge 1 commit intomodelcontextprotocol:mainfrom
lutz-grex:feat/tool-router-disable
Open

feat(router): support runtime disabling of tools#809
lutz-grex wants to merge 1 commit intomodelcontextprotocol:mainfrom
lutz-grex:feat/tool-router-disable

Conversation

@lutz-grex
Copy link
Copy Markdown

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 implementing ServerHandler, losing all macro convenience, or using remove_route which permanently destroys the tool.
This change adds reversible disable/enable support directly to ToolRouter, composing naturally with the existing macro workflow:

let mut router = Self::tool_router();
router.disable_route("dangerous_tool");
// later...
router.enable_route("dangerous_tool");

How Has This Been Tested?

  • 9 new unit tests in test_tool_routers.rs covering:
    disable, enable, builder pattern, merge preservation,
    remove cleanup, pre-disable before add, iterator
    filtering, and full invisibility across all query methods
  • All 261 existing tests pass (cargo test -p rmcp)
  • Before/after validation: confirmed disable_route and
    enable_route do not compile on the baseline and work
    correctly after the change
  • cargo clippy --all-targets --all-features clean
  • cargo +nightly fmt --all clean

Breaking Changes

has_route now returns false for disabled tools (previously
it only checked map membership). Code relying on has_route
to test structural presence regardless of enabled state should
use is_disabled instead. The ToolRouter struct is
#[non_exhaustive], so adding the private disabled field is
not a breaking change.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Three files changed:

  • crates/rmcp/src/handler/server/router/tool.rs — core
    feature: disabled HashSet 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_found dispatch now checks is_disabled
    so disabled tools cannot leak through to the inner service
  • crates/rmcp/tests/test_tool_routers.rs — 9 new tests

Add methods to disable/enable tools at runtime.
Disabled tools are hidden from listing, lookup,
and execution, including in composed routers.

Closes modelcontextprotocol#477
@lutz-grex lutz-grex requested a review from a team as a code owner April 15, 2026 09:53
@github-actions github-actions bot added T-test Testing related changes T-core Core library changes T-handler Handler implementation changes labels Apr 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T-core Core library changes T-handler Handler implementation changes T-test Testing related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom disabling of specific tools

1 participant