fix(rpc,ai): conform MCP Streamable HTTP transport to 2025-06-18#6275
Open
milkyskies wants to merge 1 commit into
Open
fix(rpc,ai): conform MCP Streamable HTTP transport to 2025-06-18#6275milkyskies wants to merge 1 commit into
milkyskies wants to merge 1 commit into
Conversation
The JSON-RPC serialization replied to a single request with a one-element array, but MCP 2025-06-18 removed JSON-RPC batching, so conformant clients (Claude Desktop/Code) reject the array and refuse to connect. - @effect/rpc: reply to a single (non-batch) request with a single JSON-RPC object; batches still receive an array. - @effect/ai: McpServer.layerHttp/layerHttpRouter answer GET and DELETE with 405 (Allow: POST) instead of 404, matching the stateless POST-only transport. Closes Effect-TS#6274 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 9944ae9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #6274.
McpServer.layerHttp/layerHttpRouterserve MCP over the generic JSON-RPC RPC protocol. At the HTTP wire level this diverged from the MCP Streamable HTTP transport, so spec-conformant MCP clients reject the endpoint.Changes
@effect/rpc— single request → single response objectThe JSON-RPC serialization replied to a single request with a one-element array (
[{...}]). MCP 2025-06-18 removed JSON-RPC batching, and clients built to that revision expect a single response object and choke on the array.The parser now tracks whether the decoded payload was a batch (top-level array) and frames the response to match: a single request gets a single object, a batch still gets an array. This is correct JSON-RPC 2.0 behavior generally, not MCP-specific —
RpcClientalready decodes both shapes, so existing RPC-over-HTTP consumers are unaffected.@effect/ai—GET/DELETE→405instead of404The transport is stateless and serves MCP over
POSTonly.GET(the optional server→client SSE stream) andDELETE(session teardown) now return405 Method Not AllowedwithAllow: POST, so the path is distinguishable from "endpoint doesn't exist."Tests
Both fail on
mainand pass with this change:packages/rpc/test/RpcSerialization.test.ts— unit: single request → object, batch → array.packages/ai/ai/test/McpServer.test.ts— end-to-end viaHttpLayerRouter.toWebHandler:initializereturns a single object;GETreturns405withAllow: POST.Out of scope
Mcp-Session-Id: deliberately not added. The transport is stateless, and omitting the header is precisely how the spec lets a server signal statelessness; returning an id that subsequent requests don't honor would be worse. Stateful session support is a separate feature.mcp-remotebridge, or a directinitializerequest — not Desktop's native "add a URL" flow. (Confirmed against a live deployment with direct requests:initializereturns a single object,tools/listreturns the full tool set, andGET→405.)🤖 Generated with Claude Code