ADR-017: Elicitation Passthrough Implementation
- Status: Accepted
- Date: 2025-10-26
- Deciders: Mihai Criveti
- Technical Story: spec/status.md item #27
Context
The Model Context Protocol (MCP) specification version 2025-06-18 introduced elicitation as a new feature for interactive user input workflows. Elicitation allows MCP servers to request structured information from users through the client during tool execution or other operations.
MCP Elicitation Overview
Flow Pattern: Server → Client (reverse request pattern)
sequenceDiagram
participant Server as MCP Server
participant Gateway as ContextForge
participant Client as MCP Client (elicitation-capable)
participant User
Server->>Gateway: elicitation/create request
Gateway->>Client: Forward elicitation/create
Client->>User: Display UI (form/dialog)
User->>Client: Provide input (accept/decline/cancel)
Client->>Gateway: Return ElicitResult
Gateway->>Server: Forward result
Server->>Server: Continue processing with user input
Key Characteristics:
- Newly introduced: First appeared in MCP 2025-06-18, design may evolve
- Optional capability: Clients must advertise
{"elicitation": {}}during initialization - Nested execution: Elicitation can occur inside tool/resource/prompt operations
- Three-action model: Users can
accept(with data),decline, orcancel - Structured schemas: Requests include JSON Schema (restricted to primitive types)
Gateway Architectural Challenge
ContextForge operates as both:
- Server to downstream clients (Claude Desktop, API consumers)
- Client to upstream servers (MCP servers, federated gateways)
This dual role creates complexity for elicitation:
- Upstream servers initiate elicitation requests
- Gateway must forward to appropriate downstream clients
- Responses must be routed back to the original requester
- Session state must track pending elicitations
Current State
File: mcpgateway/main.py:3622-3623
elif method.startswith("elicitation/"):
result = {} # Stub implementation
Problem: Returns empty dict instead of proper handling, breaking MCP spec compliance.
Decision
Implement full passthrough elicitation support with the following architecture:
1. Bidirectional Proxy Pattern
# Server-to-Client flow (elicitation/create)
Upstream Server → Gateway → Downstream Client → User
User → Client → Gateway → Server
# Request tracking
ElicitationService tracks:
- request_id → (upstream_session, downstream_session, timeout)
- Maps responses back to original requestor
2. Component Architecture
New Service: mcpgateway/services/elicitation_service.py
- Track active elicitation requests
- Map request IDs between upstream/downstream sessions
- Handle timeouts and cleanup
- Validate schemas per MCP spec
Updated Components:
mcpgateway/cache/session_registry.py- Track client elicitation capabilitymcpgateway/models.py- Add Pydantic models for elicitation typesmcpgateway/main.py- Implementelicitation/createhandler
3. Configuration Strategy
# .env.example / config.py
MCPGATEWAY_ELICITATION_ENABLED=true # Master switch
MCPGATEWAY_ELICITATION_TIMEOUT=60 # Default timeout (seconds)
MCPGATEWAY_ELICITATION_MAX_CONCURRENT=100 # Max concurrent requests
4. Error Handling
# Scenarios handled:
1. Client doesn't support elicitation → Error -32601 (Capability not available)
2. No active downstream clients → Error -32000 (No capable clients connected)
3. Timeout waiting for response → Error -32000 (Elicitation timeout)
4. Invalid schema → Error -32602 (Invalid params)
5. Gateway elicitation disabled → Error -32601 (Feature disabled)
5. Security Considerations
Per MCP spec security requirements:
- No sensitive data: Validate schemas don't request passwords, API keys, etc.
- Rate limiting: Enforce max concurrent elicitations per session
- Timeout enforcement: Prevent indefinite blocking
- Audit logging: Log all elicitation requests and responses (sanitized)
Implementation Plan
Phase 1: Foundation (Priority: High)
Add Pydantic Models (
mcpgateway/models.py)class ElicitationCapability(BaseModelWithConfigDict) class ElicitRequestParams(BaseModelWithConfigDict) class ElicitResult(BaseModelWithConfigDict)Create ElicitationService (
mcpgateway/services/elicitation_service.py)- Request tracking data structure
- Timeout management
- Response routing logic
- Schema validation (primitive types only)
Update SessionRegistry (
mcpgateway/cache/session_registry.py)- Track client
elicitationcapability from initialization - Store capability per session
- Provide lookup for capable clients
- Track client
Phase 2: Request Handling (Priority: High)
Implement Handler (
mcpgateway/main.py)elif method == "elicitation/create": # Validate elicitation enabled # Validate params (message, requestedSchema) # Find capable downstream client # Forward request via ElicitationService # Await response with timeout # Return ElicitResultAdd Configuration (
.env.example,config.py)- Feature flags
- Timeout settings
- Concurrency limits
Phase 3: Testing & Documentation (Priority: Medium)
Unit Tests
- ElicitationService request tracking
- Schema validation (primitive types only)
- Timeout handling
- Error scenarios
Integration Tests
- End-to-end elicitation flow
- Multiple concurrent requests
- Client capability negotiation
- Response routing
Update Documentation
spec/status.md- Mark item #27 as completedREADME.md- Document elicitation configuration- API docs - Document elicitation endpoints
Estimated Implementation
- Lines of Code: ~300-400 (service + models + tests)
- Files Modified: 6-8 files
- Time Estimate: 4-6 hours implementation + 2-3 hours testing
Alternatives Considered
Alternative 1: Stub Implementation (Return Error)
Decision: ❌ Rejected
elif method == "elicitation/create":
raise JSONRPCError(-32601, "Elicitation not implemented")
Rationale:
- ✅ Pro: Simplest implementation (5 lines of code)
- ✅ Pro: Honest about lack of support
- ❌ Con: Breaks MCP spec compliance (feature is in 2025-06-18 spec)
- ❌ Con: Limits gateway usability with elicitation-enabled servers
- ❌ Con: Future implementation requires complete rewrite
Alternative 2: Gateway-Initiated Elicitation Only
Decision: ❌ Rejected
Implement elicitation for gateway's own use (e.g., configuration wizards) but not passthrough.
Rationale:
- ✅ Pro: Simpler than passthrough (no session tracking)
- ✅ Pro: Useful for gateway admin UI workflows
- ❌ Con: Doesn't solve spec compliance for upstream servers
- ❌ Con: Limited real-world use cases for gateway-initiated elicitation
- ❌ Con: Still requires full implementation later for spec compliance
Alternative 3: Async Queue-Based Architecture
Decision: ❌ Rejected
Use message queue (Redis, RabbitMQ) for elicitation request routing.
Rationale:
- ✅ Pro: Better scalability for high-volume scenarios
- ✅ Pro: Natural timeout/retry handling
- ❌ Con: Adds external dependency complexity
- ❌ Con: Overkill for typical elicitation volumes (low frequency, human-in-loop)
- ❌ Con: More difficult to debug and troubleshoot
- ❌ Con: Increases deployment complexity
Consequences
Positive ✅
- MCP 2025-06-18 Compliance: Gateway fully supports latest spec
- Interactive Workflows: Enables rich user interaction patterns from upstream servers
- Future-Proof: Ready for elicitation adoption as feature matures
- Federated Support: Multi-tier gateway deployments can pass elicitations through
- Configuration Flexibility: Can disable if not needed, minimal overhead when disabled
- Security First: Validates schemas, enforces timeouts, prevents abuse
Negative ❌
- Session Complexity: Adds request/response tracking across sessions
- Memory Overhead: Must track pending elicitations (mitigated by timeout/limits)
- Testing Complexity: Requires end-to-end test infrastructure
- Error Handling: Multiple failure modes require careful handling
- Feature Maturity: MCP spec notes "design may evolve" - risk of breaking changes
Neutral 🔄
- Adoption Uncertainty: Unknown how many servers will use elicitation
- Performance Impact: Minimal (elicitations are human-speed, not hot path)
- Maintenance: New service requires ongoing maintenance as spec evolves
Risks and Mitigations
Risk 1: Spec Evolution
Risk: MCP spec notes elicitation design "may evolve in future versions"
Mitigation:
- ✅ Implement behind feature flag for easy disabling
- ✅ Comprehensive unit tests allow rapid updates
- ✅ Schema validation centralizes spec-dependent logic
- ✅ Monitor MCP spec changes and update promptly
Risk 2: Session Tracking Bugs
Risk: Request/response routing errors could cause hangs or wrong responses
Mitigation:
- ✅ Aggressive timeouts (60s default, configurable)
- ✅ Comprehensive error handling and logging
- ✅ Request ID validation prevents mis-routing
- ✅ Automatic cleanup of expired requests
Risk 3: Client Capability Detection
Risk: Incorrectly routing to non-capable clients
Mitigation:
- ✅ Validate client capabilities during initialization
- ✅ Store capability per session
- ✅ Return clear error if no capable clients available
- ✅ Log capability negotiation for debugging
Success Metrics
Functional:
- ✅ All elicitation spec requirements implemented
- ✅ 100% test coverage for ElicitationService
- ✅ Integration tests pass for all scenarios
Performance:
- ✅ Elicitation overhead <10ms (excluding human response time)
- ✅ No memory leaks from pending requests
- ✅ Graceful handling of 100+ concurrent elicitations
Operations:
- ✅ Clear error messages for all failure modes
- ✅ Comprehensive logging for debugging
- ✅ Configuration validation on startup
- ✅ Metrics exposed for monitoring
References
- MCP Specification:
spec/modelcontextprotocol/docs/specification/2025-06-18/client/elicitation.mdx - FastMCP Implementation:
.venv/lib/python3.12/site-packages/mcp/server/elicitation.py - Status Tracking:
spec/status.mditem #27 - MCP Types Reference:
.venv/lib/python3.12/site-packages/mcp/types.pylines 1277-1311
Decision Approved By: Mihai Criveti Implementation Tracked In: This ADR becomes the implementation specification for elicitation support.