feat(mcp): upgrade client SDK to v2 (#39247)

Co-authored-by: Matt Carey <mcarey@cloudflare.com>
This commit is contained in:
Aiden Cline
2026-07-27 23:19:33 -05:00
committed by GitHub
parent 199e80d83e
commit fb3b8fd059
28 changed files with 271 additions and 1028 deletions
+13 -18
View File
@@ -1,18 +1,12 @@
import path from "node:path"
import { pathToFileURL } from "node:url"
import { expect } from "bun:test"
import { Server } from "@modelcontextprotocol/sdk/server/index.js"
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js"
import {
GetPromptRequestSchema,
ListPromptsRequestSchema,
ListResourcesRequestSchema,
ListResourceTemplatesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
Server,
WebStandardStreamableHTTPServerTransport,
type ServerCapabilities,
type Tool,
} from "@modelcontextprotocol/sdk/types.js"
} from "@modelcontextprotocol/server"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Cause, Effect, Exit } from "effect"
import type { MCP as MCPNS } from "../../src/mcp/index"
@@ -66,35 +60,35 @@ function lifecycleServer(input?: { capabilities?: ServerCapabilities; instructio
})
if (capabilities.tools) {
protocol.setRequestHandler(ListToolsRequestSchema, (request) => {
protocol.setRequestHandler("tools/list", (request) => {
if (state.listToolsError) throw new Error(state.listToolsError)
const page = state.toolPages?.[request.params?.cursor ?? "initial"]
return Promise.resolve({ tools: page?.items ?? state.tools, nextCursor: page?.nextCursor })
})
}
if (capabilities.prompts) {
protocol.setRequestHandler(ListPromptsRequestSchema, (request) => {
protocol.setRequestHandler("prompts/list", (request) => {
const page = state.promptPages?.[request.params?.cursor ?? "initial"]
return Promise.resolve({ prompts: page?.items ?? state.prompts, nextCursor: page?.nextCursor })
})
protocol.setRequestHandler(GetPromptRequestSchema, async () => {
protocol.setRequestHandler("prompts/get", async () => {
if (state.requestDelay) await Bun.sleep(state.requestDelay)
return { messages: [{ role: "user", content: { type: "text", text: "prompt result" } }] }
})
}
if (capabilities.resources) {
protocol.setRequestHandler(ListResourcesRequestSchema, (request) => {
protocol.setRequestHandler("resources/list", (request) => {
const page = state.resourcePages?.[request.params?.cursor ?? "initial"]
return Promise.resolve({ resources: page?.items ?? state.resources, nextCursor: page?.nextCursor })
})
protocol.setRequestHandler(ListResourceTemplatesRequestSchema, (request) => {
protocol.setRequestHandler("resources/templates/list", (request) => {
const page = state.resourceTemplatePages?.[request.params?.cursor ?? "initial"]
return Promise.resolve({
resourceTemplates: page?.items ?? state.resourceTemplates,
nextCursor: page?.nextCursor,
})
})
protocol.setRequestHandler(ReadResourceRequestSchema, async (request) => {
protocol.setRequestHandler("resources/read", async (request) => {
if (state.requestDelay) await Bun.sleep(state.requestDelay)
return { contents: [{ uri: request.params.uri, text: "resource result" }] }
})
@@ -145,7 +139,7 @@ function hangingLifecycleServer() {
return Effect.acquireRelease(
Effect.promise(async () => {
const protocol = new Server({ name: "mcp-lifecycle-hanging", version: "1.0.0" }, { capabilities: { tools: {} } })
protocol.setRequestHandler(ListToolsRequestSchema, () => Promise.resolve({ tools: [] }))
protocol.setRequestHandler("tools/list", () => Promise.resolve({ tools: [] }))
const transport = new WebStandardStreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
enableJsonResponse: true,
@@ -284,7 +278,7 @@ it.instance("follows cursors when listing tools, prompts, resources, and templat
}),
)
it.instance("accepts empty cursors and rejects repeated cursors", () =>
it.instance("accepts empty cursors and terminates on repeated cursors", () =>
Effect.gen(function* () {
const empty = yield* lifecycleServer({ capabilities: { prompts: {} } })
empty.state.promptPages = {
@@ -301,7 +295,8 @@ it.instance("accepts empty cursors and rejects repeated cursors", () =>
const result = yield* mcp.add("looping-cursor", remote(looping.url))
expect(Object.keys(yield* mcp.prompts())).toEqual(["empty-cursor:prompt-one", "empty-cursor:prompt-two"])
expect(statusName(result.status, "looping-cursor")).toBe("failed")
expect(statusName(result.status, "looping-cursor")).toBe("connected")
expect(Object.keys(yield* mcp.tools())).toEqual([])
}),
)