Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 8 additions & 47 deletions examples/mcp-elicitation/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,31 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
createMcpHandler,
type TransportState,
WorkerTransport
} from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/server";
import { McpAgent } from "agents/mcp";
import * as z from "zod";
import { Agent, getAgentByName } from "agents";
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
import { env } from "cloudflare:workers";

const STATE_KEY = "mcp_transport_state";

interface State {
counter: number;
}

export class MyAgent extends Agent<Cloudflare.Env, State> {
server = new McpServer(
{
name: "test",
version: "1.0.0"
},
{
jsonSchemaValidator: new CfWorkerJsonSchemaValidator()
}
);

transport = new WorkerTransport({
sessionIdGenerator: () => this.name,
storage: {
get: () => {
return this.ctx.storage.kv.get<TransportState>(STATE_KEY);
},
set: (state: TransportState) => {
this.ctx.storage.kv.put<TransportState>(STATE_KEY, state);
}
}
export class MyAgent extends McpAgent<Cloudflare.Env, State> {
server = new McpServer({
name: "test",
version: "1.0.0"
});

initialState = {
counter: 0
};

onStart(): void | Promise<void> {
async init() {
this.server.registerTool(
"increase-counter",
{
description: "Increase the counter",
inputSchema: {
confirm: z.boolean().describe("Do you want to increase the counter?")

Check failure on line 25 in examples/mcp-elicitation/src/index.ts

View workflow job for this annotation

GitHub Actions / ci

Object literal may only specify known properties, and 'confirm' does not exist in type 'StandardSchemaWithJSON<unknown, unknown>'.
}
},
async ({ confirm }, extra) => {

Check failure on line 28 in examples/mcp-elicitation/src/index.ts

View workflow job for this annotation

GitHub Actions / ci

Property 'confirm' does not exist on type 'unknown'.
if (!confirm) {
return {
content: [{ type: "text", text: "Counter increase cancelled." }]
Expand All @@ -73,7 +47,7 @@
required: ["amount"]
}
},
{ relatedRequestId: extra.requestId }

Check failure on line 50 in examples/mcp-elicitation/src/index.ts

View workflow job for this annotation

GitHub Actions / ci

Property 'requestId' does not exist on type 'ServerContext'.
);

if (basicInfo.action !== "accept" || !basicInfo.content) {
Expand Down Expand Up @@ -113,19 +87,6 @@
}
);
}

async onMcpRequest(request: Request) {
return createMcpHandler(this.server, {
transport: this.transport
})(request, this.env, {} as ExecutionContext);
}
}

export default {
async fetch(request: Request) {
const sessionId =
request.headers.get("mcp-session-id") ?? crypto.randomUUID();
const agent = await getAgentByName(env.MyAgent, sessionId);
return await agent.onMcpRequest(request);
}
};
export default MyAgent.serve("/mcp", { binding: "MyAgent" });
48 changes: 47 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/agents/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.29.0",
"@cfworker/json-schema": "^4.1.1",
"@modelcontextprotocol/sdk": "1.29.0",
"@modelcontextprotocol/client": "^2.0.0-alpha.2",
"@modelcontextprotocol/server": "^2.0.0-alpha.2",
"@rolldown/plugin-babel": "^0.2.3",
"cron-schedule": "^6.0.0",
"mimetext": "^3.0.28",
Expand Down
8 changes: 3 additions & 5 deletions packages/agents/src/experimental/webmcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,8 @@
* @experimental This API is not yet stable and may change.
*/

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { ToolListChangedNotificationSchema } from "@modelcontextprotocol/sdk/types.js";

import { Client } from "@modelcontextprotocol/client";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
// ── WebMCP browser API surface (Chrome's navigator.modelContext) ─────

interface ModelContextToolAnnotations {
Expand Down Expand Up @@ -193,7 +191,7 @@ class McpHttpClient {
await this._client.connect(this._transport);

this._client.setNotificationHandler(
ToolListChangedNotificationSchema,
"notifications/tools/list_changed",
async () => {
if (signal?.aborted) return;
this._onToolsChanged?.();
Expand Down
6 changes: 3 additions & 3 deletions packages/agents/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AsyncLocalStorage } from "node:async_hooks";
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
import type { Client } from "@modelcontextprotocol/client";
import {
__DO_NOT_USE_WILL_BREAK__agentContext as agentContext,
type AgentEmail
Expand All @@ -16,15 +16,15 @@ export {
SUB_PREFIX
} from "./sub-routing";
export type { SubAgentPathMatch } from "./sub-routing";
import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
import type { SSEClientTransportOptions } from "@modelcontextprotocol/client";
import { signAgentHeaders } from "./email";

import type {
Prompt,
Resource,
ServerCapabilities,
Tool
} from "@modelcontextprotocol/sdk/types.js";
} from "@modelcontextprotocol/server";
import { parseCronExpression } from "cron-schedule";
import { nanoid } from "nanoid";
import { EmailMessage } from "cloudflare:email";
Expand Down
27 changes: 27 additions & 0 deletions packages/agents/src/mcp/cf-validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Server } from "@modelcontextprotocol/server";
import type { McpServer } from "@modelcontextprotocol/server";
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/server/validators/cf-worker";

/**
* Inject the Cloudflare Worker-compatible JSON Schema validator into an MCP
* server instance. The default Ajv validator uses `new Function()` which is
* blocked in Workers. This replaces it with `@cfworker/json-schema` which
* validates without code generation.
*
* Works with both `McpServer` (high-level) and `Server` (low-level).
*/
export function injectCfWorkerValidator(server: McpServer | Server): void {
// McpServer wraps Server as .server; raw Server is the object itself
const innerServer: Server =
"server" in server && typeof (server as McpServer).server === "object"
? (server as McpServer).server
: (server as Server);

// _jsonSchemaValidator is private, but we need to override it to avoid
// the Ajv "Code generation from strings disallowed" error in Workers.
Object.defineProperty(innerServer, "_jsonSchemaValidator", {
value: new CfWorkerJsonSchemaValidator(),
writable: true,
configurable: true
});
}
30 changes: 11 additions & 19 deletions packages/agents/src/mcp/client-connection.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,25 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { Client } from "@modelcontextprotocol/client";
import {
SSEClientTransport,
type SSEClientTransportOptions
} from "@modelcontextprotocol/sdk/client/sse.js";
} from "@modelcontextprotocol/client";
import {
StreamableHTTPClientTransport,
type StreamableHTTPClientTransportOptions
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
} from "@modelcontextprotocol/client";
// Import types directly from MCP SDK
import type {
Prompt,
Resource,
Tool
} from "@modelcontextprotocol/sdk/types.js";
import type { Prompt, Resource, Tool } from "@modelcontextprotocol/client";
import {
type ClientCapabilities,
type ElicitRequest,
ElicitRequestSchema,
type ElicitResult,
type ListPromptsResult,
type ListResourceTemplatesResult,
type ListResourcesResult,
type ListToolsResult,
PromptListChangedNotificationSchema,
ResourceListChangedNotificationSchema,
type ResourceTemplate,
type ServerCapabilities,
ToolListChangedNotificationSchema
} from "@modelcontextprotocol/sdk/types.js";
type ServerCapabilities
} from "@modelcontextprotocol/client";
import { Emitter, type Event } from "../core/events";
import type { MCPObservabilityEvent } from "../observability/mcp";
import type { AgentMcpOAuthProvider } from "./do-oauth-client-provider";
Expand Down Expand Up @@ -168,7 +160,7 @@ export class MCPClientConnection {
if (res.state === MCPConnectionState.CONNECTED && res.transport) {
// Set up elicitation request handler after successful connection
this.client.setRequestHandler(
ElicitRequestSchema,
"elicitation/create",
async (request: ElicitRequest) => {
return await this.handleElicitationRequest(request);
}
Expand Down Expand Up @@ -492,7 +484,7 @@ export class MCPClientConnection {
this._probingCapabilities
) {
this.client.setNotificationHandler(
ToolListChangedNotificationSchema,
"notifications/tools/list_changed",
async (_notification) => {
this.tools = await this.fetchTools();
}
Expand All @@ -512,7 +504,7 @@ export class MCPClientConnection {
this._probingCapabilities
) {
this.client.setNotificationHandler(
ResourceListChangedNotificationSchema,
"notifications/resources/list_changed",
async (_notification) => {
this.resources = await this.fetchResources();
}
Expand All @@ -532,7 +524,7 @@ export class MCPClientConnection {
this._probingCapabilities
) {
this.client.setNotificationHandler(
PromptListChangedNotificationSchema,
"notifications/prompts/list_changed",
async (_notification) => {
this.prompts = await this.fetchPrompts();
}
Expand Down Expand Up @@ -619,7 +611,7 @@ export class MCPClientConnection {
_request: ElicitRequest
): Promise<ElicitResult> {
// Elicitation handling must be implemented by the platform
// For MCP servers, this should be handled by McpAgent.elicitInput()
// For MCP servers, this should be handled by server.server.elicitInput()
throw new Error(
"Elicitation handler must be implemented for your platform. Override handleElicitationRequest method."
);
Expand Down
16 changes: 8 additions & 8 deletions packages/agents/src/mcp/client-transports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@
* Deprecated transport wrappers
*/

import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import type { StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { SSEClientTransport } from "@modelcontextprotocol/client";
import type { SSEClientTransportOptions } from "@modelcontextprotocol/client";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
import type { StreamableHTTPClientTransportOptions } from "@modelcontextprotocol/client";

let didWarnAboutSSEEdgeClientTransport = false;

/**
* @deprecated Use SSEClientTransport from @modelcontextprotocol/sdk/client/sse.js instead. This alias will be removed in the next major version.
* @deprecated Use SSEClientTransport from @modelcontextprotocol/client instead. This alias will be removed in the next major version.
*/
export class SSEEdgeClientTransport extends SSEClientTransport {
constructor(url: URL, options: SSEClientTransportOptions) {
super(url, options);
if (!didWarnAboutSSEEdgeClientTransport) {
didWarnAboutSSEEdgeClientTransport = true;
console.warn(
"SSEEdgeClientTransport is deprecated. Use SSEClientTransport from @modelcontextprotocol/sdk/client/sse.js instead. SSEEdgeClientTransport will be removed in the next major version."
"SSEEdgeClientTransport is deprecated. Use SSEClientTransport from @modelcontextprotocol/client instead. SSEEdgeClientTransport will be removed in the next major version."
);
}
}
Expand All @@ -27,15 +27,15 @@ export class SSEEdgeClientTransport extends SSEClientTransport {
let didWarnAboutStreamableHTTPEdgeClientTransport = false;

/**
* @deprecated Use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js instead. This alias will be removed in the next major version.
* @deprecated Use StreamableHTTPClientTransport from @modelcontextprotocol/client instead. This alias will be removed in the next major version.
*/
export class StreamableHTTPEdgeClientTransport extends StreamableHTTPClientTransport {
constructor(url: URL, options: StreamableHTTPClientTransportOptions) {
super(url, options);
if (!didWarnAboutStreamableHTTPEdgeClientTransport) {
didWarnAboutStreamableHTTPEdgeClientTransport = true;
console.warn(
"StreamableHTTPEdgeClientTransport is deprecated. Use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js instead. StreamableHTTPEdgeClientTransport will be removed in the next major version."
"StreamableHTTPEdgeClientTransport is deprecated. Use StreamableHTTPClientTransport from @modelcontextprotocol/client instead. StreamableHTTPEdgeClientTransport will be removed in the next major version."
);
}
}
Expand Down
Loading
Loading