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