agents 0.0.0-ac0e999 → 0.0.0-ac74811

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.
Files changed (45) hide show
  1. package/dist/ai-chat-agent.d.ts +13 -12
  2. package/dist/ai-chat-agent.js +94 -57
  3. package/dist/ai-chat-agent.js.map +1 -1
  4. package/dist/ai-react.d.ts +9 -8
  5. package/dist/ai-react.js +27 -29
  6. package/dist/ai-react.js.map +1 -1
  7. package/dist/{chunk-D6UOOELW.js → chunk-767EASBA.js} +15 -15
  8. package/dist/chunk-767EASBA.js.map +1 -0
  9. package/dist/{chunk-25YDMV4H.js → chunk-E3LCYPCB.js} +34 -29
  10. package/dist/chunk-E3LCYPCB.js.map +1 -0
  11. package/dist/{chunk-DMJ7L3FI.js → chunk-JFRK72K3.js} +305 -168
  12. package/dist/chunk-JFRK72K3.js.map +1 -0
  13. package/dist/{chunk-ZKIVUOTQ.js → chunk-NKZZ66QY.js} +14 -21
  14. package/dist/chunk-NKZZ66QY.js.map +1 -0
  15. package/dist/client.d.ts +7 -1
  16. package/dist/client.js +1 -2
  17. package/dist/index-CITGJflw.d.ts +486 -0
  18. package/dist/index.d.ts +25 -369
  19. package/dist/index.js +4 -5
  20. package/dist/mcp/client.d.ts +287 -15
  21. package/dist/mcp/client.js +1 -2
  22. package/dist/mcp/do-oauth-client-provider.d.ts +3 -3
  23. package/dist/mcp/do-oauth-client-provider.js +1 -2
  24. package/dist/mcp/index.d.ts +12 -12
  25. package/dist/mcp/index.js +124 -121
  26. package/dist/mcp/index.js.map +1 -1
  27. package/dist/observability/index.d.ts +12 -0
  28. package/dist/observability/index.js +10 -0
  29. package/dist/react.d.ts +76 -10
  30. package/dist/react.js +16 -8
  31. package/dist/react.js.map +1 -1
  32. package/dist/schedule.d.ts +6 -6
  33. package/dist/schedule.js +4 -6
  34. package/dist/schedule.js.map +1 -1
  35. package/dist/serializable.d.ts +32 -0
  36. package/dist/serializable.js +1 -0
  37. package/dist/serializable.js.map +1 -0
  38. package/package.json +75 -71
  39. package/src/index.ts +253 -131
  40. package/dist/chunk-25YDMV4H.js.map +0 -1
  41. package/dist/chunk-D6UOOELW.js.map +0 -1
  42. package/dist/chunk-DMJ7L3FI.js.map +0 -1
  43. package/dist/chunk-NOUFNU2O.js +0 -12
  44. package/dist/chunk-ZKIVUOTQ.js.map +0 -1
  45. /package/dist/{chunk-NOUFNU2O.js.map → observability/index.js.map} +0 -0
package/src/index.ts CHANGED
@@ -1,34 +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";
10
-
11
- import { parseCronExpression } from "cron-schedule";
12
- import { nanoid } from "nanoid";
13
-
14
- import { AsyncLocalStorage } from "node:async_hooks";
15
- import { MCPClientManager } from "./mcp/client";
16
- import {
17
- DurableObjectOAuthClientProvider,
18
- type AgentsOAuthProvider,
19
- } from "./mcp/do-oauth-client-provider";
20
- import type {
21
- Tool,
22
- Resource,
23
- Prompt,
24
- } from "@modelcontextprotocol/sdk/types.js";
25
-
26
- import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
27
- import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
28
-
29
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";
30
27
 
31
- export type { Connection, WSMessage, ConnectionContext } from "partyserver";
28
+ export type { Connection, ConnectionContext, WSMessage } from "partyserver";
32
29
 
