Add McpClient: Effect-native MCP client#6396
Open
polRk wants to merge 21 commits into
Open
Conversation
…s, lifecycle ordering, stdio env/stderr - Send the required 'Accept: application/json, text/event-stream' header on every Streamable HTTP request (compliant servers 406 without it). - Derive supported/latest protocol versions from the shared McpSchema.ProtocolVersion (now 2025-11-25/2025-06-18) instead of a stale hardcoded list; offer the latest. - Fork the roots/list_changed forwarder only after the initialize/initialized handshake, per the MCP lifecycle. - Pass extendEnv:true when spawning the stdio server so a custom env augments rather than replaces the parent (keeps PATH/HOME). - Drain the child's stderr to the debug log so a chatty server cannot deadlock.
The Streamable HTTP transport previously only captured the negotiated protocol version from an 'mcp-protocol-version' response header. A spec-compliant server is only required to advertise the negotiated version in the InitializeResult body (the response header is optional), so against a non-echoing server the client sent no MCP-Protocol-Version header on subsequent requests and the server would fall back to assuming 2025-03-26. Capture the version from the initialize response body (authoritative), keeping the header capture as a fallback.
🦋 Changeset detectedLatest commit: 3746db9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 27 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.
Summary
Ports the Effect-native
McpClient(Model Context Protocol client) fromeffect-smol#2534 into
Effect-TS/effect, and bases it on the MCPprotocol-version-negotiation / spec-compliance work from effect-smol#2601 so
the client relies on current, compliant schemas and a compliant server
counterpart.
Because GitHub cannot transfer PRs between repositories, this is the replacement
PR for the effect-smol → effect migration:
What was taken, how, and why
Base — effect-smol#2601 (@IMax153), 18 commits, brought in via
git cherry-pickThe 18 commits of #2601 are included verbatim with original authorship
preserved because the client depends on the schema and negotiation work they
contain. The effect-smol base at #2601's branch point was byte-identical to
effect/mainfor these files, so the cherry-pick applied with zeroconflicts. #2601 provides:
2025-11-25and2025-06-18with per-sessionnegotiation and lifecycle enforcement.
CreateMessageResult(adds required
role/content), samplingmetadatamade optional,elicitation content constrained to primitives, base64 binary content,
_meta.McpServer.layerHttp(session identity,MCP-Protocol-Versionenforcement, 400/404/202, notification gating) — thecompliant server this client is tested against.
Client — effect-smol#2534 (@polRk) + spec-compliance fixes on top
McpClient: automaticinitialize/initializedhandshake, stdio and StreamableHTTP transports, typed methods for tools/resources/prompts/completion, and
host-side handlers for server-initiated sampling/roots/elicitation.
Adapted to #2601's stricter schemas, then fixed against the MCP
2025-06-18spec and the official
@modelcontextprotocol/sdk(via a conformance audit):Accept: application/json, text/event-streamheader on everyStreamable HTTP request (compliant servers otherwise reply
406).McpSchema.ProtocolVersion; offer the latest and validate the negotiated one.MCP-Protocol-Versionfrom the negotiatedinitializeresult body(a compliant server is not required to echo it as a response header).
roots/list_changedforwarder only after the handshake completes(MCP lifecycle ordering).
extendEnv: true(a customenvno longer stripsPATH/HOME) and drain the child'sstderr(avoids deadlock on a full pipe).Known limitations / follow-ups (tracked separately)
Deferred to keep this PR reviewable; each needs its own tests:
text/event-stream. Needed for servers (including the official SDK) thatstream POST responses.
send()lacks the terminal/empty-response guards fromRpcClient.makeProtocolHttp.notifications/cancelled, and inboundnotifications/cancelledis a no-op.DELETEtermination, no 404 session-expiryhandling.
effect-rpcJSON-RPC serialization, affectsMcpServertoo) — JSON-RPC notifications are emitted carrying an
id, which is notstandard-JSON-RPC-compliant for third-party peers. Out of scope here.
Test plan
pnpm check— cleanpnpm test packages/effect/test/unstable/ai/— 431 passed (15 files),including feat: multiple span processors and metric readers #2601's schema/server/connection conformance suites and the
McpClient stdio + Streamable HTTP suites
pnpm lintandpnpm docgen— clean