agents 0.0.0-88ea3a1 → 0.0.0-8bc0470

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  Agent
3
- } from "../chunk-YMUU7QHV.js";
3
+ } from "../chunk-JR3NW4A7.js";
4
+ import "../chunk-7VFQNJFK.js";
4
5
  import {
5
6
  __privateAdd,
6
7
  __privateGet,
@@ -10,23 +11,33 @@ import {
10
11
 
11
12
  // src/mcp/index.ts
12
13
  import { DurableObject } from "cloudflare:workers";
13
- import { JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js";
14
- var MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
15
- function handleCORS(request, corsOptions) {
16
- const origin = request.headers.get("Origin") || "*";
17
- const corsHeaders = {
18
- "Access-Control-Allow-Origin": corsOptions?.origin || origin,
19
- "Access-Control-Allow-Methods": corsOptions?.methods || "GET, POST, OPTIONS",
20
- "Access-Control-Allow-Headers": corsOptions?.headers || "Content-Type",
21
- "Access-Control-Max-Age": (corsOptions?.maxAge || 86400).toString()
14
+ import {
15
+ InitializeRequestSchema,
16
+ isJSONRPCError,
17
+ isJSONRPCNotification,
18
+ isJSONRPCRequest,
19
+ isJSONRPCResponse,
20
+ JSONRPCMessageSchema
21
+ } from "@modelcontextprotocol/sdk/types.js";
22
+ var MAXIMUM_MESSAGE_SIZE_BYTES = 4 * 1024 * 1024;
23
+ function corsHeaders(request, corsOptions = {}) {
24
+ const origin = "*";
25
+ return {
26
+ "Access-Control-Allow-Origin": corsOptions.origin || origin,
27
+ "Access-Control-Allow-Methods": corsOptions.methods || "GET, POST, OPTIONS",
28
+ "Access-Control-Allow-Headers": corsOptions.headers || "Content-Type, mcp-session-id",
29
+ "Access-Control-Max-Age": (corsOptions.maxAge || 86400).toString(),
30
+ "Access-Control-Expose-Headers": corsOptions.exposeHeaders || "mcp-session-id"
22
31
  };
32
+ }
33
+ function handleCORS(request, corsOptions) {
23
34
  if (request.method === "OPTIONS") {
24
- return new Response(null, { headers: corsHeaders });
35
+ return new Response(null, { headers: corsHeaders(request, corsOptions) });
25
36
  }
26
37
  return null;
27
38
  }
28
39
  var _getWebSocket, _started;
29
- var McpTransport = class {
40
+ var McpSSETransport = class {
30
41
  constructor(getWebSocket) {
31
42
  __privateAdd(this, _getWebSocket);
32
43
  __privateAdd(this, _started, false);
@@ -59,18 +70,76 @@ var McpTransport = class {
59
70
  };
60
71
  _getWebSocket = new WeakMap();
61
72
  _started = new WeakMap();
62
- var _status, _transport, _connected, _agent, _McpAgent_instances, initialize_fn;
63
- var McpAgent = class extends DurableObject {
73
+ var _getWebSocketForGetRequest, _getWebSocketForMessageID, _notifyResponseIdSent, _started2;
74
+ var McpStreamableHttpTransport = class {
75
+ constructor(getWebSocketForMessageID, notifyResponseIdSent) {
76
+ // TODO: If there is an open connection to send server-initiated messages
77
+ // back, we should use that connection
78
+ __privateAdd(this, _getWebSocketForGetRequest);
79
+ // Get the appropriate websocket connection for a given message id
80
+ __privateAdd(this, _getWebSocketForMessageID);
81
+ // Notify the server that a response has been sent for a given message id
82
+ // so that it may clean up it's mapping of message ids to connections
83
+ // once they are no longer needed
84
+ __privateAdd(this, _notifyResponseIdSent);
85
+ __privateAdd(this, _started2, false);
86
+ __privateSet(this, _getWebSocketForMessageID, getWebSocketForMessageID);
87
+ __privateSet(this, _notifyResponseIdSent, notifyResponseIdSent);
88
+ __privateSet(this, _getWebSocketForGetRequest, () => null);
89
+ }
90
+ async start() {
91
+ if (__privateGet(this, _started2)) {
92
+ throw new Error("Transport already started");
93
+ }
94
+ __privateSet(this, _started2, true);
95
+ }
96
+ async send(message) {
97
+ if (!__privateGet(this, _started2)) {
98
+ throw new Error("Transport not started");
99
+ }
100
+ let websocket = null;
101
+ if (isJSONRPCResponse(message) || isJSONRPCError(message)) {
102
+ websocket = __privateGet(this, _getWebSocketForMessageID).call(this, message.id.toString());
103
+ if (!websocket) {
104
+ throw new Error(
105
+ `Could not find WebSocket for message id: ${message.id}`
106
+ );
107
+ }
108
+ } else if (isJSONRPCRequest(message)) {
109
+ websocket = __privateGet(this, _getWebSocketForGetRequest).call(this);
110
+ } else if (isJSONRPCNotification(message)) {
111
+ websocket = null;
112
+ }
113
+ try {
114
+ websocket?.send(JSON.stringify(message));
115
+ if (isJSONRPCResponse(message)) {
116
+ __privateGet(this, _notifyResponseIdSent).call(this, message.id.toString());
117
+ }
118
+ } catch (error) {
119
+ this.onerror?.(error);
120
+ throw error;
121
+ }
122
+ }
123
+ async close() {
124
+ this.onclose?.();
125
+ }
126
+ };
127
+ _getWebSocketForGetRequest = new WeakMap();
128
+ _getWebSocketForMessageID = new WeakMap();
129
+ _notifyResponseIdSent = new WeakMap();
130
+ _started2 = new WeakMap();
131
+ var _status, _transport, _transportType, _requestIdToConnectionId, _agent, _McpAgent_instances, initialize_fn;
132
+ var _McpAgent = class _McpAgent extends DurableObject {
64
133
  constructor(ctx, env) {
65
134
  var _a;
66
135
  super(ctx, env);
67
136
  __privateAdd(this, _McpAgent_instances);
68
137
  __privateAdd(this, _status, "zero");
69
138
  __privateAdd(this, _transport);
70
- __privateAdd(this, _connected, false);
139
+ __privateAdd(this, _transportType, "unset");
140
+ __privateAdd(this, _requestIdToConnectionId, /* @__PURE__ */ new Map());
71
141
  /**
72
- * Since McpAgent's _aren't_ yet real "Agents" (they route differently, don't support
73
- * websockets, don't support hibernation), let's only expose a couple of the methods
142
+ * Since McpAgent's _aren't_ yet real "Agents", let's only expose a couple of the methods
74
143
  * to the outer class: initialState/state/setState/onStateUpdate/sql
75
144
  */
76
145
  __privateAdd(this, _agent);
@@ -80,12 +149,17 @@ var McpAgent = class extends DurableObject {
80
149
  onStateUpdate(state, source) {
81
150
  return self.onStateUpdate(state, source);
82
151
  }
152
+ async onMessage(connection, message) {
153
+ return self.onMessage(connection, message);
154
+ }
83
155
  }, _a.options = {
84
156
  hibernate: true
85
157
  }, _a)(ctx, env));
86
158
  }
159
+ get mcp() {
160
+ return __privateGet(this, _agent).mcp;
161
+ }
87
162
  get state() {
88
- if (this.initialState) __privateGet(this, _agent).initialState = this.initialState;
89
163
  return __privateGet(this, _agent).state;
90
164
  }
91
165
  sql(strings, ...values) {
@@ -97,19 +171,56 @@ var McpAgent = class extends DurableObject {
97
171
  onStateUpdate(state, source) {
98
172
  }
99
173
  async onStart() {
174
+ var _a;
175
+ const self = this;
176
+ __privateSet(this, _agent, new (_a = class extends Agent {
177
+ constructor() {
178
+ super(...arguments);
179
+ this.initialState = self.initialState;
180
+ }
181
+ onStateUpdate(state, source) {
182
+ return self.onStateUpdate(state, source);
183
+ }
184
+ async onMessage(connection, event) {
185
+ return self.onMessage(connection, event);
186
+ }
187
+ }, _a.options = {
188
+ hibernate: true
189
+ }, _a)(this.ctx, this.env));
100
190
  this.props = await this.ctx.storage.get("props");
101
- this.init?.();
102
- __privateSet(this, _transport, new McpTransport(() => this.getWebSocket()));
103
- await this.server.connect(__privateGet(this, _transport));
191
+ __privateSet(this, _transportType, await this.ctx.storage.get(
192
+ "transportType"
193
+ ));
194
+ await this._init(this.props);
195
+ const server = await this.server;
196
+ if (__privateGet(this, _transportType) === "sse") {
197
+ __privateSet(this, _transport, new McpSSETransport(() => this.getWebSocket()));
198
+ await server.connect(__privateGet(this, _transport));
199
+ } else if (__privateGet(this, _transportType) === "streamable-http") {
200
+ __privateSet(this, _transport, new McpStreamableHttpTransport(
201
+ (id) => this.getWebSocketForResponseID(id),
202
+ (id) => __privateGet(this, _requestIdToConnectionId).delete(id)
203
+ ));
204
+ await server.connect(__privateGet(this, _transport));
205
+ }
104
206
  }
105
207
  async _init(props) {
106
- await this.ctx.storage.put("props", props);
208
+ await this.ctx.storage.put("props", props ?? {});
209
+ if (!this.ctx.storage.get("transportType")) {
210
+ await this.ctx.storage.put("transportType", "unset");
211
+ }
107
212
  this.props = props;
108
213
  if (!this.initRun) {
109
214
  this.initRun = true;
110
215
  await this.init();
111
216
  }
112
217
  }
218
+ async setInitialized() {
219
+ await this.ctx.storage.put("initialized", true);
220
+ }
221
+ async isInitialized() {
222
+ return await this.ctx.storage.get("initialized") === true;
223
+ }
113
224
  // Allow the worker to fetch a websocket connection to the agent
114
225
  async fetch(request) {
115
226
  if (__privateGet(this, _status) !== "started") {
@@ -121,23 +232,42 @@ var McpAgent = class extends DurableObject {
121
232
  });
122
233
  }
123
234
  const url = new URL(request.url);
124
- const sessionId = url.searchParams.get("sessionId");
125
- if (!sessionId) {
126
- return new Response("Missing sessionId", { status: 400 });
127
- }
128
- const webSocketPair = new WebSocketPair();
129
- const [client, server] = Object.values(webSocketPair);
130
- if (__privateGet(this, _connected)) {
131
- return new Response("WebSocket already connected", { status: 400 });
235
+ const path = url.pathname;
236
+ const server = await this.server;
237
+ switch (path) {
238
+ case "/sse": {
239
+ const websockets = this.ctx.getWebSockets();
240
+ if (websockets.length > 0) {
241
+ return new Response("Websocket already connected", { status: 400 });
242
+ }
243
+ await this.ctx.storage.put("transportType", "sse");
244
+ __privateSet(this, _transportType, "sse");
245
+ if (!__privateGet(this, _transport)) {
246
+ __privateSet(this, _transport, new McpSSETransport(() => this.getWebSocket()));
247
+ await server.connect(__privateGet(this, _transport));
248
+ }
249
+ return __privateGet(this, _agent).fetch(request);
250
+ }
251
+ case "/streamable-http": {
252
+ if (!__privateGet(this, _transport)) {
253
+ __privateSet(this, _transport, new McpStreamableHttpTransport(
254
+ (id) => this.getWebSocketForResponseID(id),
255
+ (id) => __privateGet(this, _requestIdToConnectionId).delete(id)
256
+ ));
257
+ await server.connect(__privateGet(this, _transport));
258
+ }
259
+ await this.ctx.storage.put("transportType", "streamable-http");
260
+ __privateSet(this, _transportType, "streamable-http");
261
+ return __privateGet(this, _agent).fetch(request);
262
+ }
263
+ default:
264
+ return new Response(
265
+ "Internal Server Error: Expected /sse or /streamable-http path",
266
+ {
267
+ status: 500
268
+ }
269
+ );
132
270
  }
133
- this.ctx.acceptWebSocket(server);
134
- __privateSet(this, _connected, true);
135
- __privateSet(this, _transport, new McpTransport(() => this.getWebSocket()));
136
- await this.server.connect(__privateGet(this, _transport));
137
- return new Response(null, {
138
- status: 101,
139
- webSocket: client
140
- });
141
271
  }
142
272
  getWebSocket() {
143
273
  const websockets = this.ctx.getWebSockets();
@@ -146,26 +276,45 @@ var McpAgent = class extends DurableObject {
146
276
  }
147
277
  return websockets[0];
148
278
  }
149
- async onMCPMessage(sessionId, request) {
279
+ getWebSocketForResponseID(id) {
280
+ const connectionId = __privateGet(this, _requestIdToConnectionId).get(id);
281
+ if (connectionId === void 0) {
282
+ return null;
283
+ }
284
+ return __privateGet(this, _agent).getConnection(connectionId) ?? null;
285
+ }
286
+ // All messages received here. This is currently never called
287
+ async onMessage(connection, event) {
288
+ if (__privateGet(this, _transportType) !== "streamable-http") {
289
+ const err = new Error(
290
+ "Internal Server Error: Expected streamable-http protocol"
291
+ );
292
+ __privateGet(this, _transport)?.onerror?.(err);
293
+ return;
294
+ }
295
+ let message;
296
+ try {
297
+ const data = typeof event === "string" ? event : new TextDecoder().decode(event);
298
+ message = JSONRPCMessageSchema.parse(JSON.parse(data));
299
+ } catch (error) {
300
+ __privateGet(this, _transport)?.onerror?.(error);
301
+ return;
302
+ }
303
+ if (isJSONRPCRequest(message)) {
304
+ __privateGet(this, _requestIdToConnectionId).set(message.id.toString(), connection.id);
305
+ }
306
+ __privateGet(this, _transport)?.onmessage?.(message);
307
+ }
308
+ // All messages received over SSE after the initial connection has been established
309
+ // will be passed here
310
+ async onSSEMcpMessage(sessionId, request) {
150
311
  if (__privateGet(this, _status) !== "started") {
151
312
  await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
152
313
  }
314
+ if (__privateGet(this, _transportType) !== "sse") {
315
+ return new Error("Internal Server Error: Expected SSE protocol");
316
+ }
153
317
  try {
154
- const contentType = request.headers.get("content-type") || "";
155
- if (!contentType.includes("application/json")) {
156
- return new Response(`Unsupported content-type: ${contentType}`, {
157
- status: 400
158
- });
159
- }
160
- const contentLength = Number.parseInt(
161
- request.headers.get("content-length") || "0",
162
- 10
163
- );
164
- if (contentLength > MAXIMUM_MESSAGE_SIZE) {
165
- return new Response(`Request body too large: ${contentLength} bytes`, {
166
- status: 400
167
- });
168
- }
169
318
  const message = await request.json();
170
319
  let parsedMessage;
171
320
  try {
@@ -175,72 +324,90 @@ var McpAgent = class extends DurableObject {
175
324
  throw error;
176
325
  }
177
326
  __privateGet(this, _transport)?.onmessage?.(parsedMessage);
178
- return new Response("Accepted", { status: 202 });
327
+ return null;
179
328
  } catch (error) {
329
+ console.error("Error forwarding message to SSE:", error);
180
330
  __privateGet(this, _transport)?.onerror?.(error);
181
- return new Response(String(error), { status: 400 });
331
+ return error;
182
332
  }
183
333
  }
184
- // This is unused since there are no incoming websocket messages
334
+ // Delegate all websocket events to the underlying agent
185
335
  async webSocketMessage(ws, event) {
186
- let message;
187
- try {
188
- const data = typeof event === "string" ? event : new TextDecoder().decode(event);
189
- message = JSONRPCMessageSchema.parse(JSON.parse(data));
190
- } catch (error) {
191
- __privateGet(this, _transport)?.onerror?.(error);
192
- return;
193
- }
194
336
  if (__privateGet(this, _status) !== "started") {
195
337
  await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
196
338
  }
197
- __privateGet(this, _transport)?.onmessage?.(message);
339
+ return await __privateGet(this, _agent).webSocketMessage(ws, event);
198
340
  }
199
341
  // WebSocket event handlers for hibernation support
200
342
  async webSocketError(ws, error) {
201
343
  if (__privateGet(this, _status) !== "started") {
202
344
  await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
203
345
  }
204
- __privateGet(this, _transport)?.onerror?.(error);
346
+ return await __privateGet(this, _agent).webSocketError(ws, error);
205
347
  }
206
348
  async webSocketClose(ws, code, reason, wasClean) {
207
349
  if (__privateGet(this, _status) !== "started") {
208
350
  await __privateMethod(this, _McpAgent_instances, initialize_fn).call(this);
209
351
  }
210
- __privateGet(this, _transport)?.onclose?.();
211
- __privateSet(this, _connected, false);
352
+ return await __privateGet(this, _agent).webSocketClose(ws, code, reason, wasClean);
212
353
  }
213
354
  static mount(path, {
214
355
  binding = "MCP_OBJECT",
215
356
  corsOptions
216
357
  } = {}) {
217
- const basePattern = new URLPattern({ pathname: path });
218
- const messagePattern = new URLPattern({ pathname: `${path}/message` });
358
+ return _McpAgent.serveSSE(path, { binding, corsOptions });
359
+ }
360
+ static serveSSE(path, {
361
+ binding = "MCP_OBJECT",
362
+ corsOptions
363
+ } = {}) {
364
+ let pathname = path;
365
+ if (path === "/") {
366
+ pathname = "/*";
367
+ }
368
+ const basePattern = new URLPattern({ pathname });
369
+ const messagePattern = new URLPattern({ pathname: `${pathname}/message` });
219
370
  return {
220
- fetch: async (request, env, ctx) => {
371
+ async fetch(request, env, ctx) {
221
372
  const corsResponse = handleCORS(request, corsOptions);
222
373
  if (corsResponse) return corsResponse;
223
374
  const url = new URL(request.url);
224
- const namespace = env[binding];
375
+ const bindingValue = env[binding];
376
+ if (bindingValue == null || typeof bindingValue !== "object") {
377
+ console.error(
378
+ `Could not find McpAgent binding for ${binding}. Did you update your wrangler configuration?`
379
+ );
380
+ return new Response("Invalid binding", { status: 500 });
381
+ }
382
+ if (bindingValue.toString() !== "[object DurableObjectNamespace]") {
383
+ return new Response("Invalid binding", { status: 500 });
384
+ }
385
+ const namespace = bindingValue;
225
386
  if (request.method === "GET" && basePattern.test(url)) {
226
387
  const sessionId = url.searchParams.get("sessionId") || namespace.newUniqueId().toString();
227
388
  const { readable, writable } = new TransformStream();
228
389
  const writer = writable.getWriter();
229
390
  const encoder = new TextEncoder();
391
+ const endpointUrl = new URL(request.url);
392
+ endpointUrl.pathname = encodeURI(`${pathname}/message`);
393
+ endpointUrl.searchParams.set("sessionId", sessionId);
394
+ const relativeUrlWithSession = endpointUrl.pathname + endpointUrl.search + endpointUrl.hash;
230
395
  const endpointMessage = `event: endpoint
231
- data: ${encodeURI(`${path}/message`)}?sessionId=${sessionId}
396
+ data: ${relativeUrlWithSession}
232
397
 
233
398
  `;
234
399
  writer.write(encoder.encode(endpointMessage));
235
- const id = namespace.idFromString(sessionId);
400
+ const id = namespace.idFromName(`sse:${sessionId}`);
236
401
  const doStub = namespace.get(id);
237
402
  await doStub._init(ctx.props);
238
403
  const upgradeUrl = new URL(request.url);
239
- upgradeUrl.searchParams.set("sessionId", sessionId);
404
+ upgradeUrl.pathname = "/sse";
240
405
  const response = await doStub.fetch(
241
406
  new Request(upgradeUrl, {
242
407
  headers: {
243
- Upgrade: "websocket"
408
+ Upgrade: "websocket",
409
+ // Required by PartyServer
410
+ "x-partykit-room": sessionId
244
411
  }
245
412
  })
246
413
  );
@@ -248,51 +415,55 @@ data: ${encodeURI(`${path}/message`)}?sessionId=${sessionId}
248
415
  if (!ws) {
249
416
  console.error("Failed to establish WebSocket connection");
250
417
  await writer.close();
251
- return;
418
+ return new Response("Failed to establish WebSocket connection", {
419
+ status: 500
420
+ });
252
421
  }
253
422
  ws.accept();
254
- ws.addEventListener("message", async (event) => {
255
- try {
256
- const message = JSON.parse(event.data);
257
- if (!(typeof message.id === "number" || message.id === null)) {
258
- throw new Error("Invalid jsonrpc message id");
259
- }
260
- if (message.jsonrpc !== "2.0") {
261
- throw new Error("Invalid jsonrpc version");
262
- }
263
- if (!Object.hasOwn(message, "result") && !Object.hasOwn(message, "error")) {
264
- throw new Error(
265
- "Invalid jsonrpc message. Must have either result or error field"
266
- );
267
- }
268
- const messageText = `event: message
269
- data: ${event.data}
423
+ ws.addEventListener("message", (event) => {
424
+ async function onMessage(event2) {
425
+ try {
426
+ const message = JSON.parse(event2.data);
427
+ const result = JSONRPCMessageSchema.safeParse(message);
428
+ if (!result.success) {
429
+ return;
430
+ }
431
+ const messageText = `event: message
432
+ data: ${JSON.stringify(result.data)}
270
433
 
271
434
  `;
272
- await writer.write(encoder.encode(messageText));
273
- } catch (error) {
274
- console.error("Error forwarding message to SSE:", error);
435
+ await writer.write(encoder.encode(messageText));
436
+ } catch (error) {
437
+ console.error("Error forwarding message to SSE:", error);
438
+ }
275
439
  }
440
+ onMessage(event).catch(console.error);
276
441
  });
277
- ws.addEventListener("error", async (error) => {
278
- try {
279
- await writer.close();
280
- } catch (e) {
442
+ ws.addEventListener("error", (error) => {
443
+ async function onError(error2) {
444
+ try {
445
+ await writer.close();
446
+ } catch (e) {
447
+ }
281
448
  }
449
+ onError(error).catch(console.error);
282
450
  });
283
- ws.addEventListener("close", async () => {
284
- try {
285
- await writer.close();
286
- } catch (error) {
287
- console.error("Error closing SSE connection:", error);
451
+ ws.addEventListener("close", () => {
452
+ async function onClose() {
453
+ try {
454
+ await writer.close();
455
+ } catch (error) {
456
+ console.error("Error closing SSE connection:", error);
457
+ }
288
458
  }
459
+ onClose().catch(console.error);
289
460
  });
290
461
  return new Response(readable, {
291
462
  headers: {
292
463
  "Content-Type": "text/event-stream",
293
464
  "Cache-Control": "no-cache",
294
465
  Connection: "keep-alive",
295
- "Access-Control-Allow-Origin": corsOptions?.origin || "*"
466
+ ...corsHeaders(request, corsOptions)
296
467
  }
297
468
  });
298
469
  }
@@ -300,34 +471,335 @@ data: ${event.data}
300
471
  const sessionId = url.searchParams.get("sessionId");
301
472
  if (!sessionId) {
302
473
  return new Response(
303
- `Missing sessionId. Expected POST to ${path} to initiate new one`,
474
+ `Missing sessionId. Expected POST to ${pathname} to initiate new one`,
304
475
  { status: 400 }
305
476
  );
306
477
  }
307
- const object = namespace.get(namespace.idFromString(sessionId));
308
- const response = await object.onMCPMessage(sessionId, request);
309
- const headers = new Headers();
310
- response.headers.forEach?.((value, key) => {
311
- headers.set(key, value);
312
- });
313
- headers.set(
314
- "Access-Control-Allow-Origin",
315
- corsOptions?.origin || "*"
478
+ const contentType = request.headers.get("content-type") || "";
479
+ if (!contentType.includes("application/json")) {
480
+ return new Response(`Unsupported content-type: ${contentType}`, {
481
+ status: 400
482
+ });
483
+ }
484
+ const contentLength = Number.parseInt(
485
+ request.headers.get("content-length") || "0",
486
+ 10
316
487
  );
317
- return new Response(response.body, {
318
- status: response.status,
319
- statusText: response.statusText,
320
- headers
488
+ if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {
489
+ return new Response(
490
+ `Request body too large: ${contentLength} bytes`,
491
+ {
492
+ status: 400
493
+ }
494
+ );
495
+ }
496
+ const id = namespace.idFromName(`sse:${sessionId}`);
497
+ const doStub = namespace.get(id);
498
+ const error = await doStub.onSSEMcpMessage(sessionId, request);
499
+ if (error) {
500
+ return new Response(error.message, {
501
+ status: 400,
502
+ headers: {
503
+ "Content-Type": "text/event-stream",
504
+ "Cache-Control": "no-cache",
505
+ Connection: "keep-alive",
506
+ ...corsHeaders(request, corsOptions)
507
+ }
508
+ });
509
+ }
510
+ return new Response("Accepted", {
511
+ status: 202,
512
+ headers: {
513
+ "Content-Type": "text/event-stream",
514
+ "Cache-Control": "no-cache",
515
+ Connection: "keep-alive",
516
+ ...corsHeaders(request, corsOptions)
517
+ }
321
518
  });
322
519
  }
323
520
  return new Response("Not Found", { status: 404 });
324
521
  }
325
522
  };
326
523
  }
524
+ static serve(path, {
525
+ binding = "MCP_OBJECT",
526
+ corsOptions
527
+ } = {}) {
528
+ let pathname = path;
529
+ if (path === "/") {
530
+ pathname = "/*";
531
+ }
532
+ const basePattern = new URLPattern({ pathname });
533
+ return {
534
+ async fetch(request, env, ctx) {
535
+ const corsResponse = handleCORS(request, corsOptions);
536
+ if (corsResponse) {
537
+ return corsResponse;
538
+ }
539
+ const url = new URL(request.url);
540
+ const bindingValue = env[binding];
541
+ if (bindingValue == null || typeof bindingValue !== "object") {
542
+ console.error(
543
+ `Could not find McpAgent binding for ${binding}. Did you update your wrangler configuration?`
544
+ );
545
+ return new Response("Invalid binding", { status: 500 });
546
+ }
547
+ if (bindingValue.toString() !== "[object DurableObjectNamespace]") {
548
+ return new Response("Invalid binding", { status: 500 });
549
+ }
550
+ const namespace = bindingValue;
551
+ if (request.method === "POST" && basePattern.test(url)) {
552
+ const acceptHeader = request.headers.get("accept");
553
+ if (!acceptHeader?.includes("application/json") || !acceptHeader.includes("text/event-stream")) {
554
+ const body2 = JSON.stringify({
555
+ jsonrpc: "2.0",
556
+ error: {
557
+ code: -32e3,
558
+ message: "Not Acceptable: Client must accept both application/json and text/event-stream"
559
+ },
560
+ id: null
561
+ });
562
+ return new Response(body2, { status: 406 });
563
+ }
564
+ const ct = request.headers.get("content-type");
565
+ if (!ct || !ct.includes("application/json")) {
566
+ const body2 = JSON.stringify({
567
+ jsonrpc: "2.0",
568
+ error: {
569
+ code: -32e3,
570
+ message: "Unsupported Media Type: Content-Type must be application/json"
571
+ },
572
+ id: null
573
+ });
574
+ return new Response(body2, { status: 415 });
575
+ }
576
+ const contentLength = Number.parseInt(
577
+ request.headers.get("content-length") ?? "0",
578
+ 10
579
+ );
580
+ if (contentLength > MAXIMUM_MESSAGE_SIZE_BYTES) {
581
+ const body2 = JSON.stringify({
582
+ jsonrpc: "2.0",
583
+ error: {
584
+ code: -32e3,
585
+ message: `Request body too large. Maximum size is ${MAXIMUM_MESSAGE_SIZE_BYTES} bytes`
586
+ },
587
+ id: null
588
+ });
589
+ return new Response(body2, { status: 413 });
590
+ }
591
+ let sessionId = request.headers.get("mcp-session-id");
592
+ let rawMessage;
593
+ try {
594
+ rawMessage = await request.json();
595
+ } catch (error) {
596
+ const body2 = JSON.stringify({
597
+ jsonrpc: "2.0",
598
+ error: {
599
+ code: -32700,
600
+ message: "Parse error: Invalid JSON"
601
+ },
602
+ id: null
603
+ });
604
+ return new Response(body2, { status: 400 });
605
+ }
606
+ let arrayMessage;
607
+ if (Array.isArray(rawMessage)) {
608
+ arrayMessage = rawMessage;
609
+ } else {
610
+ arrayMessage = [rawMessage];
611
+ }
612
+ let messages = [];
613
+ for (const msg of arrayMessage) {
614
+ if (!JSONRPCMessageSchema.safeParse(msg).success) {
615
+ const body2 = JSON.stringify({
616
+ jsonrpc: "2.0",
617
+ error: {
618
+ code: -32700,
619
+ message: "Parse error: Invalid JSON-RPC message"
620
+ },
621
+ id: null
622
+ });
623
+ return new Response(body2, { status: 400 });
624
+ }
625
+ }
626
+ messages = arrayMessage.map((msg) => JSONRPCMessageSchema.parse(msg));
627
+ const isInitializationRequest = messages.some(
628
+ (msg) => InitializeRequestSchema.safeParse(msg).success
629
+ );
630
+ if (isInitializationRequest && sessionId) {
631
+ const body2 = JSON.stringify({
632
+ jsonrpc: "2.0",
633
+ error: {
634
+ code: -32600,
635
+ message: "Invalid Request: Initialization requests must not include a sessionId"
636
+ },
637
+ id: null
638
+ });
639
+ return new Response(body2, { status: 400 });
640
+ }
641
+ if (isInitializationRequest && messages.length > 1) {
642
+ const body2 = JSON.stringify({
643
+ jsonrpc: "2.0",
644
+ error: {
645
+ code: -32600,
646
+ message: "Invalid Request: Only one initialization request is allowed"
647
+ },
648
+ id: null
649
+ });
650
+ return new Response(body2, { status: 400 });
651
+ }
652
+ if (!isInitializationRequest && !sessionId) {
653
+ const body2 = JSON.stringify({
654
+ jsonrpc: "2.0",
655
+ error: {
656
+ code: -32e3,
657
+ message: "Bad Request: Mcp-Session-Id header is required"
658
+ },
659
+ id: null
660
+ });
661
+ return new Response(body2, { status: 400 });
662
+ }
663
+ sessionId = sessionId ?? namespace.newUniqueId().toString();
664
+ const id = namespace.idFromName(`streamable-http:${sessionId}`);
665
+ const doStub = namespace.get(id);
666
+ const isInitialized = await doStub.isInitialized();
667
+ if (isInitializationRequest) {
668
+ await doStub._init(ctx.props);
669
+ await doStub.setInitialized();
670
+ } else if (!isInitialized) {
671
+ const body2 = JSON.stringify({
672
+ jsonrpc: "2.0",
673
+ error: {
674
+ code: -32001,
675
+ message: "Session not found"
676
+ },
677
+ id: null
678
+ });
679
+ return new Response(body2, { status: 404 });
680
+ }
681
+ const { readable, writable } = new TransformStream();
682
+ const writer = writable.getWriter();
683
+ const encoder = new TextEncoder();
684
+ const upgradeUrl = new URL(request.url);
685
+ upgradeUrl.pathname = "/streamable-http";
686
+ const response = await doStub.fetch(
687
+ new Request(upgradeUrl, {
688
+ headers: {
689
+ Upgrade: "websocket",
690
+ // Required by PartyServer
691
+ "x-partykit-room": sessionId
692
+ }
693
+ })
694
+ );
695
+ const ws = response.webSocket;
696
+ if (!ws) {
697
+ console.error("Failed to establish WebSocket connection");
698
+ await writer.close();
699
+ const body2 = JSON.stringify({
700
+ jsonrpc: "2.0",
701
+ error: {
702
+ code: -32001,
703
+ message: "Failed to establish WebSocket connection"
704
+ },
705
+ id: null
706
+ });
707
+ return new Response(body2, { status: 500 });
708
+ }
709
+ const requestIds = /* @__PURE__ */ new Set();
710
+ ws.accept();
711
+ ws.addEventListener("message", (event) => {
712
+ async function onMessage(event2) {
713
+ try {
714
+ const data = typeof event2.data === "string" ? event2.data : new TextDecoder().decode(event2.data);
715
+ const message = JSON.parse(data);
716
+ const result = JSONRPCMessageSchema.safeParse(message);
717
+ if (!result.success) {
718
+ return;
719
+ }
720
+ if (isJSONRPCResponse(result.data) || isJSONRPCError(result.data)) {
721
+ requestIds.delete(result.data.id);
722
+ }
723
+ const messageText = `event: message
724
+ data: ${JSON.stringify(result.data)}
725
+
726
+ `;
727
+ await writer.write(encoder.encode(messageText));
728
+ if (requestIds.size === 0) {
729
+ ws.close();
730
+ }
731
+ } catch (error) {
732
+ console.error("Error forwarding message to SSE:", error);
733
+ }
734
+ }
735
+ onMessage(event).catch(console.error);
736
+ });
737
+ ws.addEventListener("error", (error) => {
738
+ async function onError(error2) {
739
+ try {
740
+ await writer.close();
741
+ } catch (e) {
742
+ }
743
+ }
744
+ onError(error).catch(console.error);
745
+ });
746
+ ws.addEventListener("close", () => {
747
+ async function onClose() {
748
+ try {
749
+ await writer.close();
750
+ } catch (error) {
751
+ console.error("Error closing SSE connection:", error);
752
+ }
753
+ }
754
+ onClose().catch(console.error);
755
+ });
756
+ const hasOnlyNotificationsOrResponses = messages.every(
757
+ (msg) => isJSONRPCNotification(msg) || isJSONRPCResponse(msg)
758
+ );
759
+ if (hasOnlyNotificationsOrResponses) {
760
+ for (const message of messages) {
761
+ ws.send(JSON.stringify(message));
762
+ }
763
+ ws.close();
764
+ return new Response(null, {
765
+ status: 202,
766
+ headers: corsHeaders(request, corsOptions)
767
+ });
768
+ }
769
+ for (const message of messages) {
770
+ if (isJSONRPCRequest(message)) {
771
+ requestIds.add(message.id);
772
+ }
773
+ ws.send(JSON.stringify(message));
774
+ }
775
+ return new Response(readable, {
776
+ headers: {
777
+ "Content-Type": "text/event-stream",
778
+ "Cache-Control": "no-cache",
779
+ Connection: "keep-alive",
780
+ "mcp-session-id": sessionId,
781
+ ...corsHeaders(request, corsOptions)
782
+ },
783
+ status: 200
784
+ });
785
+ }
786
+ const body = JSON.stringify({
787
+ jsonrpc: "2.0",
788
+ error: {
789
+ code: -32e3,
790
+ message: "Method not allowed"
791
+ },
792
+ id: null
793
+ });
794
+ return new Response(body, { status: 405 });
795
+ }
796
+ };
797
+ }
327
798
  };
328
799
  _status = new WeakMap();
329
800
  _transport = new WeakMap();
330
- _connected = new WeakMap();
801
+ _transportType = new WeakMap();
802
+ _requestIdToConnectionId = new WeakMap();
331
803
  _agent = new WeakMap();
332
804
  _McpAgent_instances = new WeakSet();
333
805
  initialize_fn = async function() {
@@ -337,6 +809,7 @@ initialize_fn = async function() {
337
809
  __privateSet(this, _status, "started");
338
810
  });
339
811
  };
812
+ var McpAgent = _McpAgent;
340
813
  export {
341
814
  McpAgent
342
815
  };