33
30
  /**
34
31
  * RPC request message from client
@@ -112,7 +109,6 @@ export type CallableMetadata = {
112
109
  streaming?: boolean;
113
110
  };
114
111
 
115
- // biome-ignore lint/complexity/noBannedTypes: <explanation>
116
112
  const callableMetadata = new Map<Function, CallableMetadata>();
117
113
 
118
114
  /**
@@ -122,6 +118,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
122
118
  export function unstable_callable(metadata: CallableMetadata = {}) {
123
119
  return function callableDecorator<This, Args extends unknown[], Return>(
124
120
  target: (this: This, ...args: Args) => Return,
121
+ // biome-ignore lint/correctness/noUnusedFunctionParameters: later
125
122
  context: ClassMethodDecoratorContext
126
123
  ) {
127
124
  if (!callableMetadata.has(target)) {
@@ -194,7 +191,12 @@ export type MCPServer = {
194
191
  name: string;
195
192
  server_url: string;
196
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
197
  state: "authenticating" | "connecting" | "ready" | "discovering" | "failed";
198
+ instructions: string | null;
199
+ capabilities: ServerCapabilities | null;
198
200
  };
199
201
 
200
202
  /**
@@ -253,10 +255,10 @@ export function getCurrentAgent<
253
255
  export class Agent<Env, State = unknown> extends Server<Env> {
254
256
  private _state = DEFAULT_STATE as State;
255
257
 
256
- private ParentClass: typeof Agent<Env, State> =
258
+ private _ParentClass: typeof Agent<Env, State> =
257
259
  Object.getPrototypeOf(this).constructor;
258
260
 
259
- mcp: MCPClientManager = new MCPClientManager(this.ParentClass.name, "0.0.1");
261
+ mcp: MCPClientManager = new MCPClientManager(this._ParentClass.name, "0.0.1");
260
262
 
261
263
  /**
262
264
  * Initial state for the Agent
@@ -315,6 +317,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
315
317
  hibernate: true, // default to hibernate
316
318
  };
317
319
 
320
+ /**
321
+ * The observability implementation to use for the Agent
322
+ */
323
+ observability?: Observability = genericObservability;
324
+
318
325
  /**
319
326
  * Execute SQL queries against the Agent's database
320
327
  * @template T Type of the returned rows
@@ -352,7 +359,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
352
359
  `;
353
360
 
354
361
  void this.ctx.blockConcurrencyWhile(async () => {
355
- return this.tryCatch(async () => {
362
+ return this._tryCatch(async () => {
356
363
  // Create alarms table if it doesn't exist
357
364
  this.sql`
358
365
  CREATE TABLE IF NOT EXISTS cf_agents_schedules (
@@ -395,19 +402,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
395
402
  // after the MCP connection handshake, we can send updated mcp state
396
403
  this.broadcast(
397
404
  JSON.stringify({
405
+ mcp: this.getMcpServers(),
398
406
  type: "cf_agent_mcp_servers",
399
- mcp: this.#getMcpServerStateInternal(),
400
407
  })
401
408
  );
402
409
 
403
410
  // We probably should let the user configure this response/redirect, but this is fine for now.
404
411
  return new Response("<script>window.close();</script>", {
405
- status: 200,
406
412
  headers: { "content-type": "text/html" },
413
+ status: 200,
407
414
  });
408
415
  }
409
416
 
410
- return this.tryCatch(() => _onRequest(request));
417
+ return this._tryCatch(() => _onRequest(request));
411
418
  }
412
419
  );
413
420
  };
@@ -418,19 +425,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
418
425
  { agent: this, connection, request: undefined },
419
426
  async () => {
420
427
  if (typeof message !== "string") {
421
- return this.tryCatch(() => _onMessage(connection, message));
428
+ return this._tryCatch(() => _onMessage(connection, message));
422
429
  }
423
430
 
424
431
  let parsed: unknown;
425
432
  try {
426
433
  parsed = JSON.parse(message);
427
- } catch (e) {
434
+ } catch (_e) {
428
435
  // silently fail and let the onMessage handler handle it
429
- return this.tryCatch(() => _onMessage(connection, message));
436
+ return this._tryCatch(() => _onMessage(connection, message));
430
437
  }
431
438
 
432
439
  if (isStateUpdateMessage(parsed)) {
433
- this.setStateInternal(parsed.state as State, connection);
440
+ this._setStateInternal(parsed.state as State, connection);
434
441
  return;
435
442
  }
436
443
 
@@ -444,11 +451,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
444
451
  throw new Error(`Method ${method} does not exist`);
445
452
  }
446
453
 
447
- if (!this.isCallable(method)) {
454
+ if (!this._isCallable(method)) {
448
455
  throw new Error(`Method ${method} is not callable`);
449
456
  }
450
457
 
451
- // biome-ignore lint/complexity/noBannedTypes: <explanation>
452
458
  const metadata = callableMetadata.get(methodFn as Function);
453
459
 
454
460
  // For streaming methods, pass a StreamingResponse object
@@ -460,22 +466,39 @@ export class Agent<Env, State = unknown> extends Server<Env> {
460
466
 
461
467
  // For regular methods, execute and send response
462
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
+
463
486
  const response: RPCResponse = {
464
- type: "rpc",
487
+ done: true,
465
488
  id,
466
- success: true,
467
489
  result,
468
- done: true,
490
+ success: true,
491
+ type: "rpc",
469
492
  };
470
493
  connection.send(JSON.stringify(response));
471
494
  } catch (e) {
472
495
  // Send error response
473
496
  const response: RPCResponse = {
474
- type: "rpc",
475
- id: parsed.id,
476
- success: false,
477
497
  error:
478
498
  e instanceof Error ? e.message : "Unknown error occurred",
499
+ id: parsed.id,
500
+ success: false,
501
+ type: "rpc",
479
502
  };
480
503
  connection.send(JSON.stringify(response));
481
504
  console.error("RPC error:", e);
@@ -483,7 +506,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
483
506
  return;
484
507
  }
485
508
 
486
- return this.tryCatch(() => _onMessage(connection, message));
509
+ return this._tryCatch(() => _onMessage(connection, message));
487
510
  }
488
511
  );
489
512
  };
@@ -499,20 +522,32 @@ export class Agent<Env, State = unknown> extends Server<Env> {
499
522
  if (this.state) {
500
523
  connection.send(
501
524
  JSON.stringify({
502
- type: "cf_agent_state",
503
525
  state: this.state,
526
+ type: "cf_agent_state",
504
527
  })
505
528
  );
506
529
  }
507
530
 
508
531
  connection.send(
509
532
  JSON.stringify({
533
+ mcp: this.getMcpServers(),
510
534
  type: "cf_agent_mcp_servers",
511
- mcp: this.#getMcpServerStateInternal(),
512
535
  })
513
536
  );
514
537
 
515
- return this.tryCatch(() => _onConnect(connection, ctx));
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));
516
551
  }, 20);
517
552
  }
518
553
  );
@@ -527,10 +562,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
527
562
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
528
563
  `;
529
564
 
530
- // from DO storage, reconnect to all servers using our saved auth information
531
- await Promise.allSettled(
565
+ // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
566
+ Promise.allSettled(
532
567
  servers.map((server) => {
533
- return this.#connectToMcpServerInternal(
568
+ return this._connectToMcpServerInternal(
534
569
  server.name,
535
570
  server.server_url,
536
571
  server.callback_url,
@@ -543,25 +578,25 @@ export class Agent<Env, State = unknown> extends Server<Env> {
543
578
  }
544
579
  );
545
580
  })
546
- );
547
-
548
- this.broadcast(
549
- JSON.stringify({
550
- type: "cf_agent_mcp_servers",
551
- mcp: this.#getMcpServerStateInternal(),
552
- })
553
- );
554
-
555
- await this.tryCatch(() => _onStart());
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());
556
590
  }
557
591
  );
558
592
  };
559
593
  }
560
594
 
561
- private setStateInternal(
595
+ private _setStateInternal(
562
596
  state: State,
563
597
  source: Connection | "server" = "server"
564
598
  ) {
599
+ const previousState = this._state;
565
600
  this._state = state;
566
601
  this.sql`
567
602
  INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -573,16 +608,29 @@ export class Agent<Env, State = unknown> extends Server<Env> {
573
608
  `;
574
609
  this.broadcast(
575
610
  JSON.stringify({
576
- type: "cf_agent_state",
577
611
  state: state,
612
+ type: "cf_agent_state",
578
613
  }),
579
614
  source !== "server" ? [source.id] : []
580
615
  );
581
- return this.tryCatch(() => {
616
+ return this._tryCatch(() => {
582
617
  const { connection, request } = agentContext.getStore() || {};
583
618
  return agentContext.run(
584
619
  { agent: this, connection, request },
585
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
+ );
586
634
  return this.onStateUpdate(state, source);
587
635
  }
588
636
  );
@@ -594,7 +642,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
594
642
  * @param state New state to set
595
643
  */
