github.com/evalstate
Streams, Sessions, Stats: Transport and Client Behaviour
MCP Dev Summit, London
Shaun Smith
October 2025
github.com/evalstate

Shaun Smith @evalstate

  • @ Hugging Face MCP

  • MCP Maintainer / Transports WG

  • Transports WG

  • Maintainer of fast-agent


github.com/evalstate

Hugging Face MCP Server: Huge Traffic Drop!

github.com/evalstate

Request/Response Streamable HTTP MCP Server

If all you need are Tools, Prompts, Resources (and Completions) this is enough!

Client POST's its Request to the MCP Server - which returns a JSON-RPC response.

Normal HTTP!

new StreamableHTTPServerTransport(){
    // Note: Not Default!
    enableJSONResponse: true
}
github.com/evalstate

Adding Tool Progress Notifications

Longer running Tools (like Image Generation) can send Progress Notifications.

MCP Server responds to the POST Request with an SSE Stream.

Server Streams notifications to Client, then the Response, and closes.

MCP Server developer should make sure Notifications are sent on the correct channel.

async (request, extra) => {
  await server.notification({
    method: "notifications/statusUpdate",
    params: { /* your params */ }
  }, { relatedRequestId: extra.requestId });
};

await session.send_progress_notification(
    progress_token="token-789",
    progress=50,
    related_request_id="tool-call-456"  
)
github.com/evalstate

Server Elicitation Request related to Tool Call

MCP can be bi-directional: Servers can make Sampling and Elicitation requests to the Client.

Server sends its Elicitation Request via the Tool's Response SSE stream.

Elicitation Result is POSTed to the MCP Server with it's Request-ID for association - and returns a 202.

Server then returns the Tool Result via the original Post SSE stream.

Note that the Elicitation Request should complete before the SSE Connection times out!

This applies to any associated Request / Response method, but Tool and Elicitation chosen to keep example simple.

github.com/evalstate

Server Request to Client (Server Initiated)

Server Initiated communications happen over a GET SSE Stream. This includes Resource Subscriptions,List Change Notifications or Server-Initiated Sampling/Elicitation.

github.com/evalstate

Ping!

The Host can POST a Ping to the Server to tell it's alive.
The Server can Ping the Host via the GET SSE Channel (if open).

github.com/evalstate

REFERENCE: Capabilities Matrix

POST/JSON
POST/SSE
POST/SSE
+Response
POST/SSE
GET/SSE
+Response
github.com/evalstate

Using Mcp-Session-Id

Sessions are controlled by the MCP Server, not the Host - and are embedded within the Streamable HTTP Transport.

Knowledge about the User is usually handled with OAuth Identity or API key (e.g. ZeroGPU Quota, Selected Spaces).

github.com/evalstate

Mcp-Session-Id for Conversational State?

The Mcp-Session-Id is NOT a Chat ID.

VSCode uses one Transport Session per Chat

Desirable or undesirable this behaviour should be intentional.

Server State Chat 1: "Production Issue" Chat 2: "Testing Cleanup"
DB: staging "Show me the test data"
DB: staging "I need to check production"
DB: staging "Switch to production database"
DB: production "What are the recent orders?"
DB: production "Delete those old records"
DB: production 💥 Deletes production data!
github.com/evalstate

Mcp-Session-Id for Routing

alt text

With Multiple MCP Server instances, Client to Server Responses needs to go the correct Server.

Mcp-Session-Id HTTP Header can be used for Routing to the correct Server (sticky sessions).

Sharing Mcp-Session-Id state amongst the cluster is not enough: Both Mcp-Session-Id and Elicitation RequestId are needed for correlation.

github.com/evalstate

Hugging Face MCP Server: Huge Traffic Drop!

github.com/evalstate

Tool Call : MCP Method Ratio (Hugging Face MCP Server)

MCP MethodAugSep
initialize1.0001.000
tools/list0.4001.175
notifications/initialized0.9950.982
prompts/list1.0810.685
resources/list1.0390.606
notifications/cancelled0.0630.150
Actual Tool/Prompt Calls0.0110.032
ping0.0010.027
resources/templates/list0.0000.022

Initialization sequence is usually at least 3 calls.

MCP has significant overhead. For September:

  • We see ~3 Tool/Prompt Calls per 100 Initialize events.
  • and ~165 MCP Method Calls for every Tool/Prompt Call.
  • compared to 547 in August!

The resource methods aren't supported, yet clients still request them.

JSON-RPC content stops standard HTTP Cache mechanisms.

github.com/evalstate

Initialize != Usage

Interactive Hosts (IDE/UI)

Sessions may remain open for minutes/hours.

Tool/Prompt Usage is User driven so idle sessions are normal.

VSCode has a high "efficiency ratio"

Examples are claude-ai, windsurf-client and mcp-inspector.

Gateways / Embedded Hosts

MCP Server is used as part of an automation, from a "Remote" Host or a gateway.

Burst of activity to Initialize and Call Tool that typically lasts under 5 seconds.

