@cryptotaxi247 / netdata-1 / commits / 92a40a16b

MCP Streamable HTTP: add stateless Mcp-Session-Id support (#22000)

Co-authored-by: Costa Tsaousis <costa@netdata.cloud>

Copilot committed Apr 8, 2026 at 09:18 UTC 92a40a16bb395c1e11d34888503958beb910748a
5 files changed +22 -1
src/web/api/http_header.c
+9
@@ -217,6 +217,12 @@ static void http_header_x_netdata_auth(struct web_client *w, const char *v, size
217 }
218 }
219
220 +// MCP HTTP transport session identifier
221 +static void http_header_mcp_session_id(struct web_client *w, const char *v, size_t len __maybe_unused) {
222 + if (uuid_parse(v, w->mcp_session_id) != 0)
223 + uuid_clear(w->mcp_session_id);
224 +}
225 +
226 // Handle WebSocket-specific headers
227 static void http_header_upgrade(struct web_client *w, const char *v, size_t len __maybe_unused) {
228 if(strcasecmp(v, "websocket") == 0) {
@@ -365,6 +371,9 @@ struct {
371 // there are a few nightly versions of netdata UI that incorrectly use this instead of X-Netdata-Auth
372 { .hash = 0, .key = "Authorization", .cb = http_header_x_netdata_auth },
373
374 + // MCP HTTP transport session identifier
375 + { .hash = 0, .key = "Mcp-Session-Id", .cb = http_header_mcp_session_id },
376 +
377 // terminator
378 { .hash = 0, .key = NULL, .cb = NULL }
379 };
src/web/mcp/adapters/mcp-http.c
+8
@@ -208,6 +208,14 @@ int mcp_http_handle_request(struct rrdhost *host __maybe_unused, struct web_clie
208 result_code = w->response.code;
209 }
210
211 + // Stateless Mcp-Session-Id: generate if absent, then emit as response header
212 + if (uuid_is_null(w->mcp_session_id))
213 + uuid_generate_random(w->mcp_session_id);
214 +
215 + char session_id_str[UUID_STR_LEN];
216 + uuid_unparse_lower(w->mcp_session_id, session_id_str);
217 + buffer_sprintf(w->response.header, "Mcp-Session-Id: %s\r\n", session_id_str);
218 +
219 json_object_put(root);
220 mcp_free_client(mcpc);
221 return result_code;
src/web/server/web_client.c
+3 -1
@@ -164,7 +164,9 @@ static void web_client_reset_allocations(struct web_client *w, bool free_all) {
164
165 freez(w->auth_bearer_token);
166 w->auth_bearer_token = NULL;
167 -
167 +
168 + memset(w->mcp_session_id, 0, sizeof(w->mcp_session_id));
169 +
170 // Free WebSocket resources
171 freez(w->websocket.key);
172 w->websocket.key = NULL;
src/web/server/web_client.h
+1
@@ -195,6 +195,7 @@ struct web_client {
195 char *forwarded_host; // the X-Forwarded-Host: header
196 char *origin; // the Origin: header
197 char *user_agent; // the User-Agent: header
198 + nd_uuid_t mcp_session_id; // the Mcp-Session-Id: header (MCP HTTP transport)
199
200 // WebSocket related data - NEED TO BE FREED
201 struct {
src/web/server/web_client_cache.c
+1
@@ -121,6 +121,7 @@ struct web_client *web_client_get_from_cache(void) {
121 w->mode = HTTP_REQUEST_MODE_GET;
122 web_client_reset_permissions(w);
123 memset(w->transaction, 0, sizeof(w->transaction));
124 + memset(w->mcp_session_id, 0, sizeof(w->mcp_session_id));
125 memset(&w->auth, 0, sizeof(w->auth));
126
127 return w;