596
644
  setState(state: State) {
597
- this.setStateInternal(state, "server");
645
+ this._setStateInternal(state, "server");
598
646
  }
599
647
 
600
648
  /**
@@ -602,6 +650,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
602
650
  * @param state Updated state
603
651
  * @param source Source of the state update ("server" or a client connection)
604
652
  */
653
+ // biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
605
654
  onStateUpdate(state: State | undefined, source: Connection | "server") {
606
655
  // override this to handle state updates
607
656
  }
@@ -610,6 +659,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
610
659
  * Called when the Agent receives an email
611
660
  * @param email Email message to process
612
661
  */
662
+ // biome-ignore lint/correctness/noUnusedFunctionParameters: overridden later
613
663
  onEmail(email: ForwardableEmailMessage) {
614
664
  return agentContext.run(
615
665
  { agent: this, connection: undefined, request: undefined },
@@ -619,7 +669,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
619
669
  );
620
670
  }
621
671
 
622
- private async tryCatch<T>(fn: () => T | Promise<T>) {
672
+ private async _tryCatch<T>(fn: () => T | Promise<T>) {
623
673
  try {
624
674
  return await fn();
625
675
  } catch (e) {
@@ -676,6 +726,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
676
726
  ): Promise<Schedule<T>> {
677
727
  const id = nanoid(9);
678
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
+
679
741
  if (typeof callback !== "string") {
680
742
  throw new Error("Callback must be a string");
681
743
  }
@@ -693,15 +755,19 @@ export class Agent<Env, State = unknown> extends Server<Env> {
693
755
  )}, 'scheduled', ${timestamp})
694
756
  `;
695
757
 
696
- await this.scheduleNextAlarm();
758
+ await this._scheduleNextAlarm();
697
759
 
698
- return {
699
- id,
760
+ const schedule: Schedule<T> = {
700
761
  callback: callback,
762
+ id,
701
763
  payload: payload as T,
702
764
  time: timestamp,
703
765
  type: "scheduled",
704
766
  };
767
+
768
+ emitScheduleCreate(schedule);
769
+
770
+ return schedule;
705
771
  }
706
772
  if (typeof when === "number") {
707
773
  const time = new Date(Date.now() + when * 1000);
@@ -714,16 +780,20 @@ export class Agent<Env, State = unknown> extends Server<Env> {
714
780
  )}, 'delayed', ${when}, ${timestamp})
715
781
  `;
716
782
 
717
- await this.scheduleNextAlarm();
783
+ await this._scheduleNextAlarm();
718
784
 
719
- return {
720
- id,
785
+ const schedule: Schedule<T> = {
721
786
  callback: callback,
722
- payload: payload as T,
723
787
  delayInSeconds: when,
788
+ id,
789
+ payload: payload as T,
724
790
  time: timestamp,
725
791
  type: "delayed",
726
792
  };
793
+
794
+ emitScheduleCreate(schedule);
795
+
796
+ return schedule;
727
797
  }
728
798
  if (typeof when === "string") {
729
799
  const nextExecutionTime = getNextCronTime(when);
@@ -736,16 +806,20 @@ export class Agent<Env, State = unknown> extends Server<Env> {
736
806
  )}, 'cron', ${when}, ${timestamp})
