-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Summary
The middleware system (Middleware, AdvancedMiddleware, MiddlewareChain, EnhancedMiddlewareChain) is comprehensive and powerful, but there's no standard integration path to attach middleware to Client, Protocol, or Transport. Users must manually invoke middleware chains, which is error-prone and defeats the purpose of having middleware infrastructure.
Current State
What exists:
- ✅ Comprehensive middleware traits (
Middleware,AdvancedMiddleware) - ✅ Chain implementations (
MiddlewareChain,EnhancedMiddlewareChain) - ✅ Built-in middleware (Logging, Auth, Retry, RateLimit, CircuitBreaker, Metrics, Compression)
- ✅ Priority ordering, context propagation, conditional execution
- ✅ Excellent documentation in
pmcp-book/src/ch11-middleware.md
What's missing:
- ❌ No
ClientBuilder::with_middleware_chain(...)or similar API - ❌ No automatic middleware invocation on requests/responses
- ❌ No integration examples showing middleware running on real client operations
- ❌ Users must manually call
chain.process_request()/chain.process_response()
Current workaround (manual integration):
// Manual middleware invocation - cumbersome and easy to forget
let mut client = Client::new(transport);
let mut middleware_chain = MiddlewareChain::new();
middleware_chain.add(Arc::new(LoggingMiddleware::default()));
// Must manually intercept before/after every operation
let mut request = create_request("tools/list", None);
middleware_chain.process_request(&mut request).await?;
let response = client.call_method(request).await?;
middleware_chain.process_response(&mut response).await?;Proposed Solution
Add first-class middleware integration at one or more levels:
Option 1: Client-level Integration (Recommended)
use pmcp::{Client, ClientCapabilities, MiddlewareChain, LoggingMiddleware};
use std::sync::Arc;
use tracing::Level;
let mut chain = MiddlewareChain::new();
chain.add(Arc::new(LoggingMiddleware::new(Level::INFO)));
let client = Client::builder()
.capabilities(ClientCapabilities::default())
.with_middleware_chain(chain) // ← NEW API
.streamable_http_transport("http://localhost:8080")
.await?
.build()
.await?;
// Middleware runs automatically on all operations
let tools = client.list_tools(None).await?; // Logging middleware runs transparentlyOption 2: Transport-level Integration
use pmcp::{StdioTransport, LoggingMiddleware};
let transport = StdioTransport::new()
.with_middleware(Arc::new(LoggingMiddleware::default())); // ← NEW API
let client = Client::new(transport);
// Middleware intercepts all transport send/receive operationsOption 3: Protocol-level Integration
use pmcp::shared::{Protocol, ProtocolOptions};
let protocol = Protocol::new(ProtocolOptions {
middleware: Some(chain), // ← NEW FIELD
..Default::default()
});Implementation Approach
Client-level (Recommended Starting Point)
1. Update Client struct:
pub struct Client {
transport: Box<dyn Transport>,
middleware: Option<Arc<MiddlewareChain>>, // NEW
// ... existing fields
}2. Add builder method:
impl ClientBuilder {
pub fn with_middleware_chain(mut self, chain: MiddlewareChain) -> Self {
self.middleware = Some(Arc::new(chain));
self
}
}3. Intercept in request/response methods:
impl Client {
pub async fn call_method(&mut self, mut request: JSONRPCRequest) -> Result<JSONRPCResponse> {
// Apply middleware to request
if let Some(ref middleware) = self.middleware {
middleware.process_request(&mut request).await?;
}
// Send request
let response = /* ... existing logic ... */;
// Apply middleware to response
if let Some(ref middleware) = self.middleware {
middleware.process_response(&mut response).await?;
}
Ok(response)
}
}Enhanced Middleware Support
For EnhancedMiddlewareChain with context:
pub fn with_enhanced_middleware_chain(mut self, chain: EnhancedMiddlewareChain) -> Self {
self.enhanced_middleware = Some(Arc::new(chain));
self
}
// In request handling:
let context = MiddlewareContext::with_request_id(generate_request_id());
middleware.process_request_with_context(&mut request, &context).await?;Comparison with TypeScript SDK
TypeScript SDK (~/Development/mcp/sdk/typescript-sdk):
- HTTP-only:
fetchwrapper middleware - Limited scope: Only HTTP transport layer
Rust SDK (current + proposed):
- ✅ Protocol-first: Hooks at JSON-RPC level AND raw transport messages
- ✅ Transport-agnostic: Works with stdio, WebSocket, HTTP, Streamable HTTP
- ✅ Advanced patterns: Rate limiting, circuit breakers, metrics, compression
- ✅ Priority ordering and conditional execution
⚠️ Missing: First-class integration (this issue)
With this enhancement, Rust SDK would be superior in every aspect.
Benefits
- Zero-boilerplate usage: Set up once, runs automatically
- Reduced error surface: Can't forget to apply middleware
- Clear integration point: Obvious where/how to attach middleware
- Matches user expectations: Standard pattern from other middleware systems
- Makes powerful middleware system usable: Current system is powerful but disconnected
Example Use Case
Production deployment with observability:
use pmcp::shared::{
EnhancedMiddlewareChain,
MetricsMiddleware,
LoggingMiddleware,
RateLimitMiddleware,
CircuitBreakerMiddleware,
};
let mut chain = EnhancedMiddlewareChain::new();
chain.add(Arc::new(MetricsMiddleware::new("my-service".to_string())));
chain.add(Arc::new(LoggingMiddleware::new(Level::INFO)));
chain.add(Arc::new(RateLimitMiddleware::new(100, 200, Duration::from_secs(1))));
chain.add(Arc::new(CircuitBreakerMiddleware::new(5, Duration::from_secs(60), Duration::from_secs(30))));
// One-line integration
let client = Client::builder()
.with_enhanced_middleware_chain(chain)
.streamable_http_transport("http://localhost:8080")
.await?
.build()
.await?;
// All operations now have:
// - Automatic metrics collection
// - Request/response logging
// - Rate limiting protection
// - Circuit breaker fault tolerance
let tools = client.list_tools(None).await?;Proposed Implementation Plan
-
Phase 1: Client-level integration (highest impact, easiest to implement)
- Add
with_middleware_chain()toClientBuilder - Add
with_enhanced_middleware_chain()toClientBuilder - Intercept in
Client::call_method()and similar methods - Update
examples/15_middleware.rswith real integration
- Add
-
Phase 2: Transport-level integration (for advanced use cases)
- Add
with_middleware()to transport implementations - Intercept
Transport::send()/Transport::receive()
- Add
-
Phase 3: Protocol-level integration (if needed)
- Add
middlewarefield toProtocolOptions - Intercept in
Protocolrequest handling
- Add
-
Documentation updates:
- Update
pmcp-book/src/ch11-middleware.mdwith integration examples - Add "Real-World Integration" section
- Update
examples/15_middleware.rsandexamples/30_enhanced_middleware.rs
- Update
Files to Modify
src/client/mod.rsorsrc/client/builder.rs- Add builder methodssrc/client/client.rs- Add middleware interceptionsrc/shared/protocol.rs- Optional: Protocol-level integrationsrc/shared/transport.rs- Optional: Transport trait extensionexamples/15_middleware.rs- Update with real integrationexamples/30_enhanced_middleware.rs- Update with real integrationpmcp-book/src/ch11-middleware.md- Add integration documentation
Breaking Changes
None - This is purely additive. All existing code continues to work, and middleware remains optional.
References
- Current middleware implementation:
src/shared/middleware.rs - Middleware documentation:
pmcp-book/src/ch11-middleware.md - Basic middleware example:
examples/15_middleware.rs - Enhanced middleware example:
examples/30_enhanced_middleware.rs - TypeScript SDK comparison:
~/Development/mcp/sdk/typescript-sdk
Priority: High
Complexity: Medium
Impact: High - Makes middleware system actually usable in production
This enhancement would complete the middleware story and make PMCP's middleware system best-in-class across all MCP SDKs.