agents 0.0.0-9069759 → 0.0.0-931d633
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/ai-chat-agent.d.ts +51 -4
- package/dist/ai-chat-agent.js +179 -78
- package/dist/ai-chat-agent.js.map +1 -1
- package/dist/ai-react.d.ts +17 -4
- package/dist/ai-react.js +62 -48
- package/dist/ai-react.js.map +1 -1
- package/dist/ai-types.d.ts +5 -0
- package/dist/chunk-767EASBA.js +106 -0
- package/dist/chunk-767EASBA.js.map +1 -0
- package/dist/chunk-E3LCYPCB.js +469 -0
- package/dist/chunk-E3LCYPCB.js.map +1 -0
- package/dist/chunk-JFRK72K3.js +910 -0
- package/dist/chunk-JFRK72K3.js.map +1 -0
- package/dist/chunk-NKZZ66QY.js +116 -0
- package/dist/chunk-NKZZ66QY.js.map +1 -0
- package/dist/client.d.ts +15 -1
- package/dist/client.js +6 -133
- package/dist/client.js.map +1 -1
- package/dist/index-CITGJflw.d.ts +486 -0
- package/dist/index.d.ts +29 -300
- package/dist/index.js +6 -4
- package/dist/mcp/client.d.ts +1055 -0
- package/dist/mcp/client.js +9 -0
- package/dist/mcp/do-oauth-client-provider.d.ts +41 -0
- package/dist/mcp/do-oauth-client-provider.js +7 -0
- package/dist/mcp/do-oauth-client-provider.js.map +1 -0
- package/dist/mcp/index.d.ts +84 -0
- package/dist/mcp/index.js +783 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/observability/index.d.ts +12 -0
- package/dist/observability/index.js +10 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/react.d.ts +85 -5
- package/dist/react.js +50 -31
- 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 +83 -51
- package/src/index.ts +631 -154
- package/dist/chunk-HMLY7DHA.js +0 -16
- package/dist/chunk-X6BBKLSC.js +0 -568
- package/dist/chunk-X6BBKLSC.js.map +0 -1
- package/dist/mcp.d.ts +0 -58
- package/dist/mcp.js +0 -945
- package/dist/mcp.js.map +0 -1
- /package/dist/{chunk-HMLY7DHA.js.map → mcp/client.js.map} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
Prompt,
|
|
7
|
+
Resource,
|
|
8
|
+
ServerCapabilities,
|
|
9
|
+
Tool,
|
|
10
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { parseCronExpression } from "cron-schedule";
|
|
12
|
+
import { nanoid } from "nanoid";
|
|
1
13
|
import {
|
|
2
|
-
Server,
|
|
3
|
-
routePartykitRequest,
|
|
4
|
-
type PartyServerOptions,
|
|
5
|
-
getServerByName,
|
|
6
14
|
type Connection,
|
|
7
15
|
type ConnectionContext,
|
|
16
|
+
getServerByName,
|
|
17
|
+
type PartyServerOptions,
|
|
18
|
+
routePartykitRequest,
|
|
19
|
+
Server,
|
|
8
20
|
type WSMessage,
|
|
9
21
|
} from "partyserver";
|
|
22
|
+
import { camelCaseToKebabCase } from "./client";
|
|
23
|
+
import { MCPClientManager } from "./mcp/client";
|
|
24
|
+
// import type { MCPClientConnection } from "./mcp/client-connection";
|
|
25
|
+
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
|
|
26
|
+
import { genericObservability, type Observability } from "./observability";
|
|
10
27
|
|
|
11
|
-
|
|
12
|
-
import { nanoid } from "nanoid";
|
|
13
|
-
|
|
14
|
-
export type { Connection, WSMessage, ConnectionContext } from "partyserver";
|
|
15
|
-
|
|
16
|
-
import { WorkflowEntrypoint as CFWorkflowEntrypoint } from "cloudflare:workers";
|
|
28
|
+
export type { Connection, ConnectionContext, WSMessage } from "partyserver";
|
|
17
29
|
|
|
18
30
|
/**
|
|
19
31
|
* RPC request message from client
|
|
@@ -97,7 +109,6 @@ export type CallableMetadata = {
|
|
|
97
109
|
streaming?: boolean;
|
|
98
110
|
};
|
|
99
111
|
|
|
100
|
-
// biome-ignore lint/complexity/noBannedTypes: <explanation>
|
|
101
112
|
const callableMetadata = new Map<Function, CallableMetadata>();
|
|
102
113
|
|
|
103
114
|
/**
|
|
@@ -107,6 +118,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
|
|
|
107
118
|
export function unstable_callable(metadata: CallableMetadata = {}) {
|
|
108
119
|
return function callableDecorator<This, Args extends unknown[], Return>(
|
|
109
120
|
target: (this: This, ...args: Args) => Return,
|
|
121
|
+
// biome-ignore lint/correctness/noUnusedFunctionParameters: later
|
|
110
122
|
context: ClassMethodDecoratorContext
|
|
111
123
|
) {
|
|
112
124
|
if (!callableMetadata.has(target)) {
|
|
@@ -117,11 +129,6 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
|
|
|
117
129
|
};
|
|
118
130
|
}
|
|
119
131
|
|
|
120
|
-
/**
|
|
121
|
-
* A class for creating workflow entry points that can be used with Cloudflare Workers
|
|
122
|
-
*/
|
|
123
|
-
export class WorkflowEntrypoint extends CFWorkflowEntrypoint {}
|
|
124
|
-
|
|
125
132
|
/**
|
|
126
133
|
* Represents a scheduled task within an Agent
|
|
127
134
|
* @template T Type of the payload data
|
|
@@ -163,18 +170,95 @@ function getNextCronTime(cron: string) {
|
|
|
163
170
|
return interval.getNextDate();
|
|
164
171
|
}
|
|
165
172
|
|
|
173
|
+
/**
|
|
174
|
+
* MCP Server state update message from server -> Client
|
|
175
|
+
*/
|
|
176
|
+
export type MCPServerMessage = {
|
|
177
|
+
type: "cf_agent_mcp_servers";
|
|
178
|
+
mcp: MCPServersState;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export type MCPServersState = {
|
|
182
|
+
servers: {
|
|
183
|
+
[id: string]: MCPServer;
|
|
184
|
+
};
|
|
185
|
+
tools: Tool[];
|
|
186
|
+
prompts: Prompt[];
|
|
187
|
+
resources: Resource[];
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export type MCPServer = {
|
|
191
|
+
name: string;
|
|
192
|
+
server_url: string;
|
|
193
|
+
auth_url: string | null;
|
|
194
|
+
// This state is specifically about the temporary process of getting a token (if needed).
|
|
195
|
+
// Scope outside of that can't be relied upon because when the DO sleeps, there's no way
|
|
196
|
+
// to communicate a change to a non-ready state.
|
|
197
|
+
state: "authenticating" | "connecting" | "ready" | "discovering" | "failed";
|
|
198
|
+
instructions: string | null;
|
|
199
|
+
capabilities: ServerCapabilities | null;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* MCP Server data stored in DO SQL for resuming MCP Server connections
|
|
204
|
+
*/
|
|
205
|
+
type MCPServerRow = {
|
|
206
|
+
id: string;
|
|
207
|
+
name: string;
|
|
208
|
+
server_url: string;
|
|
209
|
+
client_id: string | null;
|
|
210
|
+
auth_url: string | null;
|
|
211
|
+
callback_url: string;
|
|
212
|
+
server_options: string;
|
|
213
|
+
};
|
|
214
|
+
|
|
166
215
|
const STATE_ROW_ID = "cf_state_row_id";
|
|
167
216
|
const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
168
217
|
|
|
169
218
|
const DEFAULT_STATE = {} as unknown;
|
|
170
219
|
|
|
220
|
+
const agentContext = new AsyncLocalStorage<{
|
|
221
|
+
agent: Agent<unknown>;
|
|
222
|
+
connection: Connection | undefined;
|
|
223
|
+
request: Request | undefined;
|
|
224
|
+
}>();
|
|
225
|
+
|
|
226
|
+
export function getCurrentAgent<
|
|
227
|
+
T extends Agent<unknown, unknown> = Agent<unknown, unknown>,
|
|
228
|
+
>(): {
|
|
229
|
+
agent: T | undefined;
|
|
230
|
+
connection: Connection | undefined;
|
|
231
|
+
request: Request<unknown, CfProperties<unknown>> | undefined;
|
|
232
|
+
} {
|
|
233
|
+
const store = agentContext.getStore() as
|
|
234
|
+
| {
|
|
235
|
+
agent: T;
|
|
236
|
+
connection: Connection | undefined;
|
|
237
|
+
request: Request<unknown, CfProperties<unknown>> | undefined;
|
|
238
|
+
}
|
|
239
|
+
| undefined;
|
|
240
|
+
if (!store) {
|
|
241
|
+
return {
|
|
242
|
+
agent: undefined,
|
|
243
|
+
connection: undefined,
|
|
244
|
+
request: undefined,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return store;
|
|
248
|
+
}
|
|
249
|
+
|
|
171
250
|
/**
|
|
172
251
|
* Base class for creating Agent implementations
|
|
173
252
|
* @template Env Environment type containing bindings
|
|
174
253
|
* @template State State type to store within the Agent
|
|
175
254
|
*/
|
|
176
255
|
export class Agent<Env, State = unknown> extends Server<Env> {
|
|
177
|
-
|
|
256
|
+
private _state = DEFAULT_STATE as State;
|
|
257
|
+
|
|
258
|
+
private _ParentClass: typeof Agent<Env, State> =
|
|
259
|
+
Object.getPrototypeOf(this).constructor;
|
|
260
|
+
|
|
261
|
+
mcp: MCPClientManager = new MCPClientManager(this._ParentClass.name, "0.0.1");
|
|
178
262
|
|
|
179
263
|
/**
|
|
180
264
|
* Initial state for the Agent
|
|
@@ -186,9 +270,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
186
270
|
* Current state of the Agent
|
|
187
271
|
*/
|
|
188
272
|
get state(): State {
|
|
189
|
-
if (this
|
|
273
|
+
if (this._state !== DEFAULT_STATE) {
|
|
190
274
|
// state was previously set, and populated internal state
|
|
191
|
-
return this
|
|
275
|
+
return this._state;
|
|
192
276
|
}
|
|
193
277
|
// looks like this is the first time the state is being accessed
|
|
194
278
|
// check if the state was set in a previous life
|
|
@@ -208,8 +292,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
208
292
|
) {
|
|
209
293
|
const state = result[0]?.state as string; // could be null?
|
|
210
294
|
|
|
211
|
-
this
|
|
212
|
-
return this
|
|
295
|
+
this._state = JSON.parse(state);
|
|
296
|
+
return this._state;
|
|
213
297
|
}
|
|
214
298
|
|
|
215
299
|
// ok, this is the first time the state is being accessed
|
|
@@ -233,6 +317,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
233
317
|
hibernate: true, // default to hibernate
|
|
234
318
|
};
|
|
235
319
|
|
|
320
|
+
/**
|
|
321
|
+
* The observability implementation to use for the Agent
|
|
322
|
+
*/
|
|
323
|
+
observability?: Observability = genericObservability;
|
|
324
|
+
|
|
236
325
|
/**
|
|
237
326
|
* Execute SQL queries against the Agent's database
|
|
238
327
|
* @template T Type of the returned rows
|
|
@@ -270,7 +359,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
270
359
|
`;
|
|
271
360
|
|
|
272
361
|
void this.ctx.blockConcurrencyWhile(async () => {
|
|
273
|
-
return this
|
|
362
|
+
return this._tryCatch(async () => {
|
|
274
363
|
// Create alarms table if it doesn't exist
|
|
275
364
|
this.sql`
|
|
276
365
|
CREATE TABLE IF NOT EXISTS cf_agents_schedules (
|
|
@@ -290,96 +379,225 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
290
379
|
});
|
|
291
380
|
});
|
|
292
381
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return this.#tryCatch(() => _onMessage(connection, message));
|
|
305
|
-
}
|
|
382
|
+
this.sql`
|
|
383
|
+
CREATE TABLE IF NOT EXISTS cf_agents_mcp_servers (
|
|
384
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
385
|
+
name TEXT NOT NULL,
|
|
386
|
+
server_url TEXT NOT NULL,
|
|
387
|
+
callback_url TEXT NOT NULL,
|
|
388
|
+
client_id TEXT,
|
|
389
|
+
auth_url TEXT,
|
|
390
|
+
server_options TEXT
|
|
391
|
+
)
|
|
392
|
+
`;
|
|
306
393
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
394
|
+
const _onRequest = this.onRequest.bind(this);
|
|
395
|
+
this.onRequest = (request: Request) => {
|
|
396
|
+
return agentContext.run(
|
|
397
|
+
{ agent: this, connection: undefined, request },
|
|
398
|
+
async () => {
|
|
399
|
+
if (this.mcp.isCallbackRequest(request)) {
|
|
400
|
+
await this.mcp.handleCallbackRequest(request);
|
|
401
|
+
|
|
402
|
+
// after the MCP connection handshake, we can send updated mcp state
|
|
403
|
+
this.broadcast(
|
|
404
|
+
JSON.stringify({
|
|
405
|
+
mcp: this.getMcpServers(),
|
|
406
|
+
type: "cf_agent_mcp_servers",
|
|
407
|
+
})
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
// We probably should let the user configure this response/redirect, but this is fine for now.
|
|
411
|
+
return new Response("<script>window.close();</script>", {
|
|
412
|
+
headers: { "content-type": "text/html" },
|
|
413
|
+
status: 200,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
311
416
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
417
|
+
return this._tryCatch(() => _onRequest(request));
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
};
|
|
315
421
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
422
|
+
const _onMessage = this.onMessage.bind(this);
|
|
423
|
+
this.onMessage = async (connection: Connection, message: WSMessage) => {
|
|
424
|
+
return agentContext.run(
|
|
425
|
+
{ agent: this, connection, request: undefined },
|
|
426
|
+
async () => {
|
|
427
|
+
if (typeof message !== "string") {
|
|
428
|
+
return this._tryCatch(() => _onMessage(connection, message));
|
|
320
429
|
}
|
|
321
430
|
|
|
322
|
-
|
|
323
|
-
|
|
431
|
+
let parsed: unknown;
|
|
432
|
+
try {
|
|
433
|
+
parsed = JSON.parse(message);
|
|
434
|
+
} catch (_e) {
|
|
435
|
+
// silently fail and let the onMessage handler handle it
|
|
436
|
+
return this._tryCatch(() => _onMessage(connection, message));
|
|
324
437
|
}
|
|
325
438
|
|
|
326
|
-
|
|
327
|
-
|
|
439
|
+
if (isStateUpdateMessage(parsed)) {
|
|
440
|
+
this._setStateInternal(parsed.state as State, connection);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
328
443
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
444
|
+
if (isRPCRequest(parsed)) {
|
|
445
|
+
try {
|
|
446
|
+
const { id, method, args } = parsed;
|
|
447
|
+
|
|
448
|
+
// Check if method exists and is callable
|
|
449
|
+
const methodFn = this[method as keyof this];
|
|
450
|
+
if (typeof methodFn !== "function") {
|
|
451
|
+
throw new Error(`Method ${method} does not exist`);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (!this._isCallable(method)) {
|
|
455
|
+
throw new Error(`Method ${method} is not callable`);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const metadata = callableMetadata.get(methodFn as Function);
|
|
459
|
+
|
|
460
|
+
// For streaming methods, pass a StreamingResponse object
|
|
461
|
+
if (metadata?.streaming) {
|
|
462
|
+
const stream = new StreamingResponse(connection, id);
|
|
463
|
+
await methodFn.apply(this, [stream, ...args]);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// For regular methods, execute and send response
|
|
468
|
+
const result = await methodFn.apply(this, args);
|
|
469
|
+
|
|
470
|
+
this.observability?.emit(
|
|
471
|
+
{
|
|
472
|
+
displayMessage: `RPC call to ${method}`,
|
|
473
|
+
id: nanoid(),
|
|
474
|
+
payload: {
|
|
475
|
+
args,
|
|
476
|
+
method,
|
|
477
|
+
streaming: metadata?.streaming,
|
|
478
|
+
success: true,
|
|
479
|
+
},
|
|
480
|
+
timestamp: Date.now(),
|
|
481
|
+
type: "rpc",
|
|
482
|
+
},
|
|
483
|
+
this.ctx
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
const response: RPCResponse = {
|
|
487
|
+
done: true,
|
|
488
|
+
id,
|
|
489
|
+
result,
|
|
490
|
+
success: true,
|
|
491
|
+
type: "rpc",
|
|
492
|
+
};
|
|
493
|
+
connection.send(JSON.stringify(response));
|
|
494
|
+
} catch (e) {
|
|
495
|
+
// Send error response
|
|
496
|
+
const response: RPCResponse = {
|
|
497
|
+
error:
|
|
498
|
+
e instanceof Error ? e.message : "Unknown error occurred",
|
|
499
|
+
id: parsed.id,
|
|
500
|
+
success: false,
|
|
501
|
+
type: "rpc",
|
|
502
|
+
};
|
|
503
|
+
connection.send(JSON.stringify(response));
|
|
504
|
+
console.error("RPC error:", e);
|
|
505
|
+
}
|
|
333
506
|
return;
|
|
334
507
|
}
|
|
335
508
|
|
|
336
|
-
|
|
337
|
-
const result = await methodFn.apply(this, args);
|
|
338
|
-
const response: RPCResponse = {
|
|
339
|
-
type: "rpc",
|
|
340
|
-
id,
|
|
341
|
-
success: true,
|
|
342
|
-
result,
|
|
343
|
-
done: true,
|
|
344
|
-
};
|
|
345
|
-
connection.send(JSON.stringify(response));
|
|
346
|
-
} catch (e) {
|
|
347
|
-
// Send error response
|
|
348
|
-
const response: RPCResponse = {
|
|
349
|
-
type: "rpc",
|
|
350
|
-
id: parsed.id,
|
|
351
|
-
success: false,
|
|
352
|
-
error: e instanceof Error ? e.message : "Unknown error occurred",
|
|
353
|
-
};
|
|
354
|
-
connection.send(JSON.stringify(response));
|
|
355
|
-
console.error("RPC error:", e);
|
|
509
|
+
return this._tryCatch(() => _onMessage(connection, message));
|
|
356
510
|
}
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return this.#tryCatch(() => _onMessage(connection, message));
|
|
511
|
+
);
|
|
361
512
|
};
|
|
362
513
|
|
|
363
514
|
const _onConnect = this.onConnect.bind(this);
|
|
364
515
|
this.onConnect = (connection: Connection, ctx: ConnectionContext) => {
|
|
365
516
|
// TODO: This is a hack to ensure the state is sent after the connection is established
|
|
366
517
|
// must fix this
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
518
|
+
return agentContext.run(
|
|
519
|
+
{ agent: this, connection, request: ctx.request },
|
|
520
|
+
async () => {
|
|
521
|
+
setTimeout(() => {
|
|
522
|
+
if (this.state) {
|
|
523
|
+
connection.send(
|
|
524
|
+
JSON.stringify({
|
|
525
|
+
state: this.state,
|
|
526
|
+
type: "cf_agent_state",
|
|
527
|
+
})
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
connection.send(
|
|
532
|
+
JSON.stringify({
|
|
533
|
+
mcp: this.getMcpServers(),
|
|
534
|
+
type: "cf_agent_mcp_servers",
|
|
535
|
+
})
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
this.observability?.emit(
|
|
539
|
+
{
|
|
540
|
+
displayMessage: "Connection established",
|
|
541
|
+
id: nanoid(),
|
|
542
|
+
payload: {
|
|
543
|
+
connectionId: connection.id,
|
|
544
|
+
},
|
|
545
|
+
timestamp: Date.now(),
|
|
546
|
+
type: "connect",
|
|
547
|
+
},
|
|
548
|
+
this.ctx
|
|
549
|
+
);
|
|
550
|
+
return this._tryCatch(() => _onConnect(connection, ctx));
|
|
551
|
+
}, 20);
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const _onStart = this.onStart.bind(this);
|
|
557
|
+
this.onStart = async () => {
|
|
558
|
+
return agentContext.run(
|
|
559
|
+
{ agent: this, connection: undefined, request: undefined },
|
|
560
|
+
async () => {
|
|
561
|
+
const servers = this.sql<MCPServerRow>`
|
|
562
|
+
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
563
|
+
`;
|
|
564
|
+
|
|
565
|
+
// from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
|
|
566
|
+
Promise.allSettled(
|
|
567
|
+
servers.map((server) => {
|
|
568
|
+
return this._connectToMcpServerInternal(
|
|
569
|
+
server.name,
|
|
570
|
+
server.server_url,
|
|
571
|
+
server.callback_url,
|
|
572
|
+
server.server_options
|
|
573
|
+
? JSON.parse(server.server_options)
|
|
574
|
+
: undefined,
|
|
575
|
+
{
|
|
576
|
+
id: server.id,
|
|
577
|
+
oauthClientId: server.client_id ?? undefined,
|
|
578
|
+
}
|
|
579
|
+
);
|
|
373
580
|
})
|
|
374
|
-
)
|
|
581
|
+
).then((_results) => {
|
|
582
|
+
this.broadcast(
|
|
583
|
+
JSON.stringify({
|
|
584
|
+
mcp: this.getMcpServers(),
|
|
585
|
+
type: "cf_agent_mcp_servers",
|
|
586
|
+
})
|
|
587
|
+
);
|
|
588
|
+
});
|
|
589
|
+
await this._tryCatch(() => _onStart());
|
|
375
590
|
}
|
|
376
|
-
|
|
377
|
-
}, 20);
|
|
591
|
+
);
|
|
378
592
|
};
|
|
379
593
|
}
|
|
380
594
|
|
|
381
|
-
|
|
382
|
-
|
|
595
|
+
private _setStateInternal(
|
|
596
|
+
state: State,
|
|
597
|
+
source: Connection | "server" = "server"
|
|
598
|
+
) {
|
|
599
|
+
const previousState = this._state;
|
|
600
|
+
this._state = state;
|
|
383
601
|
this.sql`
|
|
384
602
|
INSERT OR REPLACE INTO cf_agents_state (id, state)
|
|
385
603
|
VALUES (${STATE_ROW_ID}, ${JSON.stringify(state)})
|
|
@@ -390,12 +608,33 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
390
608
|
`;
|
|
391
609
|
this.broadcast(
|
|
392
610
|
JSON.stringify({
|
|
393
|
-
type: "cf_agent_state",
|
|
394
611
|
state: state,
|
|
612
|
+
type: "cf_agent_state",
|
|
395
613
|
}),
|
|
396
614
|
source !== "server" ? [source.id] : []
|
|
397
615
|
);
|
|
398
|
-
return this
|
|
616
|
+
return this._tryCatch(() => {
|
|
617
|
+
const { connection, request } = agentContext.getStore() || {};
|
|
618
|
+
return agentContext.run(
|
|
619
|
+
{ agent: this, connection, request },
|
|
620
|
+
async () => {
|
|
621
|
+
this.observability?.emit(
|
|
622
|
+
{
|
|
623
|
+
displayMessage: "State updated",
|
|
624
|
+
id: nanoid(),
|
|
625
|
+
payload: {
|
|
626
|
+
previousState,
|
|
627
|
+
state,
|
|
628
|
+
},
|
|
629
|
+
timestamp: Date.now(),
|
|
630
|
+
type: "state:update",
|
|
631
|
+
},
|
|
632
|
+
this.ctx
|
|
633
|
+
);
|
|
634
|
+
return this.onStateUpdate(state, source);
|
|
635
|
+
}
|
|
636
|
+
);
|
|
637
|
+
});
|
|
399
638
|
}
|
|
400
639
|
|
|
401
640
|
/**
|
|
@@ -403,7 +642,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
403
642
|
* @param state New state to set
|
|
404
643
|
*/
|
|
405
644
|
setState(state: State) {
|
|
406
|
-
this
|
|
645
|
+
this._setStateInternal(state, "server");
|
|
407
646
|
}
|
|
408
647
|
|
|
409
648
|
/**
|
|
@@ -411,6 +650,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
411
650
|
* @param state Updated state
|
|
412
651
|
* @param source Source of the state update ("server" or a client connection)
|
|
413
652
|
*/
|
|
653
|
+
// biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
|
|
414
654
|
onStateUpdate(state: State | undefined, source: Connection | "server") {
|
|
415
655
|
// override this to handle state updates
|
|
416
656
|
}
|
|
@@ -419,11 +659,17 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
419
659
|
* Called when the Agent receives an email
|
|
420
660
|
* @param email Email message to process
|
|
421
661
|
*/
|
|
662
|
+
// biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
|
|
422
663
|
onEmail(email: ForwardableEmailMessage) {
|
|
423
|
-
|
|
664
|
+
return agentContext.run(
|
|
665
|
+
{ agent: this, connection: undefined, request: undefined },
|
|
666
|
+
async () => {
|
|
667
|
+
console.error("onEmail not implemented");
|
|
668
|
+
}
|
|
669
|
+
);
|
|
424
670
|
}
|
|
425
671
|
|
|
426
|
-
async
|
|
672
|
+
private async _tryCatch<T>(fn: () => T | Promise<T>) {
|
|
427
673
|
try {
|
|
428
674
|
return await fn();
|
|
429
675
|
} catch (e) {
|
|
@@ -480,6 +726,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
480
726
|
): Promise<Schedule<T>> {
|
|
481
727
|
const id = nanoid(9);
|
|
482
728
|
|
|
729
|
+
const emitScheduleCreate = (schedule: Schedule<T>) =>
|
|
730
|
+
this.observability?.emit(
|
|
731
|
+
{
|
|
732
|
+
displayMessage: `Schedule ${schedule.id} created`,
|
|
733
|
+
id: nanoid(),
|
|
734
|
+
payload: schedule,
|
|
735
|
+
timestamp: Date.now(),
|
|
736
|
+
type: "schedule:create",
|
|
737
|
+
},
|
|
738
|
+
this.ctx
|
|
739
|
+
);
|
|
740
|
+
|
|
483
741
|
if (typeof callback !== "string") {
|
|
484
742
|
throw new Error("Callback must be a string");
|
|
485
743
|
}
|
|
@@ -497,15 +755,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
497
755
|
)}, 'scheduled', ${timestamp})
|
|
498
756
|
`;
|
|
499
757
|
|
|
500
|
-
await this
|
|
758
|
+
await this._scheduleNextAlarm();
|
|
501
759
|
|
|
502
|
-
|
|
503
|
-
id,
|
|
760
|
+
const schedule: Schedule<T> = {
|
|
504
761
|
callback: callback,
|
|
762
|
+
id,
|
|
505
763
|
payload: payload as T,
|
|
506
764
|
time: timestamp,
|
|
507
765
|
type: "scheduled",
|
|
508
766
|
};
|
|
767
|
+
|
|
768
|
+
emitScheduleCreate(schedule);
|
|
769
|
+
|
|
770
|
+
return schedule;
|
|
509
771
|
}
|
|
510
772
|
if (typeof when === "number") {
|
|
511
773
|
const time = new Date(Date.now() + when * 1000);
|
|
@@ -518,16 +780,20 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
518
780
|
)}, 'delayed', ${when}, ${timestamp})
|
|
519
781
|
`;
|
|
520
782
|
|
|
521
|
-
await this
|
|
783
|
+
await this._scheduleNextAlarm();
|
|
522
784
|
|
|
523
|
-
|
|
524
|
-
id,
|
|
785
|
+
const schedule: Schedule<T> = {
|
|
525
786
|
callback: callback,
|
|
526
|
-
payload: payload as T,
|
|
527
787
|
delayInSeconds: when,
|
|
788
|
+
id,
|
|
789
|
+
payload: payload as T,
|
|
528
790
|
time: timestamp,
|
|
529
791
|
type: "delayed",
|
|
530
792
|
};
|
|
793
|
+
|
|
794
|
+
emitScheduleCreate(schedule);
|
|
795
|
+
|
|
796
|
+
return schedule;
|
|
531
797
|
}
|
|
532
798
|
if (typeof when === "string") {
|
|
533
799
|
const nextExecutionTime = getNextCronTime(when);
|
|
@@ -540,16 +806,20 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
540
806
|
)}, 'cron', ${when}, ${timestamp})
|
|
541
807
|
`;
|
|
542
808
|
|
|
543
|
-
await this
|
|
809
|
+
await this._scheduleNextAlarm();
|
|
544
810
|
|
|
545
|
-
|
|
546
|
-
id,
|
|
811
|
+
const schedule: Schedule<T> = {
|
|
547
812
|
callback: callback,
|
|
548
|
-
payload: payload as T,
|
|
549
813
|
cron: when,
|
|
814
|
+
id,
|
|
815
|
+
payload: payload as T,
|
|
550
816
|
time: timestamp,
|
|
551
817
|
type: "cron",
|
|
552
818
|
};
|
|
819
|
+
|
|
820
|
+
emitScheduleCreate(schedule);
|
|
821
|
+
|
|
822
|
+
return schedule;
|
|
553
823
|
}
|
|
554
824
|
throw new Error("Invalid schedule type");
|
|
555
825
|
}
|
|
@@ -580,7 +850,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
580
850
|
*/
|
|
581
851
|
getSchedules<T = string>(
|
|
582
852
|
criteria: {
|
|
583
|
-
description?: string;
|
|
584
853
|
id?: string;
|
|
585
854
|
type?: "scheduled" | "delayed" | "cron";
|
|
586
855
|
timeRange?: { start?: Date; end?: Date };
|
|
@@ -594,11 +863,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
594
863
|
params.push(criteria.id);
|
|
595
864
|
}
|
|
596
865
|
|
|
597
|
-
if (criteria.description) {
|
|
598
|
-
query += " AND description = ?";
|
|
599
|
-
params.push(criteria.description);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
866
|
if (criteria.type) {
|
|
603
867
|
query += " AND type = ?";
|
|
604
868
|
params.push(criteria.type);
|
|
@@ -631,13 +895,26 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
631
895
|
* @returns true if the task was cancelled, false otherwise
|
|
632
896
|
*/
|
|
633
897
|
async cancelSchedule(id: string): Promise<boolean> {
|
|
898
|
+
const schedule = await this.getSchedule(id);
|
|
899
|
+
if (schedule) {
|
|
900
|
+
this.observability?.emit(
|
|
901
|
+
{
|
|
902
|
+
displayMessage: `Schedule ${id} cancelled`,
|
|
903
|
+
id: nanoid(),
|
|
904
|
+
payload: schedule,
|
|
905
|
+
timestamp: Date.now(),
|
|
906
|
+
type: "schedule:cancel",
|
|
907
|
+
},
|
|
908
|
+
this.ctx
|
|
909
|
+
);
|
|
910
|
+
}
|
|
634
911
|
this.sql`DELETE FROM cf_agents_schedules WHERE id = ${id}`;
|
|
635
912
|
|
|
636
|
-
await this
|
|
913
|
+
await this._scheduleNextAlarm();
|
|
637
914
|
return true;
|
|
638
915
|
}
|
|
639
916
|
|
|
640
|
-
async
|
|
917
|
+
private async _scheduleNextAlarm() {
|
|
641
918
|
// Find the next schedule that needs to be executed
|
|
642
919
|
const result = this.sql`
|
|
643
920
|
SELECT time FROM cf_agents_schedules
|
|
@@ -654,10 +931,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
654
931
|
}
|
|
655
932
|
|
|
656
933
|
/**
|
|
657
|
-
* Method called when an alarm fires
|
|
658
|
-
* Executes any scheduled tasks that are due
|
|
934
|
+
* Method called when an alarm fires.
|
|
935
|
+
* Executes any scheduled tasks that are due.
|
|
936
|
+
*
|
|
937
|
+
* @remarks
|
|
938
|
+
* To schedule a task, please use the `this.schedule` method instead.
|
|
939
|
+
* See {@link https://developers.cloudflare.com/agents/api-reference/schedule-tasks/}
|
|
659
940
|
*/
|
|
660
|
-
async
|
|
941
|
+
public readonly alarm = async () => {
|
|
661
942
|
const now = Math.floor(Date.now() / 1000);
|
|
662
943
|
|
|
663
944
|
// Get all schedules that should be executed now
|
|
@@ -671,16 +952,32 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
671
952
|
console.error(`callback ${row.callback} not found`);
|
|
672
953
|
continue;
|
|
673
954
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
955
|
+
await agentContext.run(
|
|
956
|
+
{ agent: this, connection: undefined, request: undefined },
|
|
957
|
+
async () => {
|
|
958
|
+
try {
|
|
959
|
+
this.observability?.emit(
|
|
960
|
+
{
|
|
961
|
+
displayMessage: `Schedule ${row.id} executed`,
|
|
962
|
+
id: nanoid(),
|
|
963
|
+
payload: row,
|
|
964
|
+
timestamp: Date.now(),
|
|
965
|
+
type: "schedule:execute",
|
|
966
|
+
},
|
|
967
|
+
this.ctx
|
|
968
|
+
);
|
|
969
|
+
|
|
970
|
+
await (
|
|
971
|
+
callback as (
|
|
972
|
+
payload: unknown,
|
|
973
|
+
schedule: Schedule<unknown>
|
|
974
|
+
) => Promise<void>
|
|
975
|
+
).bind(this)(JSON.parse(row.payload as string), row);
|
|
976
|
+
} catch (e) {
|
|
977
|
+
console.error(`error executing callback "${row.callback}"`, e);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
);
|
|
684
981
|
if (row.type === "cron") {
|
|
685
982
|
// Update next execution time for cron schedules
|
|
686
983
|
const nextExecutionTime = getNextCronTime(row.cron);
|
|
@@ -698,8 +995,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
698
995
|
}
|
|
699
996
|
|
|
700
997
|
// Schedule the next alarm
|
|
701
|
-
await this
|
|
702
|
-
}
|
|
998
|
+
await this._scheduleNextAlarm();
|
|
999
|
+
};
|
|
703
1000
|
|
|
704
1001
|
/**
|
|
705
1002
|
* Destroy the Agent, removing all state and scheduled tasks
|
|
@@ -708,20 +1005,200 @@ export class Agent<Env, State = unknown> extends Server<Env> {
|
|
|
708
1005
|
// drop all tables
|
|
709
1006
|
this.sql`DROP TABLE IF EXISTS cf_agents_state`;
|
|
710
1007
|
this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
|
|
1008
|
+
this.sql`DROP TABLE IF EXISTS cf_agents_mcp_servers`;
|
|
711
1009
|
|
|
712
1010
|
// delete all alarms
|
|
713
1011
|
await this.ctx.storage.deleteAlarm();
|
|
714
1012
|
await this.ctx.storage.deleteAll();
|
|
1013
|
+
this.ctx.abort("destroyed"); // enforce that the agent is evicted
|
|
1014
|
+
|
|
1015
|
+
this.observability?.emit(
|
|
1016
|
+
{
|
|
1017
|
+
displayMessage: "Agent destroyed",
|
|
1018
|
+
id: nanoid(),
|
|
1019
|
+
payload: {},
|
|
1020
|
+
timestamp: Date.now(),
|
|
1021
|
+
type: "destroy",
|
|
1022
|
+
},
|
|
1023
|
+
this.ctx
|
|
1024
|
+
);
|
|
715
1025
|
}
|
|
716
1026
|
|
|
717
1027
|
/**
|
|
718
1028
|
* Get all methods marked as callable on this Agent
|
|
719
1029
|
* @returns A map of method names to their metadata
|
|
720
1030
|
*/
|
|
721
|
-
|
|
722
|
-
// biome-ignore lint/complexity/noBannedTypes: <explanation>
|
|
1031
|
+
private _isCallable(method: string): boolean {
|
|
723
1032
|
return callableMetadata.has(this[method as keyof this] as Function);
|
|
724
1033
|
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Connect to a new MCP Server
|
|
1037
|
+
*
|
|
1038
|
+
* @param url MCP Server SSE URL
|
|
1039
|
+
* @param callbackHost Base host for the agent, used for the redirect URI.
|
|
1040
|
+
* @param agentsPrefix agents routing prefix if not using `agents`
|
|
1041
|
+
* @param options MCP client and transport (header) options
|
|
1042
|
+
* @returns authUrl
|
|
1043
|
+
*/
|
|
1044
|
+
async addMcpServer(
|
|
1045
|
+
serverName: string,
|
|
1046
|
+
url: string,
|
|
1047
|
+
callbackHost: string,
|
|
1048
|
+
agentsPrefix = "agents",
|
|
1049
|
+
options?: {
|
|
1050
|
+
client?: ConstructorParameters<typeof Client>[1];
|
|
1051
|
+
transport?: {
|
|
1052
|
+
headers: HeadersInit;
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
): Promise<{ id: string; authUrl: string | undefined }> {
|
|
1056
|
+
const callbackUrl = `${callbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
|
|
1057
|
+
|
|
1058
|
+
const result = await this._connectToMcpServerInternal(
|
|
1059
|
+
serverName,
|
|
1060
|
+
url,
|
|
1061
|
+
callbackUrl,
|
|
1062
|
+
options
|
|
1063
|
+
);
|
|
1064
|
+
this.sql`
|
|
1065
|
+
INSERT
|
|
1066
|
+
OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
|
|
1067
|
+
VALUES (
|
|
1068
|
+
${result.id},
|
|
1069
|
+
${serverName},
|
|
1070
|
+
${url},
|
|
1071
|
+
${result.clientId ?? null},
|
|
1072
|
+
${result.authUrl ?? null},
|
|
1073
|
+
${callbackUrl},
|
|
1074
|
+
${options ? JSON.stringify(options) : null}
|
|
1075
|
+
);
|
|
1076
|
+
`;
|
|
1077
|
+
|
|
1078
|
+
this.broadcast(
|
|
1079
|
+
JSON.stringify({
|
|
1080
|
+
mcp: this.getMcpServers(),
|
|
1081
|
+
type: "cf_agent_mcp_servers",
|
|
1082
|
+
})
|
|
1083
|
+
);
|
|
1084
|
+
|
|
1085
|
+
return result;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
async _connectToMcpServerInternal(
|
|
1089
|
+
_serverName: string,
|
|
1090
|
+
url: string,
|
|
1091
|
+
callbackUrl: string,
|
|
1092
|
+
// it's important that any options here are serializable because we put them into our sqlite DB for reconnection purposes
|
|
1093
|
+
options?: {
|
|
1094
|
+
client?: ConstructorParameters<typeof Client>[1];
|
|
1095
|
+
/**
|
|
1096
|
+
* We don't expose the normal set of transport options because:
|
|
1097
|
+
* 1) we can't serialize things like the auth provider or a fetch function into the DB for reconnection purposes
|
|
1098
|
+
* 2) We probably want these options to be agnostic to the transport type (SSE vs Streamable)
|
|
1099
|
+
*
|
|
1100
|
+
* This has the limitation that you can't override fetch, but I think headers should handle nearly all cases needed (i.e. non-standard bearer auth).
|
|
1101
|
+
*/
|
|
1102
|
+
transport?: {
|
|
1103
|
+
headers?: HeadersInit;
|
|
1104
|
+
};
|
|
1105
|
+
},
|
|
1106
|
+
reconnect?: {
|
|
1107
|
+
id: string;
|
|
1108
|
+
oauthClientId?: string;
|
|
1109
|
+
}
|
|
1110
|
+
): Promise<{
|
|
1111
|
+
id: string;
|
|
1112
|
+
authUrl: string | undefined;
|
|
1113
|
+
clientId: string | undefined;
|
|
1114
|
+
}> {
|
|
1115
|
+
const authProvider = new DurableObjectOAuthClientProvider(
|
|
1116
|
+
this.ctx.storage,
|
|
1117
|
+
this.name,
|
|
1118
|
+
callbackUrl
|
|
1119
|
+
);
|
|
1120
|
+
|
|
1121
|
+
if (reconnect) {
|
|
1122
|
+
authProvider.serverId = reconnect.id;
|
|
1123
|
+
if (reconnect.oauthClientId) {
|
|
1124
|
+
authProvider.clientId = reconnect.oauthClientId;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// allows passing through transport headers if necessary
|
|
1129
|
+
// this handles some non-standard bearer auth setups (i.e. MCP server behind CF access instead of OAuth)
|
|
1130
|
+
let headerTransportOpts: SSEClientTransportOptions = {};
|
|
1131
|
+
if (options?.transport?.headers) {
|
|
1132
|
+
headerTransportOpts = {
|
|
1133
|
+
eventSourceInit: {
|
|
1134
|
+
fetch: (url, init) =>
|
|
1135
|
+
fetch(url, {
|
|
1136
|
+
...init,
|
|
1137
|
+
headers: options?.transport?.headers,
|
|
1138
|
+
}),
|
|
1139
|
+
},
|
|
1140
|
+
requestInit: {
|
|
1141
|
+
headers: options?.transport?.headers,
|
|
1142
|
+
},
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const { id, authUrl, clientId } = await this.mcp.connect(url, {
|
|
1147
|
+
client: options?.client,
|
|
1148
|
+
reconnect,
|
|
1149
|
+
transport: {
|
|
1150
|
+
...headerTransportOpts,
|
|
1151
|
+
authProvider,
|
|
1152
|
+
},
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
return {
|
|
1156
|
+
authUrl,
|
|
1157
|
+
clientId,
|
|
1158
|
+
id,
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
async removeMcpServer(id: string) {
|
|
1163
|
+
this.mcp.closeConnection(id);
|
|
1164
|
+
this.sql`
|
|
1165
|
+
DELETE FROM cf_agents_mcp_servers WHERE id = ${id};
|
|
1166
|
+
`;
|
|
1167
|
+
this.broadcast(
|
|
1168
|
+
JSON.stringify({
|
|
1169
|
+
mcp: this.getMcpServers(),
|
|
1170
|
+
type: "cf_agent_mcp_servers",
|
|
1171
|
+
})
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
getMcpServers(): MCPServersState {
|
|
1176
|
+
const mcpState: MCPServersState = {
|
|
1177
|
+
prompts: this.mcp.listPrompts(),
|
|
1178
|
+
resources: this.mcp.listResources(),
|
|
1179
|
+
servers: {},
|
|
1180
|
+
tools: this.mcp.listTools(),
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
const servers = this.sql<MCPServerRow>`
|
|
1184
|
+
SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
|
|
1185
|
+
`;
|
|
1186
|
+
|
|
1187
|
+
for (const server of servers) {
|
|
1188
|
+
const serverConn = this.mcp.mcpConnections[server.id];
|
|
1189
|
+
mcpState.servers[server.id] = {
|
|
1190
|
+
auth_url: server.auth_url,
|
|
1191
|
+
capabilities: serverConn?.serverCapabilities ?? null,
|
|
1192
|
+
instructions: serverConn?.instructions ?? null,
|
|
1193
|
+
name: server.name,
|
|
1194
|
+
server_url: server.server_url,
|
|
1195
|
+
// mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
|
|
1196
|
+
state: serverConn?.connectionState ?? "authenticating",
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
return mcpState;
|
|
1201
|
+
}
|
|
725
1202
|
}
|
|
726
1203
|
|
|
727
1204
|
/**
|
|
@@ -761,9 +1238,9 @@ export async function routeAgentRequest<Env>(
|
|
|
761
1238
|
const corsHeaders =
|
|
762
1239
|
options?.cors === true
|
|
763
1240
|
? {
|
|
764
|
-
"Access-Control-Allow-Origin": "*",
|
|
765
|
-
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
|
|
766
1241
|
"Access-Control-Allow-Credentials": "true",
|
|
1242
|
+
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
|
|
1243
|
+
"Access-Control-Allow-Origin": "*",
|
|
767
1244
|
"Access-Control-Max-Age": "86400",
|
|
768
1245
|
}
|
|
769
1246
|
: options?.cors;
|
|
@@ -811,9 +1288,9 @@ export async function routeAgentRequest<Env>(
|
|
|
811
1288
|
* @param options Routing options
|
|
812
1289
|
*/
|
|
813
1290
|
export async function routeAgentEmail<Env>(
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1291
|
+
_email: ForwardableEmailMessage,
|
|
1292
|
+
_env: Env,
|
|
1293
|
+
_options?: AgentOptions<Env>
|
|
817
1294
|
): Promise<void> {}
|
|
818
1295
|
|
|
819
1296
|
/**
|
|
@@ -825,7 +1302,7 @@ export async function routeAgentEmail<Env>(
|
|
|
825
1302
|
* @param options Options for Agent creation
|
|
826
1303
|
* @returns Promise resolving to an Agent instance stub
|
|
827
1304
|
*/
|
|
828
|
-
export function getAgentByName<Env, T extends Agent<Env>>(
|
|
1305
|
+
export async function getAgentByName<Env, T extends Agent<Env>>(
|
|
829
1306
|
namespace: AgentNamespace<T>,
|
|
830
1307
|
name: string,
|
|
831
1308
|
options?: {
|
|
@@ -840,13 +1317,13 @@ export function getAgentByName<Env, T extends Agent<Env>>(
|
|
|
840
1317
|
* A wrapper for streaming responses in callable methods
|
|
841
1318
|
*/
|
|
842
1319
|
export class StreamingResponse {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1320
|
+
private _connection: Connection;
|
|
1321
|
+
private _id: string;
|
|
1322
|
+
private _closed = false;
|
|
846
1323
|
|
|
847
1324
|
constructor(connection: Connection, id: string) {
|
|
848
|
-
this
|
|
849
|
-
this
|
|
1325
|
+
this._connection = connection;
|
|
1326
|
+
this._id = id;
|
|
850
1327
|
}
|
|
851
1328
|
|
|
852
1329
|
/**
|
|
@@ -854,17 +1331,17 @@ export class StreamingResponse {
|
|
|
854
1331
|
* @param chunk The data to send
|
|
855
1332
|
*/
|
|
856
1333
|
send(chunk: unknown) {
|
|
857
|
-
if (this
|
|
1334
|
+
if (this._closed) {
|
|
858
1335
|
throw new Error("StreamingResponse is already closed");
|
|
859
1336
|
}
|
|
860
1337
|
const response: RPCResponse = {
|
|
861
|
-
type: "rpc",
|
|
862
|
-
id: this.#id,
|
|
863
|
-
success: true,
|
|
864
|
-
result: chunk,
|
|
865
1338
|
done: false,
|
|
1339
|
+
id: this._id,
|
|
1340
|
+
result: chunk,
|
|
1341
|
+
success: true,
|
|
1342
|
+
type: "rpc",
|
|
866
1343
|
};
|
|
867
|
-
this
|
|
1344
|
+
this._connection.send(JSON.stringify(response));
|
|
868
1345
|
}
|
|
869
1346
|
|
|
870
1347
|
/**
|
|
@@ -872,17 +1349,17 @@ export class StreamingResponse {
|
|
|
872
1349
|
* @param finalChunk Optional final chunk of data to send
|
|
873
1350
|
*/
|
|
874
1351
|
end(finalChunk?: unknown) {
|
|
875
|
-
if (this
|
|
1352
|
+
if (this._closed) {
|
|
876
1353
|
throw new Error("StreamingResponse is already closed");
|
|
877
1354
|
}
|
|
878
|
-
this
|
|
1355
|
+
this._closed = true;
|
|
879
1356
|
const response: RPCResponse = {
|
|
880
|
-
type: "rpc",
|
|
881
|
-
id: this.#id,
|
|
882
|
-
success: true,
|
|
883
|
-
result: finalChunk,
|
|
884
1357
|
done: true,
|
|
1358
|
+
id: this._id,
|
|
1359
|
+
result: finalChunk,
|
|
1360
|
+
success: true,
|
|
1361
|
+
type: "rpc",
|
|
885
1362
|
};
|
|
886
|
-
this
|
|
1363
|
+
this._connection.send(JSON.stringify(response));
|
|
887
1364
|
}
|
|
888
1365
|
}
|