737
807
  `;
738
808
 
739
- await this.scheduleNextAlarm();
809
+ await this._scheduleNextAlarm();
740
810
 
741
- return {
742
- id,
811
+ const schedule: Schedule<T> = {
743
812
  callback: callback,
744
- payload: payload as T,
745
813
  cron: when,
814
+ id,
815
+ payload: payload as T,
746
816
  time: timestamp,
747
817
  type: "cron",
748
818
  };
819
+
820
+ emitScheduleCreate(schedule);
821
+
822
+ return schedule;
749
823
  }
750
824
  throw new Error("Invalid schedule type");
751
825
  }
@@ -821,13 +895,26 @@ export class Agent<Env, State = unknown> extends Server<Env> {
821
895
  * @returns true if the task was cancelled, false otherwise
822
896
  */
823
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
+ }
824
911
  this.sql`DELETE FROM cf_agents_schedules WHERE id = ${id}`;
825
912
 
826
- await this.scheduleNextAlarm();
913
+ await this._scheduleNextAlarm();
827
914
  return true;
828
915
  }
829
916
 
830
- private async scheduleNextAlarm() {
917
+ private async _scheduleNextAlarm() {
831
918
  // Find the next schedule that needs to be executed
832
919
  const result = this.sql`
833
920
  SELECT time FROM cf_agents_schedules
