Skip to content

server: handle DELETE on /cors-proxy for MCP streamable-http - #25296

Closed
tlemaire wants to merge 1 commit into
ggml-org:masterfrom
tlemaire:server-cors-proxy-delete
Closed

server: handle DELETE on /cors-proxy for MCP streamable-http#25296
tlemaire wants to merge 1 commit into
ggml-org:masterfrom
tlemaire:server-cors-proxy-delete

Conversation

@tlemaire

@tlemaire tlemaire commented Jul 4, 2026

Copy link
Copy Markdown

Summary

The built-in CORS proxy (--ui-mcp-proxy) only registers GET and POST handlers for /cors-proxy. The MCP streamable-http transport terminates a session by sending a DELETE request, which has no matching route and falls through to the 403 handler. As a result the Web UI health-check fails for hosted MCP servers that follow the streamable-http spec (e.g. Exa), and the server is disabled for every conversation:

Protocol error after initialize: Failed to terminate session

This registers a DELETE route on /cors-proxy that responds 200 directly, since session teardown is pure cleanup and the upstream response is irrelevant to the client.

Background

  • tools/server/server.cpp previously registered only get/post for /cors-proxy.
  • tools/server/server-cors-proxy.h had no DELETE handler.
  • Reproduces with any hosted streamable-http MCP server that the browser reaches via the proxy (Exa's https://mcp.exa.ai/mcp is the reported case).

Changes

  • tools/server/server-cors-proxy.h: add proxy_handler_delete returning a permissive 200 with the same CORS headers as the other handlers (GET,POST,DELETE,OPTIONS).
  • tools/server/server.cpp: register ctx_http.del("/cors-proxy", proxy_handler_delete) and the matching res_403 fallback when the proxy is disabled.

Testing

  • Built llama-server with --ui-mcp-proxy, configured the Exa MCP server in the Web UI.
  • Before: health-check logged 405 from Exa, server marked unhealthy, tools invisible.
  • After: health-check completes, server stays healthy, web_search_exa tool is available and callable.

cc @ngxson @allozaur (server/ui maintainers per recent MCP proxy PRs #24500, #24970)

MCP streamable-http clients send DELETE to terminate a session. Hosted
MCP servers (e.g. Exa) reject DELETE with 405, which made the Web UI
health check throw "Protocol error after initialize: Failed to terminate
session" and disabled the server for conversations.

Register a DELETE route on /cors-proxy that returns 200 directly instead
of forwarding to the upstream, since session teardown is pure cleanup.

Signed-off-by: Thierry Lemaire <thierry.lemaire@gmail.com>
@tlemaire
tlemaire requested a review from a team as a code owner July 4, 2026 08:52
@github-actions github-actions Bot added the server label Jul 4, 2026

@ngxson ngxson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you didn't respect the PR description template

Comment on lines +85 to +97
static server_http_context::handler_t proxy_handler_delete = [](const server_http_req &) -> server_http_res_ptr {
// MCP streamable-http clients send DELETE to terminate a session. Some hosted
// upstreams (e.g. Exa) reject DELETE, which makes the UI health check fail and
// disables the server for conversations. Session teardown is pure cleanup, so
// answer 200 directly instead of forwarding.
auto res = std::make_unique<server_http_res>();
res->status = 200;
res->headers["Access-Control-Allow-Origin"] = "*";
res->headers["Access-Control-Allow-Headers"] = "*";
res->headers["Access-Control-Allow-Methods"] = "GET,POST,DELETE,OPTIONS";
res->headers["Access-Control-Expose-Headers"] = "*";
return res;
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a proxy, it's a fake response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants