The paradox
HTTP is fundamentally stateless: each request is independent, the server doesn’t remember previous requests, and there’s no inherent connection concept. Yet the MCP (Model Context Protocol) requires session state for tools to maintain context across multiple invocations. The question: How do you build a stateful protocol on top of a stateless transport? FastMCP’s answer: Session headers + server-side session store.How it works
Initialization (handshake)
Subsequent requests
Why this design exists
Advantage: Stateless scaling
Each request can hit any FastMCP server instance (load balancer friendly):Advantage: Natural MCP semantics
MCP expects tools to share context. Without sessions:Disadvantage: Client complexity
The client must:- Capture the session ID from the first response
- Store it somewhere accessible
- Include it in every subsequent request
- Handle session expiry gracefully
Disadvantage: Difficult to debug
Session headers are invisible in most HTTP clients:curlwon’t show them unless you add-v- Browser dev tools often hide them
- Logs that don’t include headers look like “random HTTP 401 errors”
Implementation pattern (PEBBLE’s approach)
Real-world consequences
Session expiry
FastMCP sessions typically expire after 15-30 minutes of inactivity. If a tool takes 10 minutes to run:Concurrent requests
If a client runs multiple tools in parallel:Debugging session failures
Error message:HTTP 401 Unauthorized
Reality: Could be any of:
- Session ID not included in headers
- Session ID expired
- Session ID is wrong (captured from different server instance)
- Server crashed and lost session store
How PEBBLE measures it
From benchmark results (Apr 15, 2026):| Phase | Time | Notes |
|---|---|---|
| Initialize (session + handshake) | 195ms | One-time cost |
| Session reuse (tool call) | 19-29ms | Per tool, network-bound |
| Session reuse (subsequent tools) | 20-26ms | Consistent, no degradation |
Organizational takeaway
Session-based HTTP state is a pragmatic compromise between MCP’s stateful semantics and HTTP’s stateless nature.When designing HTTP-based stateful protocols:
- Minimize session size — avoid storing large objects in session (send them with each request instead)
- Set explicit TTLs — don’t let sessions linger forever
- Log session IDs with every request — essential for debugging
- Consider alternative transport — if sessions become complex, use WebSocket or gRPC (both have built-in connection state)
- Document the session requirement — this isn’t obvious from HTTP alone
For PEBBLE specifically:
- Initialize once per provider lifecycle (usually once at startup)
- Reuse the session for all tool calls
- Re-initialize if tools start failing with 401 errors
- Monitor session initialization time as a health metric