@@ -869,6 +956,17 @@ export class Agent<Env, State = unknown> extends Server<Env> {
869
956
  { agent: this, connection: undefined, request: undefined },
870
957
  async () => {
871
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
+
872
970
  await (
873
971
  callback as (
874
972
  payload: unknown,
@@ -897,7 +995,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
897
995
  }
898
996
 
899
997
  // Schedule the next alarm
900
- await this.scheduleNextAlarm();
998
+ await this._scheduleNextAlarm();
901
999
  };
902
1000
 
903
1001
  /**
@@ -912,10 +1010,25 @@ export class Agent<Env, State = unknown> extends Server<Env> {
912
1010
  // delete all alarms
913
1011
  await this.ctx.storage.deleteAlarm();
914
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
+ );
915
1025
  }
916
1026
 
917
- private isCallable(method: string): boolean {
918
- // biome-ignore lint/complexity/noBannedTypes: <explanation>
1027
+ /**
1028
+ * Get all methods marked as callable on this Agent
1029
+ * @returns A map of method names to their metadata
1030
+ */
1031
+ private _isCallable(method: string): boolean {
919
1032
  return callableMetadata.has(this[method as keyof this] as Function);
920
1033
  }
921
1034
 
@@ -940,27 +1053,40 @@ export class Agent<Env, State = unknown> extends Server<Env> {
940
1053
  };
941
1054
  }
942
1055
  ): Promise<{ id: string; authUrl: string | undefined }> {
943
- const callbackUrl = `${callbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this.ParentClass.name)}/${this.name}/callback`;
1056
+ const callbackUrl = `${callbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
944
1057
 
945
- const result = await this.#connectToMcpServerInternal(
1058
+ const result = await this._connectToMcpServerInternal(
946
1059
  serverName,
947
1060
  url,
948
1061
  callbackUrl,
949
1062
  options
950
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
+ `;
951
1077
 
952
1078
  this.broadcast(
953
1079
  JSON.stringify({
1080
+ mcp: this.getMcpServers(),
954
1081
  type: "cf_agent_mcp_servers",
955
- mcp: this.#getMcpServerStateInternal(),
956
1082
  })
957
1083
  );
958
1084
 
959
1085
  return result;
960
1086
  }
961
1087
 
962
- async #connectToMcpServerInternal(
963
- serverName: string,
1088
+ async _connectToMcpServerInternal(
1089
+ _serverName: string,
964
1090
  url: string,
965
1091
  callbackUrl: string,
966
1092
  // it's important that any options here are serializable because we put them into our sqlite DB for reconnection purposes
@@ -981,7 +1107,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
981
1107
  id: string;
982
1108
  oauthClientId?: string;
983
1109
  }
984
- ): Promise<{ id: string; authUrl: string | undefined }> {
1110
+ ): Promise<{
1111
+ id: string;
1112
+ authUrl: string | undefined;
1113
+ clientId: string | undefined;
1114
+ }> {
985
1115
  const authProvider = new DurableObjectOAuthClientProvider(
986
1116
  this.ctx.storage,
987
1117
  this.name,
@@ -1014,30 +1144,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1014
1144
  }
1015
1145
 
1016
1146
  const { id, authUrl, clientId } = await this.mcp.connect(url, {
1147
+ client: options?.client,
1017
1148
  reconnect,
1018
1149
  transport: {
1019
1150
  ...headerTransportOpts,
1020
1151
  authProvider,
1021
1152
  },
1022
- client: options?.client,
1023
1153
  });
1024
1154
 
1025
- this.sql`
1026
- INSERT OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
1027
- VALUES (
1028
- ${id},
1029
- ${serverName},
1030
- ${url},
1031
- ${clientId ?? null},
1032
- ${authUrl ?? null},
1033
- ${callbackUrl},
1034
- ${options ? JSON.stringify(options) : null}
1035
- );
1036
- `;
1037
-
1038
1155
  return {
1039
- id,
1040
1156
  authUrl,
1157
+ clientId,
1158
+ id,
1041
1159
  };
1042
1160
  }
1043
1161
 
@@ -1048,18 +1166,18 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1048
1166
  `;
1049
1167
  this.broadcast(
1050
1168
  JSON.stringify({
1169
+ mcp: this.getMcpServers(),
1051
1170
  type: "cf_agent_mcp_servers",
1052
- mcp: this.#getMcpServerStateInternal(),
1053
1171
  })
1054
1172
  );
1055
1173
  }
1056
1174
 
1057
- #getMcpServerStateInternal(): MCPServersState {
1175
+ getMcpServers(): MCPServersState {
1058
1176
  const mcpState: MCPServersState = {
1059
- servers: {},
1060
- tools: this.mcp.listTools(),
1061
1177
  prompts: this.mcp.listPrompts(),
1062
1178
  resources: this.mcp.listResources(),
1179
+ servers: {},
1180
+ tools: this.mcp.listTools(),
1063
1181
  };
1064
1182
 
1065
1183
  const servers = this.sql<MCPServerRow>`
