Skip to content

feat: MCP Sampling security — bidirectional middleware pipeline - #114

Merged
nfvelten merged 1 commit into
masterfrom
feat/mcp-sampling-security
Apr 7, 2026
Merged

feat: MCP Sampling security — bidirectional middleware pipeline#114
nfvelten merged 1 commit into
masterfrom
feat/mcp-sampling-security

Conversation

@nfvelten

@nfvelten nfvelten commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Summary

MCP servers can initiate sampling/createMessage and elicitation/create requests toward the client (reverse direction: server→client). Previously these bypassed all security controls — no rate limiting, no payload filtering, no audit.

This PR closes that gap with a minimal, backward-compatible architecture change:

  • Middleware trait gains check_response(&self, ctx) -> Decision with a default Allow implementation — existing middlewares need zero changes.
  • Pipeline gains run_response() that calls check_response on each middleware in order, stopping at the first Block.
  • PayloadFilterMiddleware::check_response scans sampling/createMessage message text and elicitation/create prompt strings for blocked patterns and injection signatures.
  • RateLimitMiddleware::check_response counts server-initiated sampling requests against the agent's global rate limit (server-triggered LLM inference is billed to the agent — must be metered).
  • McpGateway::handle_server_request is the new transport-level entry point: runs run_response, emits audit entries for every server-initiated message, and returns a JSON-RPC error to the server when blocked. Transport layers call this when they detect a server-initiated message in the upstream SSE stream.

Closes #86

Test plan

  • cargo fmt --check — clean
  • cargo clippy -- -D warnings — zero warnings
  • cargo test --lib — all 446 unit tests pass
  • run_response_empty_pipeline_allows — empty pipeline passes
  • run_response_default_impl_allows — middlewares without check_response don't interfere
  • run_response_blocks_when_check_response_blocks — custom check_response can block
  • run_response_stops_at_first_block — short-circuits correctly
  • sampling_clean_message_allowed — benign sampling passes payload filter
  • sampling_blocked_pattern_in_message_blocked — blocked pattern in sampling text
  • elicitation_injection_in_prompt_blocked — injection in elicitation prompt
  • non_sampling_method_skipped_by_check_response — non-sampling methods skipped
  • sampling_redact_mode_does_not_block_on_block_pattern — redact mode respected
  • sampling_clean_message_passes (gateway) — passes through as None
  • sampling_blocked_pattern_returns_error (gateway) — returns JSON-RPC error
  • elicitation_injection_returns_error (gateway) — injection blocked at gateway level

🤖 Generated with Claude Code

#86)

Add check_response() to the Middleware trait (default: Allow) and
run_response() to Pipeline. PayloadFilterMiddleware and
RateLimitMiddleware implement check_response to enforce payload
filtering and rate limiting on server-initiated sampling/createMessage
and elicitation/create messages.

McpGateway::handle_server_request is the new transport entry point:
runs run_response, emits audit entries, and returns a JSON-RPC error
to the server when a request is blocked. 12 new unit tests cover
pipeline routing, payload blocking, injection detection, and the
gateway entry point.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nfvelten
nfvelten merged commit 8806101 into master Apr 7, 2026
3 checks passed
@nfvelten
nfvelten deleted the feat/mcp-sampling-security branch April 7, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: MCP Sampling security — bidirectional middleware pipeline for server→client requests

1 participant