1- import {
2- Client ,
3- SSEClientTransport ,
4- StreamableHTTPClientTransport ,
5- type FetchLike ,
6- type OAuthClientProvider ,
7- } from "@modelcontextprotocol/client" ;
8- import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/client/validators/cf-worker" ;
1+ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js" ;
2+ import { Client } from "@modelcontextprotocol/sdk/client/index.js" ;
3+ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js" ;
4+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js" ;
5+ import type { FetchLike } from "@modelcontextprotocol/sdk/shared/transport.js" ;
6+ import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker" ;
97import { Effect , Layer , Predicate , Stream } from "effect" ;
108import { HttpClient , HttpClientRequest } from "effect/unstable/http" ;
119
1210// NOTE: `StdioClientTransport` is NOT imported eagerly. The upstream module
13- // (`@modelcontextprotocol/client/stdio`) still imports Node process/stream and
14- // `cross-spawn` eagerly at evaluation time, which crashes workerd (including
15- // vitest-pool-workers) with SIGSEGV on module instantiation. Cloud callers set
11+ // (`@modelcontextprotocol/sdk/ client/stdio.js `) touches `node:child_process`
12+ // at evaluation time, which crashes workerd (incl. vitest-pool-workers) at
13+ // SIGSEGV on module instantiation. Cloud callers set
1614// `dangerouslyAllowStdioMCP: false` and never reach the stdio branch below;
1715// prod bundles that DO use stdio load it via a dynamic import inside the
1816// stdio branch of `createMcpConnector`.
@@ -203,13 +201,12 @@ const fetchFromHttpClientLayer = (
203201// MCP plugin runs inside a Cloudflare Worker (executor.sh). The
204202// cfworker validator does not use code generation and works in every
205203// runtime we ship to.
206- const createClient = ( versionNegotiation ?: { readonly mode : "auto" } ) : Client =>
204+ const createClient = ( ) : Client =>
207205 new Client (
208206 { name : "executor-mcp" , version : "0.1.0" } ,
209207 {
210208 capabilities : { elicitation : { form : { } , url : { } } } ,
211209 jsonSchemaValidator : new CfWorkerJsonSchemaValidator ( ) ,
212- ...( versionNegotiation === undefined ? { } : { versionNegotiation } ) ,
213210 } ,
214211 ) ;
215212
@@ -250,10 +247,9 @@ const connectionFailure = (
250247const connectClient = ( input : {
251248 transport : string ;
252249 createTransport : ( ) => Parameters < Client [ "connect" ] > [ 0 ] ;
253- versionNegotiation ?: { readonly mode : "auto" } ;
254250} ) : Effect . Effect < McpConnection , McpConnectionError | McpOAuthReauthorizationRequired > =>
255251 Effect . gen ( function * ( ) {
256- const client = createClient ( input . versionNegotiation ) ;
252+ const client = createClient ( ) ;
257253 const transportInstance = input . createTransport ( ) ;
258254
259255 yield * Effect . tryPromise ( {
@@ -266,15 +262,6 @@ const connectClient = (input: {
266262 catch : ( cause ) =>
267263 connectionFailure ( input . transport , `Failed connecting via ${ input . transport } ` , cause ) ,
268264 } ) . pipe (
269- // The negotiated era ("modern" = 2026-07-28 server/discover, "legacy" =
270- // 2025 initialize) is otherwise invisible: both eras list and call tools
271- // identically, so traces are the one place an integration author can
272- // verify which handshake a connection actually used.
273- Effect . tap ( ( ) =>
274- Effect . annotateCurrentSpan ( {
275- "plugin.mcp.protocol_era" : client . getProtocolEra ( ) ?? "unknown" ,
276- } ) ,
277- ) ,
278265 Effect . withSpan ( "plugin.mcp.connection.handshake" , {
279266 attributes : { "plugin.mcp.transport" : input . transport } ,
280267 } ) ,
@@ -313,12 +300,6 @@ export const createMcpConnector = (input: ConnectorInput): McpConnector => {
313300
314301 return yield * connectClient ( {
315302 transport : "stdio" ,
316- // Opt-in per integration (default legacy) — see
317- // `McpStdioVersionNegotiation` for why stdio does not follow the
318- // remote transport's unconditional auto.
319- ...( input . versionNegotiation === "auto"
320- ? { versionNegotiation : { mode : "auto" as const } }
321- : { } ) ,
322303 createTransport : ( ) =>
323304 createStdioTransport ( {
324305 command,
@@ -338,13 +319,8 @@ export const createMcpConnector = (input: ConnectorInput): McpConnector => {
338319
339320 const endpoint = buildEndpointUrl ( input . endpoint , input . queryParams ?? { } ) ;
340321
341- // Auto-negotiate the 2026-07-28 era unconditionally only on Streamable
342- // HTTP. SSE is a legacy-only transport; stdio negotiates per the
343- // integration's `versionNegotiation` (default legacy — see the stdio
344- // branch above).
345322 const connectStreamableHttp = connectClient ( {
346323 transport : "streamable-http" ,
347- versionNegotiation : { mode : "auto" } ,
348324 createTransport : ( ) =>
349325 new StreamableHTTPClientTransport ( endpoint , {
350326 requestInit,
0 commit comments