Skip to content

fix: register GET/DELETE /mcp for Streamable HTTP (Inspector, Rider, and other clients)#79

Open
jekakmail wants to merge 1 commit into
microsoft:mainfrom
jekakmail:fix/streamable-http-get-mcp-405
Open

fix: register GET/DELETE /mcp for Streamable HTTP (Inspector, Rider, and other clients)#79
jekakmail wants to merge 1 commit into
microsoft:mainfrom
jekakmail:fix/streamable-http-get-mcp-405

Conversation

@jekakmail
Copy link
Copy Markdown

Problem

DebugMCP exposes a Streamable HTTP endpoint at POST /mcp (default port 3001).
Several MCP clients perform an initial GET /mcp during connection to check
whether the server offers a Server-Sent Events (SSE) stream for server-to-client
messages.

According to the MCP Streamable HTTP transport spec,
the endpoint must support both POST and GET. If the server is stateless and does
not provide SSE, it must respond to GET with HTTP 405 Method Not Allowed
(not 404).

In the current implementation, app.get('/mcp') and app.delete('/mcp') are
registered only inside the catch block of the POST /mcp handler. Until a POST
request fails, no GET route exists, so clients receive HTTP 404.

Affected clients (confirmed in practice)

Client Symptom
MCP Inspector (@modelcontextprotocol/inspector, Streamable HTTP) Connect failed (HTTP): Expected status code 200 but was 404 on GET http://localhost:3001/mcp
JetBrains Rider (built-in MCP / mcp-rider-tools, Streamable HTTP to external servers) Same connect failure when adding DebugMCP as an HTTP MCP server at http://localhost:3001/mcp
Other Streamable HTTP MCP clients (e.g. Cursor, VS Code MCP, Claude Desktop with streamableHttp) Same pattern: GET probe during handshake before POST initialize

POST /mcp with a valid initialize request works; the failure is specific to
the missing or late-registered GET handler.

Fix

  • Register GET /mcp and DELETE /mcp at server startup (before POST /mcp).
  • Return 405 with a JSON-RPC-style error body for stateless mode (no SSE).
  • Remove duplicate route registration from the POST catch block.

How to verify

  1. Build/run the extension from this branch.
  2. Ensure DebugMCP is listening on port 3001 (default debugmcp.serverPort).
# Must return 405, not 404
curl -i -X GET http://localhost:3001/mcp \
  -H "Accept: application/json, text/event-stream"

# Must return 200 with initialize result
curl -i -X POST http://localhost:3001/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
  1. MCP Inspector: Transport = Streamable HTTP, URL = http://localhost:3001/mcp,
    headers Accept: application/json, text/event-stream, Content-Type: application/json → connect succeeds.

  2. JetBrains Rider: Settings → Tools → MCP Servers — add HTTP server
    http://localhost:3001/mcp with Streamable HTTP → tools/resources list loads.

Notes

  • Legacy GET /sse remains 410 Gone (deprecated SSE endpoint); this change only fixes /mcp.
  • No session/SSE support is added; clients that accept 405 on GET continue with POST-only communication.

Move GET and DELETE /mcp route registration out of the POST error handler
so they are available at server startup.

Streamable HTTP clients (MCP Inspector, JetBrains Rider MCP, Cursor, and
others) send GET /mcp during connect to detect an optional SSE stream.
Per the MCP transport spec, a stateless server without SSE must respond
with HTTP 405 Method Not Allowed. Because handlers were only registered
inside the POST /mcp catch block, GET /mcp returned 404 until a POST
failed, and clients failed to connect with errors like "Expected status
code 200 but was 404".
@jekakmail
Copy link
Copy Markdown
Author

@jekakmail please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"

Contributor License Agreement

@microsoft-github-policy-service agree

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant