MCP Streamable HTTP: add stateless Mcp-Session-Id support#22000
MCP Streamable HTTP: add stateless Mcp-Session-Id support#22000
Conversation
There was a problem hiding this comment.
1 issue found across 4 files
Confidence score: 3/5
- There is a concrete regression risk in
src/web/mcp/adapters/mcp-http.c: batch JSON requests (json_type_array) appear to skip session handling becausemethodis not extracted for arrays, despite the comment indicating otherwise. - Given the 6/10 severity with high confidence (9/10), this is likely user-impacting for clients that send batch requests, even if non-batch flows remain unaffected.
- Pay close attention to
src/web/mcp/adapters/mcp-http.c- batch-request parsing needs to enforce the same session logic as object-based requests.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/web/mcp/adapters/mcp-http.c">
<violation number="1" location="src/web/mcp/adapters/mcp-http.c:310">
P2: Batch requests (`json_type_array`) bypass all session logic because `method` is never extracted from the array. The comment claims the first array element is inspected, but the code only handles `json_type_object`. Add an `else if (json_object_is_type(root, json_type_array))` branch to extract the method from the first item.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Client as MCP Client (Codex)
participant Web as Web Server (web_client)
participant Adapter as MCP HTTP Adapter
participant Store as Session Store (Dictionary)
participant Core as MCP Core Logic
Note over Client,Store: Initial Handshake (First Request)
Client->>Web: POST /mcp (method: "initialize")
Web->>Web: Parse standard HTTP headers
Web->>Adapter: mcp_http_handle_request()
Adapter->>Core: mcp_create_client() (transient)
Core-->>Adapter: mcpc object
Adapter->>Adapter: Process "initialize" logic
Adapter->>Store: NEW: mcp_http_session_create(mcpc)
Store->>Store: Generate UUID & Store state (TTL 1h)
Store-->>Adapter: session_id
Adapter->>Web: NEW: Add "Mcp-Session-Id" to response headers
Web-->>Client: 200 OK + Mcp-Session-Id
Note over Client,Store: Subsequent Request (e.g. notifications/initialized)
Client->>Web: POST /mcp (method: "...", Mcp-Session-Id: UUID)
Web->>Web: NEW: http_header_mcp_session_id() (Parse header)
Web->>Adapter: mcp_http_handle_request()
alt NEW: Session lookup
Adapter->>Store: mcp_http_session_exists(UUID)
Store-->>Adapter: false (Expired/Invalid)
Adapter-->>Client: 404 Not Found (Per Spec)
else Session Valid
Store-->>Adapter: true
Adapter->>Store: NEW: mcp_http_session_restore(UUID, mcpc)
Store-->>Adapter: Restore protocol_version, ready, client_info
Adapter->>Core: Process request (e.g. set mcpc->ready = true)
Adapter->>Store: NEW: mcp_http_session_update(UUID, mcpc)
Note right of Store: Persist state changes (e.g. ready flag)
Adapter-->>Client: 200 OK / 202 Accepted
end
opt Background Cleanup
Store->>Store: NEW: mcp_http_sessions_cleanup_expired_nolock()
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
@cubic-dev-ai review this PR |
@ilyam8 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 4 files
Confidence score: 3/5
- There is a concrete session-management risk in
src/web/mcp/adapters/mcp-http.c: validation checksMcp-Session-Idexistence but not TTL expiry, so expired sessions may remain usable. - Given the high severity/confidence (7/10, 9/10) and direct user-facing auth impact, this is more than a minor issue and adds meaningful regression/security risk before merge.
- Pay close attention to
src/web/mcp/adapters/mcp-http.c- ensure session validation enforces TTL expiration so stale session IDs are rejected.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/web/mcp/adapters/mcp-http.c">
<violation number="1" location="src/web/mcp/adapters/mcp-http.c:171">
P1: Session validation ignores TTL, so expired `Mcp-Session-Id` values can continue to work until another session is created.</violation>
</file>
Architecture diagram
sequenceDiagram
participant C as Client (Codex)
participant W as Web Server (Headers)
participant A as MCP Adapter (mcp-http.c)
participant S as Session Store (Dictionary)
participant L as MCP Logic (Core)
Note over C, L: Session Creation (Handshake)
C->>W: POST /mcp (method: initialize)
W->>A: mcp_http_handle_request()
A->>L: mcp_create_client()
L-->>A: mcpc instance (default state)
A->>L: Process initialize params
A->>S: NEW: mcp_http_session_create(mcpc)
S->>S: Generate UUID & store state (TTL 1h)
S-->>A: session_id
A-->>C: 200 OK + NEW: Mcp-Session-Id header
Note over C, L: Subsequent Request (State Resumption)
C->>W: POST /mcp (Mcp-Session-Id: uuid, method: ...)
W->>W: CHANGED: Parse Mcp-Session-Id into web_client
W->>A: mcp_http_handle_request()
alt Session exists
A->>S: NEW: mcp_http_session_restore(uuid, mcpc)
S-->>A: success (state loaded into mcpc)
A->>L: Execute method (e.g. notifications/initialized)
L->>L: Update mcpc (e.g. ready = true)
A->>S: NEW: mcp_http_session_update(uuid, mcpc)
A-->>C: 200/202 OK + Echo Mcp-Session-Id
else Session missing or expired
A->>S: NEW: mcp_http_session_exists(uuid)
S-->>A: false
A-->>C: NEW: 404 Not Found (per spec)
end
Note over C, L: Session Termination
C->>W: NEW: DELETE /mcp (Mcp-Session-Id: uuid)
W->>A: mcp_http_handle_request()
A->>S: NEW: mcp_http_session_delete(uuid)
alt Found
S-->>A: true
A-->>C: 200 OK
else Not Found
S-->>A: false
A-->>C: 404 Not Found
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
@copilot-swe-agent We reviewed this PR in depth and have a fundamental question about the design. The session store persists 5 fields (
Additionally, auth is re-derived from the The MCP Streamable HTTP spec only requires:
So: why does Netdata need to keep state for something it does not need? If Codex needs an Can you explain what breaks if we don't persist any server-side state? What specific client behavior requires the server to remember these fields? |
You are correct on every point — I traced all five fields and confirmed none require server-side persistence:
Fixed in d559008. The session store now tracks only UUIDs with a
|
d559008 to
acf3635
Compare
|
@ilyam8 I rewrote this PR from scratch. Here's the reasoning: Why stateless session IDsThe original implementation (by Copilot) introduced a server-side session store: a dictionary with spinlock, TTL expiry, cleanup sweeps, and session validation on every request. I went through several iterations before realizing the fundamental problem: the session store itself is unnecessary and creates attack surface. The MCP spec (2025-03-26) says:
A server-side session store would let an attacker exhaust server memory by opening sessions, with no benefit — we don't use the session state for anything. What this PR does instead
No session store, no validation, no TTL, no tracking, no spinlock. The This fixes the Codex startup failure (#21999) while keeping the implementation minimal and safe. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds stateless Mcp-Session-Id support for MCP Streamable HTTP by storing a per-request UUID on web_client, parsing incoming session IDs, and emitting the header on responses (including generating one on successful initialize).
Changes:
- Add
mcp_session_idfield toweb_clientand clear it on request/client reset. - Parse incoming
Mcp-Session-Idheader intoweb_client. - Generate and emit
Mcp-Session-Idon successfulinitialize, and emit on subsequent responses.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/web/server/web_client_cache.c | Clears mcp_session_id when reusing web_client from cache. |
| src/web/server/web_client.h | Adds nd_uuid_t mcp_session_id to persist session ID per request. |
| src/web/server/web_client.c | Clears mcp_session_id during allocation reset. |
| src/web/mcp/adapters/mcp-http.c | Generates UUID on initialize success and emits Mcp-Session-Id header; changes batch response allocation. |
| src/web/api/http_header.c | Parses Mcp-Session-Id request header into web_client. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Parse the incoming Mcp-Session-Id request header into web_client as nd_uuid_t. If no session ID was received (first request / initialize), generate a random UUID. Emit Mcp-Session-Id as a response header on every MCP HTTP response. No server-side session state is kept — every request is self-contained.
cade1cb to
b267acb
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Add stateless
Mcp-Session-Idsupport for the MCP Streamable HTTP transport (spec 2025-03-26).Fixes #21999 — Codex fails during MCP initialization because Netdata did not return
Mcp-Session-Idin theinitializeresponse.Approach
The session ID is a stateless opaque token — no server-side session state is kept.
Mcp-Session-Idrequest header intoweb_client.mcp_session_id(asnd_uuid_t— 16 bytes, no heap)Mcp-Session-Idresponse header on every MCP HTTP responseChanges
src/web/server/web_client.hnd_uuid_t mcp_session_idfieldsrc/web/server/web_client.csrc/web/server/web_client_cache.csrc/web/api/http_header.cMcp-Session-Idheader (with validation)src/web/mcp/adapters/mcp-http.c