@@ -1067,11 +1185,15 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1067
1185
  `;
1068
1186
 
1069
1187
  for (const server of servers) {
1188
+ const serverConn = this.mcp.mcpConnections[server.id];
1070
1189
  mcpState.servers[server.id] = {
1190
+ auth_url: server.auth_url,
1191
+ capabilities: serverConn?.serverCapabilities ?? null,
1192
+ instructions: serverConn?.instructions ?? null,
1071
1193
  name: server.name,
1072
1194
  server_url: server.server_url,
1073
- auth_url: server.auth_url,
1074
- state: this.mcp.mcpConnections[server.id].connectionState,
1195
+ // mark as "authenticating" because the server isn't automatically connected, so it's pending authenticating
1196
+ state: serverConn?.connectionState ?? "authenticating",
1075
1197
  };
1076
1198
  }
1077
1199
 
@@ -1116,9 +1238,9 @@ export async function routeAgentRequest<Env>(
1116
1238
  const corsHeaders =
1117
1239
  options?.cors === true
1118
1240
  ? {
1119
- "Access-Control-Allow-Origin": "*",
1120
- "Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
1121
1241
  "Access-Control-Allow-Credentials": "true",
1242
+ "Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS",
1243
+ "Access-Control-Allow-Origin": "*",
1122
1244
  "Access-Control-Max-Age": "86400",
1123
1245
  }
1124
1246
  : options?.cors;
@@ -1166,9 +1288,9 @@ export async function routeAgentRequest<Env>(
1166
1288
  * @param options Routing options
1167
1289
  */
1168
1290
  export async function routeAgentEmail<Env>(
1169
- email: ForwardableEmailMessage,
1170
- env: Env,
1171
- options?: AgentOptions<Env>
1291
+ _email: ForwardableEmailMessage,
1292
+ _env: Env,
1293
+ _options?: AgentOptions<Env>
1172
1294
  ): Promise<void> {}
1173
1295
 
1174
1296
  /**
@@ -1195,13 +1317,13 @@ export async function getAgentByName<Env, T extends Agent<Env>>(
1195
1317
  * A wrapper for streaming responses in callable methods
1196
1318
  */
1197
1319
  export class StreamingResponse {
1198
- private connection: Connection;
1199
- private id: string;
1200
- private closed = false;
1320
+ private _connection: Connection;
1321
+ private _id: string;
1322
+ private _closed = false;
1201
1323
 
1202
1324
  constructor(connection: Connection, id: string) {
1203
- this.connection = connection;
1204
- this.id = id;
1325
+ this._connection = connection;
1326
+ this._id = id;
1205
1327
  }
1206
1328
 
1207
1329
  /**
@@ -1209,17 +1331,17 @@ export class StreamingResponse {
1209
1331
  * @param chunk The data to send
1210
1332
  */
1211
1333
  send(chunk: unknown) {
1212
- if (this.closed) {
1334
+ if (this._closed) {
1213
1335
  throw new Error("StreamingResponse is already closed");
1214
1336
  }
1215
1337
  const response: RPCResponse = {
1216
- type: "rpc",
1217
- id: this.id,
1218
- success: true,
1219
- result: chunk,
1220
1338
  done: false,
1339
+ id: this._id,
1340
+ result: chunk,
1341
+ success: true,
1342
+ type: "rpc",
1221
1343
  };
1222
- this.connection.send(JSON.stringify(response));
1344
+ this._connection.send(JSON.stringify(response));
1223
1345
  }
1224
1346
 
1225
1347
  /**
@@ -1227,17 +1349,17 @@ export class StreamingResponse {
1227
1349
  * @param finalChunk Optional final chunk of data to send
1228
1350
  */
1229
1351
  end(finalChunk?: unknown) {
1230
- if (this.closed) {
1352
+ if (this._closed) {
1231
1353
  throw new Error("StreamingResponse is already closed");
1232
1354
  }
1233
- this.closed = true;
1355
+ this._closed = true;
1234
1356
  const response: RPCResponse = {
1235
- type: "rpc",
1236
- id: this.id,
1237
- success: true,
1238
- result: finalChunk,
1239
1357
  done: true,
1358
+ id: this._id,
1359
+ result: finalChunk,
1360
+ success: true,
1361
+ type: "rpc",
1240
1362
  };
1241
- this.connection.send(JSON.stringify(response));
1363
+ this._connection.send(JSON.stringify(response));
1242
1364
  }
1243
1365
  }