You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This SEP proposes an interceptor framework for the Model Context Protocol (MCP) that enables intercepting, validating, and transforming messages at various extension points within the protocol lifecycle. The interceptor system introduces a new resource type that can hook into MCP events including server features (tool discovery, tool invocation, prompt handling, resource access), client features (sampling requests, elicitation requests, roots access), as well as LLM interactions (chat completions). Interceptors can be deployed on both client and server sides, supporting validation with severity levels (info, warn, error), mutation operations that replace message content, and observability for auditing, logging, and metrics collection. The framework follows MCP's JSON-RPC patterns and includes a discovery mechanism similar to existing tools, prompts, and resources.
Motivation
The Problem: A Sprawling Ecosystem Without Reusability
The MCP ecosystem is rapidly developing a sprawling landscape of sidecars, proxies, and gateways to address cross-cutting concerns like validation, security, observability, and rate limiting. However, these implementations are largely:
Non-reusable: Each solution is tightly coupled to specific deployment patterns or languages
Non-interoperable: Different approaches don't work together, creating vendor lock-in
Duplicative: Similar functionality is reimplemented across different tools and servers
Fragmented: No common interface or discovery mechanism exists
This leads to an M × N problem: Each of M clients must integrate with each of N middleware solutions, resulting in M × N integration points and configurations.
The Solution: Standardized Plug-and-Play Interceptors
This SEP proposes standardizing interceptors in the same plug-and-play fashion that made MCP tools successful. The core goal is to transform the M × N problem into an M + N problem:
Clients implement the interceptor call pattern once
Servers expose interceptors through a standardized interface once
Platform teams can deploy interceptors across all compatible clients and servers
Key Advantages
1. Plug-and-Play Simplicity
Just as tools made MCP powerful through discoverability and standardization, interceptors bring the same benefits to cross-cutting concerns:
Add gateway-level interceptor → Plug in configuration → All tools and servers gain new capabilities
2. Language-Agnostic Deployment
Unlike framework-specific middleware (e.g., FastAPI middleware, Express.js middleware), interceptors enable platform teams to:
Introduce new capabilities company-wide regardless of implementation language
Guarantee consistent behavior across Python, TypeScript, Go, Rust, or any other server implementation
Deploy guardrails and policies without requiring code changes to each service
3. Sidecar and Service-Based Architecture
While traditional library-based middleware requires code integration, interceptors support:
First-party interceptors: Deployed as code running locally within the server process
Third-party interceptors: Deployed as separate services that servers call out to
Hybrid approaches: Mix local and remote interceptors based on security, performance, and organizational requirements
This flexibility is critical for:
Security: Sensitive validation logic can run in isolated, hardened services
Compliance: Audit logging can be centralized and immutable
Performance: High-throughput operations can use in-process interceptors while complex policies use dedicated services
4. Well-Defined Invocation Lifecycle
Interceptors provide a standardized execution model with clear semantics for:
Event subscription and filtering
Request/response phase handling
Priority-based ordering for mutations
Validation severity levels (info, warn, error)
Error handling and propagation
Technical Gaps Addressed
The current MCP specification lacks a standardized mechanism for:
Input Validation: No way to validate tool parameters, prompt inputs, resource requests, sampling requests, or elicitation requests before processing
Output Transformation: No capability to transform or sanitize responses before they reach clients or servers
Cross-cutting Concerns: No support for logging, rate limiting, or content filtering that applies across multiple protocol operations (both server and client features)
Policy Enforcement: No mechanism to enforce organizational policies on what prompts can be sent to LLMs, what tool results are returned, what sampling models can be used, or what data can be elicited from users
Security Boundaries: When MCP servers and clients cross trust boundaries, there's no way to inspect or validate messages in either direction
LLM Interaction Control: While MCP handles tool/resource/prompt orchestration and sampling requests, there's no hook into these interactions for content filtering or prompt injection detection
Client Feature Protection: No way to prevent servers from requesting excessive sampling tokens, asking for sensitive information through elicitation, or accessing unauthorized filesystem roots
Observability: No standardized way to collect metrics, traces, and audit logs across MCP operations including client-provided capabilities
These limitations become critical in enterprise deployments where:
Multiple MCP servers with varying trust levels need coordination
Compliance requirements mandate audit trails and content filtering
Security policies require validation of inputs and outputs
Performance monitoring requires instrumentation across protocol operations
Deploy interceptors once, gain coverage across all compatible servers and clients
Enforce company-wide policies without modifying individual services
Centralize security, compliance, and observability concerns
For Server Developers
Inherit rich ecosystem of interceptors without custom integration work
Focus on core business logic rather than cross-cutting concerns
Support both local and remote interceptor patterns
For Client Developers
Discover and utilize interceptors through standard MCP mechanisms
Consistent behavior across different servers
Reduced integration complexity
For the Community
Build reusable interceptors that work across the entire ecosystem
Share best practices through standardized implementations
Foster innovation in security, observability, and policy enforcement
An interceptor framework addresses these needs by providing standardized extension points that maintain MCP's design principles while enabling powerful cross-cutting functionality and solving the ecosystem's reusability challenge.
f
Specification
1. Interceptor Resource Type
Interceptors are introduced as a new first-class resource type in MCP, similar to tools, prompts, and resources.
1.1 Interceptor Definition
interfaceInterceptor{/** * Unique identifier for the interceptor */name: string;/** * Semantic version of this interceptor */version?: string;/** * Human-readable description */description?: string;/** * Events this interceptor subscribes to */events: InterceptorEvent[];/** * Interceptor operation type */type: "validation"|"mutation"|"observability";/** * Execution phase */phase: "request"|"response"|"both";/** * Priority hint for mutation interceptor ordering (lower executes first). * * Can be specified as: * - A single number: applies to both request and response phases * - An object: specify different priorities per phase * * Range: 32-bit signed integer (-2,147,483,648 to 2,147,483,647) * Default: 0 if omitted * * Tie-breaker: Interceptors with equal priorityHint are ordered alphabetically by name. * For validation and observability interceptors, this field is ignored. * * Examples: * priorityHint: -1000 // Same priority for both phases * priorityHint: { request: -1000 } // Request: -1000, Response: 0 (default) * priorityHint: { response: 1000 } // Request: 0 (default), Response: 1000 * priorityHint: { request: -1000, response: 1000 } // Different per phase */priorityHint?: number|{request?: number;response?: number;};/** * Protocol version compatibility */compat?: {/** * Minimum MCP protocol version required */minProtocol: string;/** * Maximum MCP protocol version supported (optional) */maxProtocol?: string;};/** * Optional JSON Schema for interceptor configuration * Documents the expected configuration format */configSchema?: {type: "object";properties?: Record<string,unknown>;required?: string[];additionalProperties?: boolean;};}
1.2 Interceptor Events
typeInterceptorEvent=// MCP Server Features|"tools/list"|"tools/call"|"prompts/list"|"prompts/get"|"resources/list"|"resources/read"|"resources/subscribe"// MCP Client Features|"sampling/createMessage"|"elicitation/create"|"roots/list"// LLM Interaction Events (using common format)|"llm/completion"// Wildcard|"*/request"|"*/response"|"*";
{jsonrpc: "2.0",id: 1,result: {interceptors: [{name: "content-filter",version: "1.2.0",description: "Filters sensitive content from prompts and responses",events: ["llm/completion","prompts/get"],type: "mutation",phase: "both",priorityHint: -500,// Same priority for both request and responsecompat: {minProtocol: "2024-11-05"},configSchema: {type: "object",properties: {sensitivityLevel: {type: "string",enum: ["low","medium","high"]},redactPatterns: {type: "array",items: {type: "string"}}}}},{name: "parameter-validator",version: "2.0.1",description: "Validates tool call parameters",events: ["tools/call"],type: "validation",phase: "request",compat: {minProtocol: "2024-11-05",maxProtocol: "2025-12-31"},configSchema: {type: "object",properties: {strictMode: {type: "boolean"}}}},{name: "pii-redactor",version: "2.1.0",description: "Redacts PII from requests and responses",events: ["tools/call","llm/completion"],type: "mutation",phase: "both",// Different priorities for request vs responsepriorityHint: {request: -1000,// Redact early when sendingresponse: 1000// Redact late when receiving (after decryption)},compat: {minProtocol: "2024-11-05"},configSchema: {type: "object",properties: {patterns: {type: "array",items: {type: "string"}}}}},{name: "sampling-guard",version: "1.0.0",description: "Enforces token limits and model policies for sampling requests",events: ["sampling/createMessage"],type: "validation",phase: "request",compat: {minProtocol: "2025-06-18"},configSchema: {type: "object",properties: {maxTokens: {type: "number"},allowedModels: {type: "array",items: {type: "string"}}}}},{name: "elicitation-pii-blocker",version: "1.0.0",description: "Prevents elicitation requests from asking for sensitive information",events: ["elicitation/create"],type: "validation",phase: "request",compat: {minProtocol: "2025-06-18"},configSchema: {type: "object",properties: {blockedPatterns: {type: "array",items: {type: "string"}},sensitiveFields: {type: "array",items: {type: "string"}}}}},{name: "audit-logger",version: "1.0.0",description: "Logs all MCP operations for compliance",events: ["*"],type: "observability",phase: "both",configSchema: {type: "object",properties: {destination: {type: "string",enum: ["local","remote","both"]},includePayloads: {type: "boolean"}},required: ["destination"]}}]}}
2.2 Unified Result Envelope
All interceptor invocations return results conforming to a unified envelope structure:
/** * Base result shared by all interceptor types */interfaceBaseInterceptorResult{/** * Name of the interceptor that produced this result */interceptor: string;/** * Type of interceptor */type: "validation"|"mutation"|"observability";/** * Phase when this interceptor executed */phase: "request"|"response";/** * Execution duration in milliseconds */durationMs?: number;/** * Additional interceptor-specific information */info?: Record<string,unknown>;}/** * Validation interceptor result */interfaceValidationResultextendsBaseInterceptorResult{type: "validation";/** * Overall validation outcome */valid: boolean;/** * Validation severity (error blocks execution) */severity?: "info"|"warn"|"error";/** * Detailed validation messages */messages?: Array<{path?: string;message: string;severity: "info"|"warn"|"error";}>;/** * Optional suggested corrections */suggestions?: Array<{path: string;value: unknown;}>;/** * Future: Cryptographic signature for validation result */signature?: {algorithm: "ed25519";publicKey: string;value: string;};}/** * Mutation interceptor result */interfaceMutationResultextendsBaseInterceptorResult{type: "mutation";/** * Whether the payload was modified */modified: boolean;/** * The mutated payload (or original if not modified) */payload: unknown;}/** * Observability interceptor result */interfaceObservabilityResultextendsBaseInterceptorResult{type: "observability";/** * Whether observation was recorded */observed: boolean;/** * Optional metrics collected */metrics?: Record<string,number>;}typeInterceptorResult=ValidationResult|MutationResult|ObservabilityResult;
{jsonrpc: "2.0",id: 2,result: {valid: false,severity: "error",messages: [{path: "params.arguments.location",message: "Input contains potentially malicious content",severity: "error"}],// Optional: suggested correctionssuggestions?: [{path: "params.arguments.location",value: "[REDACTED]"}],// Future: Cryptographic signature for validation result// This enables verification that validation occurred at trust boundarysignature?: {algorithm: "ed25519",publicKey: "...",value: "..."}}}
Note: The signature field is reserved for future use to enable cryptographic verification of validation results at trust boundaries. This will allow recipients to verify that validations were performed by authorized interceptor before accepting data.
Request for Mutation:
{jsonrpc: "2.0",id: 3,method: "interceptor/invoke",params: {name: "content-filter",event: "llm/completion",phase: "request",payload: {// LLM completion request (following common format)messages: [{role: "user",content: "What is the password for admin@example.com?"}],model: "gpt-4"}}}
Response for Mutation:
{jsonrpc: "2.0",id: 3,result: {modified: true,payload: {messages: [{role: "user",content: "What is the password for [REDACTED_EMAIL]?"}],model: "gpt-4"},// Additional information about the mutationinfo: {redactions: 1,reason: "PII detected and redacted"}}}
{jsonrpc: "2.0",id: 4,result: {observed: true,// Optional metrics collectedmetrics: {payloadSize: 156,processingTime: 2},// Additional information about what was observedinfo: {logId: "log-xyz-789",destination: "audit-system",// Optional alerts or notifications triggeredalerts?: [{level: "info",message: "High-privilege command executed",tags: ["security","audit"]}]}}}
2.4 Interceptor Chain Execution
Request:
{jsonrpc: "2.0",id: 5,method: "interceptor/executeChain",params: {event: "tools/call",phase: "request",payload: {method: "tools/call",params: {name: "get_weather",arguments: {location: "San Francisco"}}},// Optional: specify which interceptor to includeinterceptor?: string[];// Optional: per-invocation configurationconfig?: Record<string,Record<string,unknown>>;// Optional: timeout for entire chaintimeoutMs?: number;// Optional: context passed to all interceptorcontext?: {principal?: {type: "user"|"service"|"anonymous";id?: string;};traceId?: string;timestamp: string;};}}
Response:
{jsonrpc: "2.0",id: 5,result: {/** * Overall chain execution status */status: "success"|"validation_failed"|"mutation_failed"|"timeout",/** * Event and phase for this chain */event: "tools/call",phase: "request",/** * Results from all executed interceptor */results: [{interceptor: "pii-redactor",type: "mutation",phase: "request",modified: true,payload: {/* mutated payload */},durationMs: 5},{interceptor: "content-filter",type: "mutation",phase: "request",modified: false,payload: {/* unchanged */},durationMs: 3},{interceptor: "parameter-validator",type: "validation",phase: "request",valid: true,durationMs: 2},{interceptor: "audit-logger",type: "observability",phase: "request",observed: true,metrics: {payloadSize: 156},durationMs: 1}],/** * Final payload after all mutations (if chain completed) */finalPayload?: unknown;/** * Validation summary */validationSummary: {errors: 0,warnings: 0,infos: 1},/** * Total execution time */totalDurationMs: 11,/** * If chain was aborted, details about where and why */abortedAt?: {interceptor: string;reason: string;type: "validation"|"mutation"|"timeout";}}}
The interceptor/executeChain method executes the full interceptor chain for an event and phase, handling ordering, parallel execution, and aggregation automatically. This is an optional convenience helper, not required by MCP. Implementations can choose to use individual interceptor/invoke calls if they prefer explicit control. The helper method obeys negotiated capabilities and protocol versions per the MCP Lifecycle initialization.
This is the recommended way to invoke interceptor as it:
Automatically discovers and orders applicable interceptor
Handles mutations sequentially by priorityHint with alphabetical tie-breaking
Executes validations and observability in parallel
Aggregates results into a single response
Provides detailed execution metrics and validation summaries
Simplifies client/server implementation
2.5 Error Handling
Interceptor invocation errors follow standard JSON-RPC error format:
Multiple interceptors can be chained for a single event. The execution model follows a trust-boundary-aware pattern where validation acts as a security gate.
Discovery and Ordering
The interceptor/list method returns all available interceptors. The invoker is responsible for:
Collecting relevant interceptors for an event
Resolving phase-specific priorities (see Section 3.1.3)
Ordering mutations by priority (lower executes first), then alphabetically by name for ties
Executing validations and observability in parallel
Trust Boundary Execution Pattern
Execution order depends on data flow direction:
Sending Data (Client → Server or Server → Client):
Observability logs (fire-and-forget, never blocks)
Mutations process validated data
This pattern ensures validation guards every trust boundary crossing, enabling defense-in-depth and future cryptographic verification.
Interceptor Type Behaviors
Type
Execution
Failure Behavior
Ordering
Mutation
Sequential
Chain halts, returns last valid state
By priorityHint (low→high), then alphabetically
Validation
Parallel
severity: "error" rejects request
None (parallel)
Observability
Parallel, fire-and-forget
Never blocks
None (parallel)
Key semantics:
Mutations are atomic: entire chain succeeds or none apply
Validations with severity: "warn"/"info" don't block execution
Observability failures are logged internally but never affect request flow
4. Example Interceptor Chain
For a tools/call event, assume the following interceptor are available:
// From interceptor/list response:[{name: "pii-redactor",type: "mutation",phase: "both",priorityHint: {request: -1000,// Run first when sendingresponse: 1000// Run last when receiving}},{name: "content-filter",type: "mutation",phase: "both",priorityHint: -500// Same priority for both phases},{name: "format-normalizer",type: "mutation",phase: "both",priorityHint: {request: 100}// Only applies to requests, response uses default (0)},{name: "schema-validator",type: "validation",phase: "request"// No priorityHint - runs in parallel with other validators},{name: "parameter-validator",type: "validation",phase: "request"// No priorityHint - runs in parallel},{name: "audit-logger",type: "observability",phase: "both"// No priorityHint - fire-and-forget}]
sequenceDiagram
participant App as Application
participant Client as MCP Client
participant MW1 as Mutation 1<br/>(PII Redactor)
participant MW2 as Mutation 2<br/>(Content Filter)
participant MW3 as Validation<br/>(Validator)
participant MW4 as Observability<br/>(Audit Logger)
participant Server as MCP Server
Note over App,Server: CLIENT REQUEST (Sending)
App->>Client: tools/call request
Client->>Client: Discover interceptor via interceptor/list<br/>Order mutations by suggestedPriority
Note over Client,MW2: STEP 1: Mutations (Sequential)
Client->>MW1: interceptor/invoke<br/>(pii-redactor, priority=10)
MW1-->>Client: { modified: true, payload: {...} }
Client->>MW2: interceptor/invoke<br/>(content-filter, priority=20)
MW2-->>Client: { modified: true, payload: {...} }
Note over Client,MW4: STEP 2: Validation & Observability (Parallel)<br/>Trust Boundary Check
par Validation before sending
Client->>MW3: interceptor/invoke<br/>(validator)
MW3-->>Client: { valid: true }
and Observability (fire-and-forget)
Client->>MW4: interceptor/invoke<br/>(audit-logger)
MW4-->>Client: { observed: true }
end
Client->>Server: Modified & validated request
Note over Client,Server: ═══ TRUST BOUNDARY ═══
Server-->>Client: tools/call response
Note over App,Server: CLIENT RESPONSE (Receiving)
Note over Client,MW4: STEP 1: Validation & Observability (Parallel)<br/>Trust Boundary Check
par Validation after receiving
Client->>MW3: interceptor/invoke<br/>(response-validator)
MW3-->>Client: { valid: true }
and Observability (fire-and-forget)
Client->>MW4: interceptor/invoke<br/>(audit-logger)
MW4-->>Client: { observed: true }
end
Note over Client,MW2: STEP 2: Mutations (Sequential)
Client->>MW1: interceptor/invoke<br/>(response-enhancer, priority=10)
MW1-->>Client: { modified: false, payload: {...} }
Client->>App: Final validated & processed response
Loading
Server-Side Interceptor Invocation:
sequenceDiagram
participant Client as MCP Client
participant Server as MCP Server
participant MW1 as Validation<br/>(Schema Validator)
participant MW2 as Validation<br/>(Rate Limiter)
participant MW3 as Observability<br/>(Audit Logger)
participant MW4 as Mutation<br/>(Sanitizer)
participant Tool as Tool Implementation
Note over Client,Tool: SERVER REQUEST (Receiving)
Client->>Server: tools/call request
Note over Client,Server: ═══ TRUST BOUNDARY ═══
Server->>Server: Discover interceptor via interceptor/list<br/>Order mutations by suggestedPriority
Note over Server,MW3: STEP 1: Validation & Observability (Parallel)<br/>Trust Boundary Check
par Validation after receiving
Server->>MW1: interceptor/invoke<br/>(schema-validator)
MW1-->>Server: { valid: true }
and Validation
Server->>MW2: interceptor/invoke<br/>(rate-limiter)
MW2-->>Server: { valid: true }
and Observability (fire-and-forget)
Server->>MW3: interceptor/invoke<br/>(audit-logger)
MW3-->>Server: { observed: true }
end
alt Any validation failed
Server-->>Client: Error: Validation failed
else All validations passed
Note over Server,MW4: STEP 2: Mutations (Sequential)
Server->>MW4: interceptor/invoke<br/>(sanitizer, priority=10)
MW4-->>Server: { modified: true, payload: {...} }
Server->>Tool: Execute tool
Tool-->>Server: Tool result
Note over Client,Tool: SERVER RESPONSE (Sending)
Note over Server,MW4: STEP 1: Mutations (Sequential)
Server->>MW4: interceptor/invoke<br/>(response-sanitizer, priority=10)
MW4-->>Server: { modified: true, payload: {...} }
Note over Server,MW3: STEP 2: Validation & Observability (Parallel)<br/>Trust Boundary Check
par Validation before sending
Server->>MW1: interceptor/invoke<br/>(schema-validator)
MW1-->>Server: { valid: true }
and Observability (fire-and-forget)
Server->>MW3: interceptor/invoke<br/>(audit-logger)
MW3-->>Server: { observed: true }
end
Note over Client,Server: ═══ TRUST BOUNDARY ═══
Server-->>Client: Mutated & validated response
end
Loading
Cross-Boundary Interceptor (Client + Server):
sequenceDiagram
participant App as Application
participant Client as MCP Client
participant CMut as Client Mutation<br/>(PII Redactor)
participant CVal as Client Validation<br/>(Validator)
participant CObs as Client Observability<br/>(Logger)
participant Server as MCP Server
participant SVal as Server Validation<br/>(Schema/PII Validator)
participant SMut as Server Mutation<br/>(Sanitizer)
participant SObs as Server Observability<br/>(Audit Logger)
participant Tool as Tool
Note over App,Tool: CLIENT REQUEST (Sending - Trust Boundary)
App->>Client: tools/call with PII
Note over Client,CMut: Client STEP 1: Mutations
Client->>CMut: interceptor/invoke<br/>(pii-redactor)
CMut-->>Client: { modified: true, payload: "[REDACTED]" }
Note over Client,CObs: Client STEP 2: Validation & Observability<br/>(Trust Boundary Check)
par Client validates before sending
Client->>CVal: interceptor/invoke<br/>(validator)
CVal-->>Client: { valid: true }
and Observability
Client->>CObs: interceptor/invoke<br/>(logger)
CObs-->>Client: { observed: true }
end
Client->>Server: Validated request
Note over Client,Server: ═══════════════════════<br/>║ TRUST BOUNDARY ║<br/>═══════════════════════
Note over Server,SObs: Server STEP 1: Validation & Observability<br/>(Trust Boundary Check)
par Server validates after receiving
Server->>SVal: interceptor/invoke<br/>(schema/pii-validator)
SVal-->>Server: { valid: true }
and Observability
Server->>SObs: interceptor/invoke<br/>(audit-logger)
SObs-->>Server: { observed: true }
end
Note over Server,SMut: Server STEP 2: Mutations
Server->>SMut: interceptor/invoke<br/>(sanitizer)
SMut-->>Server: { modified: true, payload: {...} }
Server->>Tool: Execute tool safely
Tool-->>Server: Result
Note over App,Tool: SERVER RESPONSE (Sending - Trust Boundary)
Note over Server,SMut: Server STEP 1: Mutations
Server->>SMut: interceptor/invoke<br/>(response-sanitizer)
SMut-->>Server: { modified: true, payload: {...} }
Note over Server,SObs: Server STEP 2: Validation & Observability<br/>(Trust Boundary Check)
par Server validates before sending
Server->>SVal: interceptor/invoke<br/>(schema-validator)
SVal-->>Server: { valid: true }
and Observability
Server->>SObs: interceptor/invoke<br/>(audit-logger)
SObs-->>Server: { observed: true }
end
Server-->>Client: Validated response
Note over Client,Server: ═══════════════════════<br/>║ TRUST BOUNDARY ║<br/>═══════════════════════
Note over Client,CObs: Client STEP 1: Validation & Observability<br/>(Trust Boundary Check)
par Client validates after receiving
Client->>CVal: interceptor/invoke<br/>(response-validator)
CVal-->>Client: { valid: true }
and Observability
Client->>CObs: interceptor/invoke<br/>(logger)
CObs-->>Client: { observed: true }
end
Note over Client,CMut: Client STEP 2: Mutations
Client->>CMut: interceptor/invoke<br/>(response-enhancer)
CMut-->>Client: { modified: false, payload: {...} }
Client->>App: Final validated result
Note over App,Tool: Validation guards both sides of trust boundary
Loading
4. Payload Formats
This section describes the payload formats for all interceptable events. Payloads are organized into three categories: Server Features (tools, prompts, resources), Client Features (sampling, elicitation, roots), and LLM Interactions (completion requests). Each event type receives equal treatment with format specifications and example use cases.
4.1 Server Feature Events
Server feature events encompass tool discovery and invocation, prompt handling, and resource access. These events follow the standard MCP JSON-RPC format.
During initialization, servers declare interceptor support:
{jsonrpc: "2.0",id: 1,result: {protocolVersion: "2024-11-05",capabilities: {interceptor?: {// Events this server's interceptor can handlesupportedEvents: InterceptorEvent[];},// ...existing capabilities},serverInfo: {name: "example-server",version: "1.0.0"}}}
6. Configuration and Context
Interceptor may receive configuration and context:
interfaceInterceptorInvocationParams{name: string;event: InterceptorEvent;phase: "request"|"response";payload: unknown;// Optional configuration for this invocationconfig?: Record<string,unknown>;// Optional timeout in milliseconds// If exceeded, interceptor execution is cancelled and returns timeout errortimeoutMs?: number;// Optional context informationcontext?: {// Identity informationprincipal?: {type: "user"|"service"|"anonymous";id?: string;claims?: Record<string,unknown>;};// Tracing informationtraceId?: string;spanId?: string;// Timing informationtimestamp: string;// ISO 8601// Session informationsessionId?: string;// FUTURE: Interceptor state propagation// Allows interceptor to share state through the chain// This field is reserved for future enhancementinterceptorState?: Record<string,unknown>;};}
Timeout and Cancellation:
When timeoutMs is specified:
Interceptor execution is cancelled if it exceeds the timeout
A timeout error is returned with code -32000 (Server error)
For interceptor/executeChain, the entire chain is cancelled on timeout
7.1 Context Propagation and Interceptor State (Future)
Status: Reserved for future specification
Inspired by gRPC interceptors and OpenTelemetry context propagation, future versions may support enrichable interceptor state that flows through the entire chain. This would enable:
State Sharing: Early interceptor can enrich context for downstream interceptor
Cross-Cutting Data: Authentication, rate limiting, and audit data flows through chain
Deadline Propagation: Timeout context flows through nested operations
Proposed Model:
// FUTURE: Enhanced context with state propagationinterfaceInterceptorChainContext{// Immutable request context (set by initiator, read-only)readonlyrequest: {principal?: {type: "user"|"service"|"anonymous";id?: string;claims?: Record<string,unknown>;};traceId: string;spanId?: string;timestamp: string;sessionId?: string;};// Mutable interceptor state (enriched by interceptor during chain execution)// Each interceptor can read and write to this shared stateinterceptorState?: Record<string,unknown>;// Deadline for entire operation (inherited and propagated)deadline?: string;// ISO 8601 timestamp// Distributed tracing baggage (key-value pairs for trace correlation)// Follows W3C Baggage specificationbaggage?: Record<string,string>;}
Interceptor State Enrichment:
Mutation and validation interceptor could optionally return context updates:
// FUTURE: Interceptor result with context enrichmentinterfaceMutationResultextendsBaseInterceptorResult{type: "mutation";modified: boolean;payload: unknown;// Optional: Updates to propagate to downstream interceptorcontextUpdates?: {interceptorState?: Record<string,unknown>;baggage?: Record<string,string>;};}
Example Flow:
// Interceptor 1: Authentication enriches context{interceptor: "authenticator",type: "mutation",modified: false,payload: {/* unchanged */},contextUpdates: {interceptorState: {principal: {id: "user-123",roles: ["admin"],authMethod: "oauth2"}}}}// Interceptor 2: Rate limiter reads enriched context// Has access to context.interceptorState.principal{interceptor: "rate-limiter",type: "validation",valid: true,info: {rateLimitUsed: 45,rateLimitMax: 100,// Used principal.id from context for rate limiting},contextUpdates: {interceptorState: {rateLimit: {remaining: 55,resetAt: "2025-11-04T15:30:00Z"}}}}// Interceptor 3: Audit logger has full enriched context// Logs with principal + rate limit info + original request{interceptor: "audit-logger",type: "observability",observed: true,info: {logEntry: {user: "user-123",roles: ["admin"],rateLimitRemaining: 55,action: "tools/call",timestamp: "2025-11-04T15:29:00Z"}}}
Benefits of State Propagation:
Loose Coupling: Interceptor don't need direct dependencies
Composability: New interceptor can leverage existing enriched state
Observability: Full context available for logging and tracing
Immutability: Request context is read-only to prevent tampering
Namespace Conflicts: Interceptor should use namespaced keys (e.g., auth.principal, rateLimit.info)
Size Limits: Context should have reasonable size limits to prevent abuse
Serialization: Context must be JSON-serializable for transport
This feature would be opt-in and backward compatible, with interceptor that don't support context enrichment working unchanged.
Rationale
Design Decisions
1. Separation of Validation, Mutation, and Observability
We explicitly separate these three interceptor types rather than combining them because:
Clear intent: Users know immediately what a interceptor does
Simpler implementation: Each type has focused logic
Better error handling: Validation failures vs mutation failures have different semantics
Performance: Validation and observability can run in parallel, mutations run sequentially
Isolation: Observability failures never affect the request/response flow
Alternative considered: Combined interceptor that can both validate and mutate. Rejected because it creates ambiguity in execution order and error handling.
Receiving: Validate → Mutate → Process (security barrier, then process)
This ensures validation guards all boundary crossings, enabling zero-trust architecture and future cryptographic verification. Alternative (same order for all phases) rejected because validation must be first when receiving untrusted data.
3. Phase-Aware Priority Hints
Mutations declare priorityHint (default: 0) which can differ by phase. Benefits:
Flexibility: PII redaction runs early when sending (-50000), late when receiving (+50000)
Simplicity: Single number for common cases, object for phase-specific needs
Determinism: Alphabetical tie-breaking by name ensures consistency
Wide range: 32-bit integers align with Kubernetes standards
Alternative (global fixed ordering) rejected as too rigid for phase-aware operations like compression/decompression.
4. Other Design Choices
Event-Based Targeting: Interceptors declare specific events (vs. all traffic) for performance and security
Severity Levels (info/warn/error): Graduated response vs. binary pass/fail enables observability without blocking
Replace vs. Patch: Mutations replace entire payloads (vs. JSON Patch) for simplicity and atomicity
Method Names: interceptor/list and interceptor/invoke mirror MCP patterns (tools/list, etc.)
"info" Field: Used instead of "metadata" to avoid confusion with MCP's _meta protocol metadata
Cross-Boundary Support: Client and server interceptors enable defense-in-depth and zero-trust architecture
Related Work and Inspiration
Inspired By:
Kubernetes Admission Controllers: Validation/mutation webhooks (MCP uses replace vs. patch for simplicity)
gRPC Interceptors: Client/server-side interception (MCP adds discoverability and dynamic invocation)
HTTP Middleware: Express.js, ASP.NET patterns (MCP is event-specific vs. path-based)
Adds validation/mutation capabilities to sampling, tools, prompts, and resources
Enables parameter validation, access control, and content filtering across all MCP features
Backward Compatibility
This SEP is fully backward compatible:
Optional Capability: Interceptor is an optional server capability. Servers without interceptor support continue to work.
No Protocol Changes: Existing JSON-RPC methods are unchanged. Interceptor adds new methods (interceptor/list, interceptor/invoke) but doesn't modify existing ones.
Client Compatibility: Clients that don't support interceptor can still communicate with interceptor-enabled servers. Interceptor runs transparently on the server side.
Graceful Degradation: If a client requests interceptor from a server that doesn't support it, the server returns a standard JSON-RPC error, which clients already handle.
Version Negotiation: The existing protocol version negotiation during initialization allows clients and servers to discover interceptor support.
Security Implications
Threat Model
1. Malicious Interceptor
A compromised interceptor could:
Exfiltrate sensitive data from requests/responses
Modify data to inject malicious content
Cause denial of service by rejecting all requests
Bypass security controls by approving invalid inputs
Mitigations:
Interceptor runs in the same trust domain as the server/client
Organizations must vet interceptor before deployment
Interceptor should be sandboxed where possible
Audit logging of all interceptor invocations
Cryptographic signing of interceptor for integrity
2. Interceptor Bypass
Attackers might try to bypass interceptor validation:
Send requests directly to underlying services
Exploit interceptor chain ordering
Mitigations:
Interceptor enforcement must be at the transport layer
Server-side interceptor is mandatory for requests entering the trust boundary
Interceptor chain order must be explicitly configured and immutable
Detailed errors should only be logged, not returned to untrusted clients
Separate internal and external validation messages
4. Denial of Service
Interceptor could be exploited for DoS:
Computationally expensive validation
Infinite loops in mutation logic
Memory exhaustion from large payloads
Mitigations:
Timeouts for interceptor execution
Resource limits (CPU, memory)
Rate limiting on interceptor invocations
Circuit breakers for failing interceptor
5. Privilege Escalation
Interceptor mutation could be used to inject unauthorized operations:
Modifying request parameters to access unauthorized resources
Changing tool invocation targets
Injecting administrative commands
Mitigations:
Validation interceptor should verify request authorization at trust boundaries
Immutable fields should be enforced at protocol level
Audit all mutations that touch security-sensitive fields
Security Best Practices
Principle of Least Privilege: Interceptor should only have access to the data it needs to process
Defense in Depth: Deploy interceptor at both client and server boundaries
Audit Everything: Log all interceptor invocations, including payloads (with PII redaction)
Fail Secure: If interceptor fails, the default should be to reject the request
Input Validation: Interceptor configuration and payloads should be validated
Cryptographic Integrity: Consider signing interceptor responses to prevent tampering
Isolation: Run untrusted interceptor in sandboxed environments
Compliance Considerations
Interceptor enables compliance with:
GDPR: PII detection and redaction
HIPAA: PHI filtering in healthcare contexts
SOC 2: Audit logging and access control
PCI DSS: Sensitive data masking
However, organizations must ensure:
Interceptor doesn't create new compliance violations (e.g., storing PII in logs)
Audit trails are tamper-proof
Data retention policies apply to interceptor logs
Reference Implementation
The reference implementation will provide:
Multi-Language SDKs
SDK libraries for writing and running interceptors in various languages, including Python, TypeScript/JavaScript, Go and other community-contributed languages. These SDKs will provide base classes, helpers, and patterns for building validation, mutation, and observability interceptors.
Common Interceptor Sidecar
A universal interceptor runtime that enables teams to deploy and manage interceptors without modifying individual MCP servers. The sidecar acts as an MCP proxy that loads custom interceptors (both local and remote) via configuration and injects interceptor invocations transparently.
This approach enables platform teams to deploy guardrails across all services from a central control plane without code changes to existing MCP servers.
Interceptor Framework for Model Context Protocol
Preamble
Title: Interceptor Framework for Model Context Protocol
Status: Draft
Created: 2025-11-04
Authors: @sambhav
Abstract
This SEP proposes an interceptor framework for the Model Context Protocol (MCP) that enables intercepting, validating, and transforming messages at various extension points within the protocol lifecycle. The interceptor system introduces a new resource type that can hook into MCP events including server features (tool discovery, tool invocation, prompt handling, resource access), client features (sampling requests, elicitation requests, roots access), as well as LLM interactions (chat completions). Interceptors can be deployed on both client and server sides, supporting validation with severity levels (info, warn, error), mutation operations that replace message content, and observability for auditing, logging, and metrics collection. The framework follows MCP's JSON-RPC patterns and includes a discovery mechanism similar to existing tools, prompts, and resources.
Motivation
The Problem: A Sprawling Ecosystem Without Reusability
The MCP ecosystem is rapidly developing a sprawling landscape of sidecars, proxies, and gateways to address cross-cutting concerns like validation, security, observability, and rate limiting. However, these implementations are largely:
This leads to an M × N problem: Each of M clients must integrate with each of N middleware solutions, resulting in M × N integration points and configurations.
The Solution: Standardized Plug-and-Play Interceptors
This SEP proposes standardizing interceptors in the same plug-and-play fashion that made MCP tools successful. The core goal is to transform the M × N problem into an M + N problem:
Key Advantages
1. Plug-and-Play Simplicity
Just as tools made MCP powerful through discoverability and standardization, interceptors bring the same benefits to cross-cutting concerns:
2. Language-Agnostic Deployment
Unlike framework-specific middleware (e.g., FastAPI middleware, Express.js middleware), interceptors enable platform teams to:
3. Sidecar and Service-Based Architecture
While traditional library-based middleware requires code integration, interceptors support:
This flexibility is critical for:
4. Well-Defined Invocation Lifecycle
Interceptors provide a standardized execution model with clear semantics for:
Technical Gaps Addressed
The current MCP specification lacks a standardized mechanism for:
These limitations become critical in enterprise deployments where:
Ecosystem Benefits
For Platform Teams
For Server Developers
For Client Developers
For the Community
An interceptor framework addresses these needs by providing standardized extension points that maintain MCP's design principles while enabling powerful cross-cutting functionality and solving the ecosystem's reusability challenge.
f
Specification
1. Interceptor Resource Type
Interceptors are introduced as a new first-class resource type in MCP, similar to tools, prompts, and resources.
1.1 Interceptor Definition
1.2 Interceptor Events
2. JSON-RPC Methods
2.1 Interceptor Discovery
Request:
Response:
2.2 Unified Result Envelope
All interceptor invocations return results conforming to a unified envelope structure:
2.3 Interceptor Invocation
Request for Validation:
Response for Validation:
Request for Mutation:
Response for Mutation:
Request for Observability:
Response for Observability:
2.4 Interceptor Chain Execution
Request:
Response:
The
interceptor/executeChainmethod executes the full interceptor chain for an event and phase, handling ordering, parallel execution, and aggregation automatically. This is an optional convenience helper, not required by MCP. Implementations can choose to use individualinterceptor/invokecalls if they prefer explicit control. The helper method obeys negotiated capabilities and protocol versions per the MCP Lifecycle initialization.This is the recommended way to invoke interceptor as it:
priorityHintwith alphabetical tie-breaking2.5 Error Handling
Interceptor invocation errors follow standard JSON-RPC error format:
3. Execution Model
3.1 Interceptor Chain
Multiple interceptors can be chained for a single event. The execution model follows a trust-boundary-aware pattern where validation acts as a security gate.
Discovery and Ordering
The
interceptor/listmethod returns all available interceptors. The invoker is responsible for:Trust Boundary Execution Pattern
Execution order depends on data flow direction:
Sending Data (Client → Server or Server → Client):
Receiving Data (Server ← Client or Client ← Server):
This pattern ensures validation guards every trust boundary crossing, enabling defense-in-depth and future cryptographic verification.
Interceptor Type Behaviors
priorityHint(low→high), then alphabeticallyseverity: "error"rejects requestKey semantics:
severity: "warn"/"info"don't block execution4. Example Interceptor Chain
For a
tools/callevent, assume the following interceptor are available:Full Request/Response Cycle
Key Points:
Error Handling
Interceptor chains handle errors based on type:
// Validation failure: All validations complete, then reject if severity: "error"
{
error: {
code: -32602,
message: "Interceptor validation failed",
data: {
validationErrors: [
{ interceptor: "schema-validator", severity: "error", message: "Required field missing" }
]
}
}
}
3.2 Priority Resolution
When ordering mutations, the invoker resolves phase-specific priorities:
Recommended Priority Ranges:
3.3 Sequence Diagrams
Client-Side Interceptor Invocation:
sequenceDiagram participant App as Application participant Client as MCP Client participant MW1 as Mutation 1<br/>(PII Redactor) participant MW2 as Mutation 2<br/>(Content Filter) participant MW3 as Validation<br/>(Validator) participant MW4 as Observability<br/>(Audit Logger) participant Server as MCP Server Note over App,Server: CLIENT REQUEST (Sending) App->>Client: tools/call request Client->>Client: Discover interceptor via interceptor/list<br/>Order mutations by suggestedPriority Note over Client,MW2: STEP 1: Mutations (Sequential) Client->>MW1: interceptor/invoke<br/>(pii-redactor, priority=10) MW1-->>Client: { modified: true, payload: {...} } Client->>MW2: interceptor/invoke<br/>(content-filter, priority=20) MW2-->>Client: { modified: true, payload: {...} } Note over Client,MW4: STEP 2: Validation & Observability (Parallel)<br/>Trust Boundary Check par Validation before sending Client->>MW3: interceptor/invoke<br/>(validator) MW3-->>Client: { valid: true } and Observability (fire-and-forget) Client->>MW4: interceptor/invoke<br/>(audit-logger) MW4-->>Client: { observed: true } end Client->>Server: Modified & validated request Note over Client,Server: ═══ TRUST BOUNDARY ═══ Server-->>Client: tools/call response Note over App,Server: CLIENT RESPONSE (Receiving) Note over Client,MW4: STEP 1: Validation & Observability (Parallel)<br/>Trust Boundary Check par Validation after receiving Client->>MW3: interceptor/invoke<br/>(response-validator) MW3-->>Client: { valid: true } and Observability (fire-and-forget) Client->>MW4: interceptor/invoke<br/>(audit-logger) MW4-->>Client: { observed: true } end Note over Client,MW2: STEP 2: Mutations (Sequential) Client->>MW1: interceptor/invoke<br/>(response-enhancer, priority=10) MW1-->>Client: { modified: false, payload: {...} } Client->>App: Final validated & processed responseServer-Side Interceptor Invocation:
sequenceDiagram participant Client as MCP Client participant Server as MCP Server participant MW1 as Validation<br/>(Schema Validator) participant MW2 as Validation<br/>(Rate Limiter) participant MW3 as Observability<br/>(Audit Logger) participant MW4 as Mutation<br/>(Sanitizer) participant Tool as Tool Implementation Note over Client,Tool: SERVER REQUEST (Receiving) Client->>Server: tools/call request Note over Client,Server: ═══ TRUST BOUNDARY ═══ Server->>Server: Discover interceptor via interceptor/list<br/>Order mutations by suggestedPriority Note over Server,MW3: STEP 1: Validation & Observability (Parallel)<br/>Trust Boundary Check par Validation after receiving Server->>MW1: interceptor/invoke<br/>(schema-validator) MW1-->>Server: { valid: true } and Validation Server->>MW2: interceptor/invoke<br/>(rate-limiter) MW2-->>Server: { valid: true } and Observability (fire-and-forget) Server->>MW3: interceptor/invoke<br/>(audit-logger) MW3-->>Server: { observed: true } end alt Any validation failed Server-->>Client: Error: Validation failed else All validations passed Note over Server,MW4: STEP 2: Mutations (Sequential) Server->>MW4: interceptor/invoke<br/>(sanitizer, priority=10) MW4-->>Server: { modified: true, payload: {...} } Server->>Tool: Execute tool Tool-->>Server: Tool result Note over Client,Tool: SERVER RESPONSE (Sending) Note over Server,MW4: STEP 1: Mutations (Sequential) Server->>MW4: interceptor/invoke<br/>(response-sanitizer, priority=10) MW4-->>Server: { modified: true, payload: {...} } Note over Server,MW3: STEP 2: Validation & Observability (Parallel)<br/>Trust Boundary Check par Validation before sending Server->>MW1: interceptor/invoke<br/>(schema-validator) MW1-->>Server: { valid: true } and Observability (fire-and-forget) Server->>MW3: interceptor/invoke<br/>(audit-logger) MW3-->>Server: { observed: true } end Note over Client,Server: ═══ TRUST BOUNDARY ═══ Server-->>Client: Mutated & validated response endCross-Boundary Interceptor (Client + Server):
sequenceDiagram participant App as Application participant Client as MCP Client participant CMut as Client Mutation<br/>(PII Redactor) participant CVal as Client Validation<br/>(Validator) participant CObs as Client Observability<br/>(Logger) participant Server as MCP Server participant SVal as Server Validation<br/>(Schema/PII Validator) participant SMut as Server Mutation<br/>(Sanitizer) participant SObs as Server Observability<br/>(Audit Logger) participant Tool as Tool Note over App,Tool: CLIENT REQUEST (Sending - Trust Boundary) App->>Client: tools/call with PII Note over Client,CMut: Client STEP 1: Mutations Client->>CMut: interceptor/invoke<br/>(pii-redactor) CMut-->>Client: { modified: true, payload: "[REDACTED]" } Note over Client,CObs: Client STEP 2: Validation & Observability<br/>(Trust Boundary Check) par Client validates before sending Client->>CVal: interceptor/invoke<br/>(validator) CVal-->>Client: { valid: true } and Observability Client->>CObs: interceptor/invoke<br/>(logger) CObs-->>Client: { observed: true } end Client->>Server: Validated request Note over Client,Server: ═══════════════════════<br/>║ TRUST BOUNDARY ║<br/>═══════════════════════ Note over Server,SObs: Server STEP 1: Validation & Observability<br/>(Trust Boundary Check) par Server validates after receiving Server->>SVal: interceptor/invoke<br/>(schema/pii-validator) SVal-->>Server: { valid: true } and Observability Server->>SObs: interceptor/invoke<br/>(audit-logger) SObs-->>Server: { observed: true } end Note over Server,SMut: Server STEP 2: Mutations Server->>SMut: interceptor/invoke<br/>(sanitizer) SMut-->>Server: { modified: true, payload: {...} } Server->>Tool: Execute tool safely Tool-->>Server: Result Note over App,Tool: SERVER RESPONSE (Sending - Trust Boundary) Note over Server,SMut: Server STEP 1: Mutations Server->>SMut: interceptor/invoke<br/>(response-sanitizer) SMut-->>Server: { modified: true, payload: {...} } Note over Server,SObs: Server STEP 2: Validation & Observability<br/>(Trust Boundary Check) par Server validates before sending Server->>SVal: interceptor/invoke<br/>(schema-validator) SVal-->>Server: { valid: true } and Observability Server->>SObs: interceptor/invoke<br/>(audit-logger) SObs-->>Server: { observed: true } end Server-->>Client: Validated response Note over Client,Server: ═══════════════════════<br/>║ TRUST BOUNDARY ║<br/>═══════════════════════ Note over Client,CObs: Client STEP 1: Validation & Observability<br/>(Trust Boundary Check) par Client validates after receiving Client->>CVal: interceptor/invoke<br/>(response-validator) CVal-->>Client: { valid: true } and Observability Client->>CObs: interceptor/invoke<br/>(logger) CObs-->>Client: { observed: true } end Note over Client,CMut: Client STEP 2: Mutations Client->>CMut: interceptor/invoke<br/>(response-enhancer) CMut-->>Client: { modified: false, payload: {...} } Client->>App: Final validated result Note over App,Tool: Validation guards both sides of trust boundary4. Payload Formats
This section describes the payload formats for all interceptable events. Payloads are organized into three categories: Server Features (tools, prompts, resources), Client Features (sampling, elicitation, roots), and LLM Interactions (completion requests). Each event type receives equal treatment with format specifications and example use cases.
4.1 Server Feature Events
Server feature events encompass tool discovery and invocation, prompt handling, and resource access. These events follow the standard MCP JSON-RPC format.
4.1.1 Tool Events
tools/call
tools/list
Example Use Cases for Tool Interceptors:
4.1.2 Prompt Events
prompts/get
prompts/list
Example Use Cases for Prompt Interceptors:
4.1.3 Resource Events
resources/read
resources/list
resources/subscribe
Example Use Cases for Resource Interceptors:
4.2 Client Feature Events
Client feature events enable servers to request services from clients: sampling (LLM inference), elicitation (user input), and roots (filesystem access). These events follow MCP client capability formats.
4.2.1 Sampling Events
sampling/createMessage
Example Use Cases for Sampling Interceptors:
4.2.2 Elicitation Events
elicitation/create
Example Use Cases for Elicitation Interceptors:
4.2.3 Roots Events
roots/list
Example Use Cases for Roots Interceptors:
4.3 LLM Interaction Events
For LLM interaction events, we use a common format inspired by OpenAI's API:
llm/completion
Example Use Cases for LLM Interceptors:
5. Interceptor Server Capabilities
During initialization, servers declare interceptor support:
6. Configuration and Context
Interceptor may receive configuration and context:
Timeout and Cancellation:
When
timeoutMsis specified:-32000(Server error)interceptor/executeChain, the entire chain is cancelled on timeoutinterceptor/invokecalls timeout independently7. Future Enhancements
7.1 Context Propagation and Interceptor State (Future)
Status: Reserved for future specification
Inspired by gRPC interceptors and OpenTelemetry context propagation, future versions may support enrichable interceptor state that flows through the entire chain. This would enable:
Proposed Model:
Interceptor State Enrichment:
Mutation and validation interceptor could optionally return context updates:
Example Flow:
Benefits of State Propagation:
Design Considerations:
auth.principal,rateLimit.info)This feature would be opt-in and backward compatible, with interceptor that don't support context enrichment working unchanged.
Rationale
Design Decisions
1. Separation of Validation, Mutation, and Observability
We explicitly separate these three interceptor types rather than combining them because:
Alternative considered: Combined interceptor that can both validate and mutate. Rejected because it creates ambiguity in execution order and error handling.
2. Validation as Trust Boundary Guard
Execution order is trust-boundary-aware:
This ensures validation guards all boundary crossings, enabling zero-trust architecture and future cryptographic verification. Alternative (same order for all phases) rejected because validation must be first when receiving untrusted data.
3. Phase-Aware Priority Hints
Mutations declare
priorityHint(default: 0) which can differ by phase. Benefits:nameensures consistencyAlternative (global fixed ordering) rejected as too rigid for phase-aware operations like compression/decompression.
4. Other Design Choices
interceptor/listandinterceptor/invokemirror MCP patterns (tools/list, etc.)_metaprotocol metadataRelated Work and Inspiration
Inspired By:
Complements Existing MCP:
Backward Compatibility
This SEP is fully backward compatible:
Optional Capability: Interceptor is an optional server capability. Servers without interceptor support continue to work.
No Protocol Changes: Existing JSON-RPC methods are unchanged. Interceptor adds new methods (
interceptor/list,interceptor/invoke) but doesn't modify existing ones.Client Compatibility: Clients that don't support interceptor can still communicate with interceptor-enabled servers. Interceptor runs transparently on the server side.
Graceful Degradation: If a client requests interceptor from a server that doesn't support it, the server returns a standard JSON-RPC error, which clients already handle.
Version Negotiation: The existing protocol version negotiation during initialization allows clients and servers to discover interceptor support.
Security Implications
Threat Model
1. Malicious Interceptor
A compromised interceptor could:
Mitigations:
2. Interceptor Bypass
Attackers might try to bypass interceptor validation:
Mitigations:
3. Information Disclosure
Interceptor error messages might leak sensitive information:
Mitigations:
4. Denial of Service
Interceptor could be exploited for DoS:
Mitigations:
5. Privilege Escalation
Interceptor mutation could be used to inject unauthorized operations:
Mitigations:
Security Best Practices
Principle of Least Privilege: Interceptor should only have access to the data it needs to process
Defense in Depth: Deploy interceptor at both client and server boundaries
Audit Everything: Log all interceptor invocations, including payloads (with PII redaction)
Fail Secure: If interceptor fails, the default should be to reject the request
Input Validation: Interceptor configuration and payloads should be validated
Cryptographic Integrity: Consider signing interceptor responses to prevent tampering
Isolation: Run untrusted interceptor in sandboxed environments
Compliance Considerations
Interceptor enables compliance with:
However, organizations must ensure:
Reference Implementation
The reference implementation will provide:
Multi-Language SDKs
SDK libraries for writing and running interceptors in various languages, including Python, TypeScript/JavaScript, Go and other community-contributed languages. These SDKs will provide base classes, helpers, and patterns for building validation, mutation, and observability interceptors.
Common Interceptor Sidecar
A universal interceptor runtime that enables teams to deploy and manage interceptors without modifying individual MCP servers. The sidecar acts as an MCP proxy that loads custom interceptors (both local and remote) via configuration and injects interceptor invocations transparently.
Example Configuration:
This approach enables platform teams to deploy guardrails across all services from a central control plane without code changes to existing MCP servers.
Acknowledgments
This proposal draws inspiration from: