Skip to content

Add McpClient: Effect-native MCP client#6396

Open
polRk wants to merge 21 commits into
Effect-TS:mainfrom
polRk:claude/effect-ts-port-a1156d
Open

Add McpClient: Effect-native MCP client#6396
polRk wants to merge 21 commits into
Effect-TS:mainfrom
polRk:claude/effect-ts-port-a1156d

Conversation

@polRk

@polRk polRk commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports the Effect-native McpClient (Model Context Protocol client) from
effect-smol#2534 into Effect-TS/effect, and bases it on the MCP
protocol-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-pick

The 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/main for these files, so the cherry-pick applied with zero
conflicts
. #2601 provides:

  • MCP protocol versions 2025-11-25 and 2025-06-18 with per-session
    negotiation and lifecycle enforcement.
  • Fixes to the MCP schemas the client relies on — notably CreateMessageResult
    (adds required role/content), sampling metadata made optional,
    elicitation content constrained to primitives, base64 binary content, _meta.
  • A Streamable-HTTP-compliant McpServer.layerHttp (session identity,
    MCP-Protocol-Version enforcement, 400/404/202, notification gating) — the
    compliant server this client is tested against.

Client — effect-smol#2534 (@polRk) + spec-compliance fixes on top

McpClient: automatic initialize/initialized handshake, stdio and Streamable
HTTP 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-18
spec and the official @modelcontextprotocol/sdk (via a conformance audit):

  • Send the required Accept: application/json, text/event-stream header on every
    Streamable HTTP request (compliant servers otherwise reply 406).
  • Derive supported/latest protocol versions from the shared
    McpSchema.ProtocolVersion; offer the latest and validate the negotiated one.
  • Send MCP-Protocol-Version from the negotiated initialize result body
    (a compliant server is not required to echo it as a response header).
  • Fork the roots/list_changed forwarder only after the handshake completes
    (MCP lifecycle ordering).
  • Spawn the stdio server with extendEnv: true (a custom env no longer strips
    PATH/HOME) and drain the child's stderr (avoids deadlock on a full pipe).

Known limitations / follow-ups (tracked separately)

Deferred to keep this PR reviewable; each needs its own tests:

  • SSE responses — the HTTP transport decodes JSON responses only, not
    text/event-stream. Needed for servers (including the official SDK) that
    stream POST responses.
  • HTTP hang-safety — in-flight requests aren't failed on child-process exit;
    send() lacks the terminal/empty-response guards from
    RpcClient.makeProtocolHttp.
  • Cancellation — interrupting a request doesn't emit
    notifications/cancelled, and inbound notifications/cancelled is a no-op.
  • HTTP session lifecycle — no DELETE termination, no 404 session-expiry
    handling.
  • Systemic (shared effect-rpc JSON-RPC serialization, affects McpServer
    too)
    — JSON-RPC notifications are emitted carrying an id, which is not
    standard-JSON-RPC-compliant for third-party peers. Out of scope here.

Test plan

  • pnpm check — clean
  • pnpm 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 lint and pnpm docgen — clean

IMax153 and others added 21 commits July 14, 2026 23:17
…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.
@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Jul 14, 2026
@changeset-bot

changeset-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3746db9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 27 packages
Name Type
effect Major
@effect/opentelemetry Major
@effect/platform-browser Major
@effect/platform-bun Major
@effect/platform-node-shared Major
@effect/platform-node Major
@effect/vitest Major
@effect/ai-anthropic Major
@effect/ai-openai-compat Major
@effect/ai-openai Major
@effect/ai-openrouter Major
@effect/atom-react Major
@effect/atom-solid Major
@effect/atom-vue Major
@effect/sql-clickhouse Major
@effect/sql-d1 Major
@effect/sql-libsql Major
@effect/sql-mssql Major
@effect/sql-mysql2 Major
@effect/sql-pg Major
@effect/sql-pglite Major
@effect/sql-sqlite-bun Major
@effect/sql-sqlite-do Major
@effect/sql-sqlite-node Major
@effect/sql-sqlite-react-native Major
@effect/sql-sqlite-wasm Major
@effect/openapi-generator Major

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

@IMax153 IMax153 added enhancement New feature or request 4.0 ai labels Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 ai enhancement New feature or request

Projects

Status: Discussion Ongoing

Development

Successfully merging this pull request may close these issues.

2 participants