Skip to content

security(mcp): implement tools/list_changed refresh path with sanitization#1762

Merged
bug-ops merged 2 commits intomainfrom
mcp-list-changed-sanitize
Mar 14, 2026
Merged

security(mcp): implement tools/list_changed refresh path with sanitization#1762
bug-ops merged 2 commits intomainfrom
mcp-list-changed-sanitize

Conversation

@bug-ops
Copy link
Copy Markdown
Owner

@bug-ops bug-ops commented Mar 14, 2026

Summary

Fixes #1746. MCP servers can push tool list updates via tools/list_changed notifications. This PR implements the handler so that sanitize_tools() is always called before refreshed tools enter the registry or system prompt.

  • Add ToolListChangedHandler implementing rmcp::ClientHandler; applies rate-limiting (5 s/server) and MAX_TOOLS_PER_SERVER=100 cap before sanitize_tools(), then sends a ToolRefreshEvent via mpsc
  • McpManager::spawn_refresh_task() receives events and broadcasts via watch::Sender<Vec<McpTool>>
  • McpManager::subscribe_tool_changes() exposes the watch receiver to callers
  • remove_server() cleans up server_tools HashMap and last_refresh DashMap
  • shutdown_all_shared() drops refresh_tx, terminating the refresh task cleanly
  • Agent checks watch::Receiver between turns via check_tool_refresh(), guarded against consuming the empty initial value

Security invariants: sanitize_tools() is unconditionally called before tools reach the channel or the agent — same guarantee as the connect_all() and add_server() paths.

Test plan

  • 5527 tests pass (cargo nextest run --workspace --features full --lib --bins)
  • cargo clippy --workspace --features full -- -D warnings passes (0 warnings)
  • cargo +nightly fmt --check passes
  • New unit tests cover: sanitization in refresh path, rate-limiting, tool count cap truncation, empty initial value guard, remove_server cleanup, shutdown lifecycle, multi-server refresh
  • Security audit: all three tool delivery paths (connect_all, add_server, watch channel) verified to call sanitize_tools before tools reach the agent

…ation

Implement MCP tools/list_changed notification handler in McpManager so that
tool list refreshes pushed by servers are sanitized before entering the system
prompt (closes #1746).

Key changes:
- Add ToolListChangedHandler implementing rmcp::ClientHandler; handler applies
  rate-limiting (5 s per server) and MAX_TOOLS_PER_SERVER cap (100) before
  calling sanitize_tools(), then sends a ToolRefreshEvent via mpsc
- McpManager gains a background refresh task (spawn_refresh_task) that receives
  ToolRefreshEvents and broadcasts via watch::Sender<Vec<McpTool>>
- subscribe_tool_changes() exposes the watch receiver to callers
- remove_server() cleans up server_tools HashMap and last_refresh DashMap
- shutdown_all_shared() drops refresh_tx, terminating the refresh task cleanly
- Agent checks watch::Receiver between turns via check_tool_refresh(), guarded
  against consuming the empty initial value

Security invariants: sanitize_tools() is always called before tools reach the
channel or the agent — same guarantee as the connect_all()/add_server() paths.
@github-actions github-actions bot added size/XL Extra large PR (500+ lines) documentation Improvements or additions to documentation llm zeph-llm crate (Ollama, Claude) rust Rust code changes core zeph-core crate and removed size/XL Extra large PR (500+ lines) labels Mar 14, 2026
@bug-ops bug-ops enabled auto-merge (squash) March 14, 2026 17:06
@github-actions github-actions bot added size/XL Extra large PR (500+ lines) and removed llm zeph-llm crate (Ollama, Claude) labels Mar 14, 2026
@bug-ops bug-ops merged commit dd16e29 into main Mar 14, 2026
15 checks passed
@bug-ops bug-ops deleted the mcp-list-changed-sanitize branch March 14, 2026 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core zeph-core crate documentation Improvements or additions to documentation rust Rust code changes size/XL Extra large PR (500+ lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security(mcp): tools/list_changed refresh path bypasses sanitize_tools()

1 participant