Examples are openai-mcp, docker-mcp-gateway, and javelin-mcp-client.

github.com/evalstate

Client Top 20 (by Usage) with Capabilities / Approx ~1.5m Sessions Sep '25

#ClientIcons#ClientIcons
1claude-ai
RSE
11docker-mcp-gateway
DeleteRSE
2lmstudio-mcp-bridge
RSE
12groq-mcp-client
RSE
3Visual Studio Code
RSE
13openai-mcp
DeleteRSE
4test-client
AlertRSE
14Cherry Studio
RSE
5cursor-vscode
AlertRSE
15fast-agent-mcp
DeleteRSE
6claude-code
RSE
16codex (via mcp-remote)
RSE
7mcp (via mcp-remote)
RSE
17claude-ai (via mcp-remote)
RSE
8Anthropic/ClaudeAI
RSE
18@n8n/langchain.mcpClientT
AlertRSE
9mcp
DeleteRSE
19lobehub-mcp-client
RSE
10Manus
DeleteRSE
20dev.warp.Warp-Stable
RSE
R Roots
S Sampling
E Elicitation
Delete Session Deletion
Alert Invalid Capabilities
github.com/evalstate

MCP Client Primitives

Icon Feature Usage
Roots Roots 7.1% of all sessions, 33.6% of sessions that use tools
Not currently useful for Remote Servers
Sampling Sampling 0.9% of all sessions, 22.2% of sessions that use tools
Elicitations Elicitations 3.2% of all sessions, 21.6% of sessions that use tools
Session Deletion Session Deletion 4 of the top 20 clients delete sessions, only 6.64% of sessions get deleted overall
github.com/evalstate

Thoughts and Guidance

  • SDK DevEx differs between Transports and Capability usage - consider deployment options carefully and be intentional. Don't forget to Use extra/related-request-id and configure JSON-RPC mode. fast-agent can help with diagnosis and debugging.

  • Consider whether Server -> Client features are necessary for your use-case - especially in an uncontrolled environment. If they are, harder!

  • Don't rely on Clients managing sessions - for now consider what you want to use Mcp-Session-Id for.

github.com/evalstate

Some pain points

  • GET Handler: For Interactive Hosts it's expensive (especially for speculative Tool Change Notifications). For Transactional Hosts it's unnecessary. Can be used for SSE Failure resumption - hard in practice. Management in elastic environments tricky.

  • MCP Protocol Overhead - JSON-RPC Packaging requires inspection for Processing and Return - prohibiting standard HTTP Caching. Protocol is "Chatty" in practice.

  • Sessions are coupled to the Transport implementation and State. Deploying in a Cluster means Sticky Sessions. Server Initiated Requests must be identified by both Mcp-Session-Id + JSON-RPC-RequestId

  • Timeout Handling - Progress Notifications resetting HTTP timeouts for example is not consistent Client behaviour.

github.com/evalstate

Transport WG / Relevant SEPs

  • Handle inconsistencies between transports

  • Separate JSON-RPC layer from Protocol Data Layer.

  • SEP #1442 - Make MCP Stateless by Default: Move State captured in Initialize to Request/Response cycle.

  • Pure HTTP Transport - https://github.com/mikekistler/pure-http-transport

github.com/evalstate

Thanks to the Transport Working Group

  1. Everything Server PR 1: https://github.com/modelcontextprotocol/servers/pull/2789
  2. Everything Server PR 2: https://github.com/modelcontextprotocol/servers/pull/2672
  3. Hugging Face MCP Server: https://huggingface.co/mcp
  4. MCP community Working Groups https://modelcontextprotocol-community.github.io/working-groups/
github.com/evalstate

educational, bit of data, what next

Stateless JSON-RPC. All we can do is respond. Fine! If you don't need state. MCP SDK can still do some of the lifting for you.

when i was here last time, i said the great thing about MCP was it just worked

we have lots of options for Streamable HTTP and not much time, so we'll build up

<div class="bottom-image"> <img src="./images/capabilities_stateless_1.png" alt="MCP Capabilities - Stateless" /> </div>

> <div class="bottom-image"> <img src="./images/capabilities_stateless_1.png" alt="MCP Capabilities - Stateless" /> </div>

![w:300](./images/session_per_chat.png)

Usage Guide: - Session deletion icon: <span class="icon-delete"><img src="./images/trash-2.svg" alt="Delete" /></span> - Alert/warning icon: <span class="icon-alert"><img src="./images/circle-alert.svg" alt="Alert" /></span> - Enabled capability: <span class="capability-icon"><img src="./images/[icon].svg" /></span> - Disabled capability: <span class="capability-icon disabled"><img src="./images/[icon].svg" /></span> - folders.svg = Roots - cpu.svg = Sampling - message-circle-question-mark.svg = Elicitation - Icons are placed in the <div class="client-icons"> container - Enabled capabilities: bold blue color with light blue background - Disabled capabilities: subtle gray with minimal background - Delete/Alert icons: red color with light red background