agents 0.0.0-e48e5f9 → 0.0.0-e777fdd

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 (67) hide show
  1. package/README.md +234 -6
  2. package/dist/_esm-LV5FJ3HK.js +3922 -0
  3. package/dist/_esm-LV5FJ3HK.js.map +1 -0
  4. package/dist/ai-chat-agent.d.ts +12 -9
  5. package/dist/ai-chat-agent.js +151 -59
  6. package/dist/ai-chat-agent.js.map +1 -1
  7. package/dist/ai-chat-v5-migration.d.ts +152 -0
  8. package/dist/ai-chat-v5-migration.js +20 -0
  9. package/dist/ai-chat-v5-migration.js.map +1 -0
  10. package/dist/ai-react.d.ts +68 -71
  11. package/dist/ai-react.js +252 -99
  12. package/dist/ai-react.js.map +1 -1
  13. package/dist/ai-types.d.ts +37 -19
  14. package/dist/ai-types.js +7 -0
  15. package/dist/ccip-CMBYN64O.js +15 -0
  16. package/dist/ccip-CMBYN64O.js.map +1 -0
  17. package/dist/chunk-5Y6BEZDY.js +276 -0
  18. package/dist/chunk-5Y6BEZDY.js.map +1 -0
  19. package/dist/chunk-BER7KXUJ.js +18 -0
  20. package/dist/chunk-BER7KXUJ.js.map +1 -0
  21. package/dist/chunk-JJBFIGUC.js +5202 -0
  22. package/dist/chunk-JJBFIGUC.js.map +1 -0
  23. package/dist/chunk-PR4QN5HX.js +43 -0
  24. package/dist/chunk-PR4QN5HX.js.map +1 -0
  25. package/dist/{chunk-MW5BQ2FW.js → chunk-QEPGNUG6.js} +213 -32
  26. package/dist/chunk-QEPGNUG6.js.map +1 -0
  27. package/dist/{chunk-KUH345EY.js → chunk-QEVM4BVL.js} +5 -5
  28. package/dist/chunk-QEVM4BVL.js.map +1 -0
  29. package/dist/{chunk-5YIRLLUX.js → chunk-RS5OCNEQ.js} +169 -110
  30. package/dist/chunk-RS5OCNEQ.js.map +1 -0
  31. package/dist/chunk-TYAY6AU6.js +159 -0
  32. package/dist/chunk-TYAY6AU6.js.map +1 -0
  33. package/dist/chunk-UJVEAURM.js +150 -0
  34. package/dist/chunk-UJVEAURM.js.map +1 -0
  35. package/dist/{chunk-PVQZBKN7.js → chunk-XFS5ERG3.js} +24 -3
  36. package/dist/chunk-XFS5ERG3.js.map +1 -0
  37. package/dist/client-BohGLma8.d.ts +5041 -0
  38. package/dist/client.js +3 -1
  39. package/dist/index.d.ts +564 -32
  40. package/dist/index.js +8 -4
  41. package/dist/mcp/client.d.ts +9 -1053
  42. package/dist/mcp/client.js +2 -1
  43. package/dist/mcp/do-oauth-client-provider.d.ts +9 -0
  44. package/dist/mcp/do-oauth-client-provider.js +2 -1
  45. package/dist/mcp/index.d.ts +57 -63
  46. package/dist/mcp/index.js +954 -638
  47. package/dist/mcp/index.js.map +1 -1
  48. package/dist/mcp/x402.d.ts +39 -0
  49. package/dist/mcp/x402.js +3195 -0
  50. package/dist/mcp/x402.js.map +1 -0
  51. package/dist/observability/index.d.ts +46 -12
  52. package/dist/observability/index.js +6 -4
  53. package/dist/react.d.ts +7 -3
  54. package/dist/react.js +8 -5
  55. package/dist/react.js.map +1 -1
  56. package/dist/schedule.d.ts +83 -9
  57. package/dist/schedule.js +17 -2
  58. package/dist/schedule.js.map +1 -1
  59. package/dist/secp256k1-M22GZP2U.js +2193 -0
  60. package/dist/secp256k1-M22GZP2U.js.map +1 -0
  61. package/package.json +25 -9
  62. package/src/index.ts +240 -131
  63. package/dist/chunk-5YIRLLUX.js.map +0 -1
  64. package/dist/chunk-KUH345EY.js.map +0 -1
  65. package/dist/chunk-MW5BQ2FW.js.map +0 -1
  66. package/dist/chunk-PVQZBKN7.js.map +0 -1
  67. package/dist/index-BIJvkfYt.d.ts +0 -614
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { env } from "cloudflare:workers";
1
2
  import { AsyncLocalStorage } from "node:async_hooks";
2
3
  import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
4
  import type { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js";
@@ -22,9 +23,10 @@ import {
22
23
  } from "partyserver";
23
24
  import { camelCaseToKebabCase } from "./client";
24
25
  import { MCPClientManager } from "./mcp/client";
25
- // import type { MCPClientConnection } from "./mcp/client-connection";
26
26
  import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
27
+ import type { TransportType } from "./mcp/types";
27
28
  import { genericObservability, type Observability } from "./observability";
29
+ import { MessageType } from "./ai-types";
28
30
 
29
31
  export type { Connection, ConnectionContext, WSMessage } from "partyserver";
30
32
 
@@ -42,7 +44,7 @@ export type RPCRequest = {
42
44
  * State update message from client
43
45
  */
44
46
  export type StateUpdateMessage = {
45
- type: "cf_agent_state";
47
+ type: MessageType.CF_AGENT_STATE;
46
48
  state: unknown;
47
49
  };
48
50
 
@@ -50,7 +52,7 @@ export type StateUpdateMessage = {
50
52
  * RPC response message to client
51
53
  */
52
54
  export type RPCResponse = {
53
- type: "rpc";
55
+ type: MessageType.RPC;
54
56
  id: string;
55
57
  } & (
56
58
  | {
@@ -77,7 +79,7 @@ function isRPCRequest(msg: unknown): msg is RPCRequest {
77
79
  typeof msg === "object" &&
78
80
  msg !== null &&
79
81
  "type" in msg &&
80
- msg.type === "rpc" &&
82
+ msg.type === MessageType.RPC &&
81
83
  "id" in msg &&
82
84
  typeof msg.id === "string" &&
83
85
  "method" in msg &&
@@ -95,7 +97,7 @@ function isStateUpdateMessage(msg: unknown): msg is StateUpdateMessage {
95
97
  typeof msg === "object" &&
96
98
  msg !== null &&
97
99
  "type" in msg &&
98
- msg.type === "cf_agent_state" &&
100
+ msg.type === MessageType.CF_AGENT_STATE &&
99
101
  "state" in msg
100
102
  );
101
103
  }
@@ -116,7 +118,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
116
118
  * Decorator that marks a method as callable by clients
117
119
  * @param metadata Optional metadata about the callable method
118
120
  */
119
- export function unstable_callable(metadata: CallableMetadata = {}) {
121
+ export function callable(metadata: CallableMetadata = {}) {
120
122
  return function callableDecorator<This, Args extends unknown[], Return>(
121
123
  target: (this: This, ...args: Args) => Return,
122
124
  // biome-ignore lint/correctness/noUnusedFunctionParameters: later
@@ -130,6 +132,23 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
130
132
  };
131
133
  }
132
134
 
135
+ let didWarnAboutUnstableCallable = false;
136
+
137
+ /**
138
+ * Decorator that marks a method as callable by clients
139
+ * @deprecated this has been renamed to callable, and unstable_callable will be removed in the next major version
140
+ * @param metadata Optional metadata about the callable method
141
+ */
142
+ export const unstable_callable = (metadata: CallableMetadata = {}) => {
143
+ if (!didWarnAboutUnstableCallable) {
144
+ didWarnAboutUnstableCallable = true;
145
+ console.warn(
146
+ "unstable_callable is deprecated, use callable instead. unstable_callable will be removed in the next major version."
147
+ );
148
+ }
149
+ callable(metadata);
150
+ };
151
+
133
152
  export type QueueItem<T = string> = {
134
153
  id: string;
135
154
  payload: T;
@@ -178,11 +197,13 @@ function getNextCronTime(cron: string) {
178
197
  return interval.getNextDate();
179
198
  }
180
199
 
200
+ export type { TransportType } from "./mcp/types";
201
+
181
202
  /**
182
203
  * MCP Server state update message from server -> Client
183
204
  */
184
205
  export type MCPServerMessage = {
185
- type: "cf_agent_mcp_servers";
206
+ type: MessageType.CF_AGENT_MCP_SERVERS;
186
207
  mcp: MCPServersState;
187
208
  };
188
209
 
@@ -271,7 +292,13 @@ function withAgentContext<T extends (...args: any[]) => any>(
271
292
  method: T
272
293
  ): (this: Agent<unknown, unknown>, ...args: Parameters<T>) => ReturnType<T> {
273
294
  return function (...args: Parameters<T>): ReturnType<T> {
274
- const { connection, request, email } = getCurrentAgent();
295
+ const { connection, request, email, agent } = getCurrentAgent();
296
+
297
+ if (agent === this) {
298
+ // already wrapped, so we can just call the method
299
+ return method.apply(this, args);
300
+ }
301
+ // not wrapped, so we need to wrap it
275
302
  return agentContext.run({ agent: this, connection, request, email }, () => {
276
303
  return method.apply(this, args);
277
304
  });
@@ -283,13 +310,20 @@ function withAgentContext<T extends (...args: any[]) => any>(
283
310
  * @template Env Environment type containing bindings
284
311
  * @template State State type to store within the Agent
285
312
  */
286
- export class Agent<Env, State = unknown> extends Server<Env> {
313
+ export class Agent<
314
+ Env = typeof env,
315
+ State = unknown,
316
+ Props extends Record<string, unknown> = Record<string, unknown>
317
+ > extends Server<Env, Props> {
287
318
  private _state = DEFAULT_STATE as State;
288
319
 
289
320
  private _ParentClass: typeof Agent<Env, State> =
290
321
  Object.getPrototypeOf(this).constructor;
291
322
 
292
- mcp: MCPClientManager = new MCPClientManager(this._ParentClass.name, "0.0.1");
323
+ readonly mcp: MCPClientManager = new MCPClientManager(
324
+ this._ParentClass.name,
325
+ "0.0.1"
326
+ );
293
327
 
294
328
  /**
295
329
  * Initial state for the Agent
@@ -382,8 +416,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
382
416
  constructor(ctx: AgentContext, env: Env) {
383
417
  super(ctx, env);
384
418
 
385
- // Auto-wrap custom methods with agent context
386
- this._autoWrapCustomMethods();
419
+ if (!wrappedClasses.has(this.constructor)) {
420
+ // Auto-wrap custom methods with agent context
421
+ this._autoWrapCustomMethods();
422
+ wrappedClasses.add(this.constructor);
423
+ }
387
424
 
388
425
  this.sql`
389
426
  CREATE TABLE IF NOT EXISTS cf_agents_state (
@@ -446,7 +483,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
446
483
  this.broadcast(
447
484
  JSON.stringify({
448
485
  mcp: this.getMcpServers(),
449
- type: "cf_agent_mcp_servers"
486
+ type: MessageType.CF_AGENT_MCP_SERVERS
450
487
  })
451
488
  );
452
489
 
@@ -515,10 +552,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
515
552
  displayMessage: `RPC call to ${method}`,
516
553
  id: nanoid(),
517
554
  payload: {
518
- args,
519
555
  method,
520
- streaming: metadata?.streaming,
521
- success: true
556
+ streaming: metadata?.streaming
522
557
  },
523
558
  timestamp: Date.now(),
524
559
  type: "rpc"
@@ -531,7 +566,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
531
566
  id,
532
567
  result,
533
568
  success: true,
534
- type: "rpc"
569
+ type: MessageType.RPC
535
570
  };
536
571
  connection.send(JSON.stringify(response));
537
572
  } catch (e) {
@@ -541,7 +576,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
541
576
  e instanceof Error ? e.message : "Unknown error occurred",
542
577
  id: parsed.id,
543
578
  success: false,
544
- type: "rpc"
579
+ type: MessageType.RPC
545
580
  };
546
581
  connection.send(JSON.stringify(response));
547
582
  console.error("RPC error:", e);
@@ -560,44 +595,42 @@ export class Agent<Env, State = unknown> extends Server<Env> {
560
595
  // must fix this
561
596
  return agentContext.run(
562
597
  { agent: this, connection, request: ctx.request, email: undefined },
563
- async () => {
564
- setTimeout(() => {
565
- if (this.state) {
566
- connection.send(
567
- JSON.stringify({
568
- state: this.state,
569
- type: "cf_agent_state"
570
- })
571
- );
572
- }
573
-
598
+ () => {
599
+ if (this.state) {
574
600
  connection.send(
575
601
  JSON.stringify({
576
- mcp: this.getMcpServers(),
577
- type: "cf_agent_mcp_servers"
602
+ state: this.state,
603
+ type: MessageType.CF_AGENT_STATE
578
604
  })
579
605
  );
606
+ }
580
607
 
581
- this.observability?.emit(
582
- {
583
- displayMessage: "Connection established",
584
- id: nanoid(),
585
- payload: {
586
- connectionId: connection.id
587
- },
588
- timestamp: Date.now(),
589
- type: "connect"
608
+ connection.send(
609
+ JSON.stringify({
610
+ mcp: this.getMcpServers(),
611
+ type: MessageType.CF_AGENT_MCP_SERVERS
612
+ })
613
+ );
614
+
615
+ this.observability?.emit(
616
+ {
617
+ displayMessage: "Connection established",
618
+ id: nanoid(),
619
+ payload: {
620
+ connectionId: connection.id
590
621
  },
591
- this.ctx
592
- );
593
- return this._tryCatch(() => _onConnect(connection, ctx));
594
- }, 20);
622
+ timestamp: Date.now(),
623
+ type: "connect"
624
+ },
625
+ this.ctx
626
+ );
627
+ return this._tryCatch(() => _onConnect(connection, ctx));
595
628
  }
596
629
  );
597
630
  };
598
631
 
599
632
  const _onStart = this.onStart.bind(this);
600
- this.onStart = async () => {
633
+ this.onStart = async (props?: Props) => {
601
634
  return agentContext.run(
602
635
  {
603
636
  agent: this,
@@ -606,15 +639,29 @@ export class Agent<Env, State = unknown> extends Server<Env> {
606
639
  email: undefined
607
640
  },
608
641
  async () => {
609
- const servers = this.sql<MCPServerRow>`
642
+ await this._tryCatch(() => {
643
+ const servers = this.sql<MCPServerRow>`
610
644
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
611
645
  `;
612
646
 
613
- // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
614
- if (servers && Array.isArray(servers) && servers.length > 0) {
615
- Promise.allSettled(
616
- servers.map((server) => {
617
- return this._connectToMcpServerInternal(
647
+ this.broadcast(
648
+ JSON.stringify({
649
+ mcp: this.getMcpServers(),
650
+ type: MessageType.CF_AGENT_MCP_SERVERS
651
+ })
652
+ );
653
+
654
+ // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
655
+ if (servers && Array.isArray(servers) && servers.length > 0) {
656
+ // Restore callback URLs for OAuth-enabled servers
657
+ servers.forEach((server) => {
658
+ if (server.callback_url) {
659
+ this.mcp.registerCallbackUrl(server.callback_url);
660
+ }
661
+ });
662
+
663
+ servers.forEach((server) => {
664
+ this._connectToMcpServerInternal(
618
665
  server.name,
619
666
  server.server_url,
620
667
  server.callback_url,
@@ -625,18 +672,33 @@ export class Agent<Env, State = unknown> extends Server<Env> {
625
672
  id: server.id,
626
673
  oauthClientId: server.client_id ?? undefined
627
674
  }
628
- );
629
- })
630
- ).then((_results) => {
631
- this.broadcast(
632
- JSON.stringify({
633
- mcp: this.getMcpServers(),
634
- type: "cf_agent_mcp_servers"
635
- })
636
- );
637
- });
638
- }
639
- await this._tryCatch(() => _onStart());
675
+ )
676
+ .then(() => {
677
+ // Broadcast updated MCP servers state after each server connects
678
+ this.broadcast(
679
+ JSON.stringify({
680
+ mcp: this.getMcpServers(),
681
+ type: MessageType.CF_AGENT_MCP_SERVERS
682
+ })
683
+ );
684
+ })
685
+ .catch((error) => {
686
+ console.error(
687
+ `Error connecting to MCP server: ${server.name} (${server.server_url})`,
688
+ error
689
+ );
690
+ // Still broadcast even if connection fails, so clients know about the failure
691
+ this.broadcast(
692
+ JSON.stringify({
693
+ mcp: this.getMcpServers(),
694
+ type: MessageType.CF_AGENT_MCP_SERVERS
695
+ })
696
+ );
697
+ });
698
+ });
699
+ }
700
+ return _onStart(props);
701
+ });
640
702
  }
641
703
  );
642
704
  };
@@ -646,7 +708,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
646
708
  state: State,
647
709
  source: Connection | "server" = "server"
648
710
  ) {
649
- const previousState = this._state;
650
711
  this._state = state;
651
712
  this.sql`
652
713
  INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -659,7 +720,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
659
720
  this.broadcast(
660
721
  JSON.stringify({
661
722
  state: state,
662
- type: "cf_agent_state"
723
+ type: MessageType.CF_AGENT_STATE
663
724
  }),
664
725
  source !== "server" ? [source.id] : []
665
726
  );
@@ -672,10 +733,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
672
733
  {
673
734
  displayMessage: "State updated",
674
735
  id: nanoid(),
675
- payload: {
676
- previousState,
677
- state
678
- },
736
+ payload: {},
679
737
  timestamp: Date.now(),
680
738
  type: "state:update"
681
739
  },
@@ -815,41 +873,37 @@ export class Agent<Env, State = unknown> extends Server<Env> {
815
873
  while (proto && proto !== Object.prototype && depth < 10) {
816
874
  const methodNames = Object.getOwnPropertyNames(proto);
817
875
  for (const methodName of methodNames) {
818
- // Skip if it's a private method or not a function
876
+ const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
877
+
878
+ // Skip if it's a private method, a base method, a getter, or not a function,
819
879
  if (
820
880
  baseMethods.has(methodName) ||
821
881
  methodName.startsWith("_") ||
822
- typeof this[methodName as keyof this] !== "function"
882
+ !descriptor ||
883
+ !!descriptor.get ||
884
+ typeof descriptor.value !== "function"
823
885
  ) {
824
886
  continue;
825
887
  }
826
- // If the method doesn't exist in base prototypes, it's a custom method
827
- if (!baseMethods.has(methodName)) {
828
- const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
829
- if (descriptor && typeof descriptor.value === "function") {
830
- // Wrap the custom method with context
831
-
832
- const wrappedFunction = withAgentContext(
833
- // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
834
- this[methodName as keyof this] as (...args: any[]) => any
835
- // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
836
- ) as any;
837
-
838
- // if the method is callable, copy the metadata from the original method
839
- if (this._isCallable(methodName)) {
840
- callableMetadata.set(
841
- wrappedFunction,
842
- callableMetadata.get(
843
- this[methodName as keyof this] as Function
844
- )!
845
- );
846
- }
847
888
 
848
- // set the wrapped function on the prototype
849
- this.constructor.prototype[methodName as keyof this] =
850
- wrappedFunction;
851
- }
889
+ // Now, methodName is confirmed to be a custom method/function
890
+ // Wrap the custom method with context
891
+ const wrappedFunction = withAgentContext(
892
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
893
+ this[methodName as keyof this] as (...args: any[]) => any
894
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
895
+ ) as any;
896
+
897
+ // if the method is callable, copy the metadata from the original method
898
+ if (this._isCallable(methodName)) {
899
+ callableMetadata.set(
900
+ wrappedFunction,
901
+ callableMetadata.get(this[methodName as keyof this] as Function)!
902
+ );
852
903
  }
904
+
905
+ // set the wrapped function on the prototype
906
+ this.constructor.prototype[methodName as keyof this] = wrappedFunction;
853
907
  }
854
908
 
855
909
  proto = Object.getPrototypeOf(proto);
@@ -927,7 +981,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
927
981
  }
928
982
  this._flushingQueue = true;
929
983
  while (true) {
930
- const executed: string[] = [];
931
984
  const result = this.sql<QueueItem<string>>`
932
985
  SELECT * FROM cf_agents_queues
933
986
  ORDER BY created_at ASC
@@ -959,13 +1012,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
959
1012
  queueItem: QueueItem<string>
960
1013
  ) => Promise<void>
961
1014
  ).bind(this)(JSON.parse(row.payload as string), row);
962
- executed.push(row.id);
1015
+ await this.dequeue(row.id);
963
1016
  }
964
1017
  );
965
1018
  }
966
- for (const id of executed) {
967
- await this.dequeue(id);
968
- }
969
1019
  }
970
1020
  this._flushingQueue = false;
971
1021
  }
@@ -1040,7 +1090,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1040
1090
  {
1041
1091
  displayMessage: `Schedule ${schedule.id} created`,
1042
1092
  id: nanoid(),
1043
- payload: schedule,
1093
+ payload: {
1094
+ callback: callback as string,
1095
+ id: id
1096
+ },
1044
1097
  timestamp: Date.now(),
1045
1098
  type: "schedule:create"
1046
1099
  },
@@ -1210,7 +1263,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1210
1263
  {
1211
1264
  displayMessage: `Schedule ${id} cancelled`,
1212
1265
  id: nanoid(),
1213
- payload: schedule,
1266
+ payload: {
1267
+ callback: schedule.callback,
1268
+ id: schedule.id
1269
+ },
1214
1270
  timestamp: Date.now(),
1215
1271
  type: "schedule:cancel"
1216
1272
  },
@@ -1226,9 +1282,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1226
1282
  private async _scheduleNextAlarm() {
1227
1283
  // Find the next schedule that needs to be executed
1228
1284
  const result = this.sql`
1229
- SELECT time FROM cf_agents_schedules
1285
+ SELECT time FROM cf_agents_schedules
1230
1286
  WHERE time > ${Math.floor(Date.now() / 1000)}
1231
- ORDER BY time ASC
1287
+ ORDER BY time ASC
1232
1288
  LIMIT 1
1233
1289
  `;
1234
1290
  if (!result) return;
@@ -1275,7 +1331,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1275
1331
  {
1276
1332
  displayMessage: `Schedule ${row.id} executed`,
1277
1333
  id: nanoid(),
1278
- payload: row,
1334
+ payload: {
1335
+ callback: row.callback,
1336
+ id: row.id
1337
+ },
1279
1338
  timestamp: Date.now(),
1280
1339
  type: "schedule:execute"
1281
1340
  },
@@ -1352,8 +1411,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1352
1411
  /**
1353
1412
  * Connect to a new MCP Server
1354
1413
  *
1414
+ * @param serverName Name of the MCP server
1355
1415
  * @param url MCP Server SSE URL
1356
- * @param callbackHost Base host for the agent, used for the redirect URI.
1416
+ * @param callbackHost Base host for the agent, used for the redirect URI. If not provided, will be derived from the current request.
1357
1417
  * @param agentsPrefix agents routing prefix if not using `agents`
1358
1418
  * @param options MCP client and transport (header) options
1359
1419
  * @returns authUrl
@@ -1361,7 +1421,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1361
1421
  async addMcpServer(
1362
1422
  serverName: string,
1363
1423
  url: string,
1364
- callbackHost: string,
1424
+ callbackHost?: string,
1365
1425
  agentsPrefix = "agents",
1366
1426
  options?: {
1367
1427
  client?: ConstructorParameters<typeof Client>[1];
@@ -1370,7 +1430,22 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1370
1430
  };
1371
1431
  }
1372
1432
  ): Promise<{ id: string; authUrl: string | undefined }> {
1373
- const callbackUrl = `${callbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
1433
+ // If callbackHost is not provided, derive it from the current request
1434
+ let resolvedCallbackHost = callbackHost;
1435
+ if (!resolvedCallbackHost) {
1436
+ const { request } = getCurrentAgent();
1437
+ if (!request) {
1438
+ throw new Error(
1439
+ "callbackHost is required when not called within a request context"
1440
+ );
1441
+ }
1442
+
1443
+ // Extract the origin from the request
1444
+ const requestUrl = new URL(request.url);
1445
+ resolvedCallbackHost = `${requestUrl.protocol}//${requestUrl.host}`;
1446
+ }
1447
+
1448
+ const callbackUrl = `${resolvedCallbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
1374
1449
 
1375
1450
  const result = await this._connectToMcpServerInternal(
1376
1451
  serverName,
@@ -1395,7 +1470,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1395
1470
  this.broadcast(
1396
1471
  JSON.stringify({
1397
1472
  mcp: this.getMcpServers(),
1398
- type: "cf_agent_mcp_servers"
1473
+ type: MessageType.CF_AGENT_MCP_SERVERS
1399
1474
  })
1400
1475
  );
1401
1476
 
@@ -1418,6 +1493,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1418
1493
  */
1419
1494
  transport?: {
1420
1495
  headers?: HeadersInit;
1496
+ type?: TransportType;
1421
1497
  };
1422
1498
  },
1423
1499
  reconnect?: {
@@ -1460,12 +1536,16 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1460
1536
  };
1461
1537
  }
1462
1538
 
1539
+ // Use the transport type specified in options, or default to "auto"
1540
+ const transportType = options?.transport?.type || "auto";
1541
+
1463
1542
  const { id, authUrl, clientId } = await this.mcp.connect(url, {
1464
1543
  client: options?.client,
1465
1544
  reconnect,
1466
1545
  transport: {
1467
1546
  ...headerTransportOpts,
1468
- authProvider
1547
+ authProvider,
1548
+ type: transportType
1469
1549
  }
1470
1550
  });
1471
1551
 
@@ -1478,13 +1558,14 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1478
1558
 
1479
1559
  async removeMcpServer(id: string) {
1480
1560
  this.mcp.closeConnection(id);
1561
+ this.mcp.unregisterCallbackUrl(id);
1481
1562
  this.sql`
1482
1563
  DELETE FROM cf_agents_mcp_servers WHERE id = ${id};
1483
1564
  `;
1484
1565
  this.broadcast(
1485
1566
  JSON.stringify({
1486
1567
  mcp: this.getMcpServers(),
1487
- type: "cf_agent_mcp_servers"
1568
+ type: MessageType.CF_AGENT_MCP_SERVERS
1488
1569
  })
1489
1570
  );
1490
1571
  }
@@ -1520,6 +1601,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1520
1601
  }
1521
1602
  }
1522
1603
 
1604
+ // A set of classes that have been wrapped with agent context
1605
+ const wrappedClasses = new Set<typeof Agent.prototype.constructor>();
1606
+
1523
1607
  /**
1524
1608
  * Namespace for creating Agent instances
1525
1609
  * @template Agentic Type of the Agent class
@@ -1696,6 +1780,13 @@ export type EmailRoutingOptions<Env> = AgentOptions<Env> & {
1696
1780
  resolver: EmailResolver<Env>;
1697
1781
  };
1698
1782
 
1783
+ // Cache the agent namespace map for email routing
1784
+ // This maps both kebab-case and original names to namespaces
1785
+ const agentMapCache = new WeakMap<
1786
+ Record<string, unknown>,
1787
+ Record<string, unknown>
1788
+ >();
1789
+
1699
1790
  /**
1700
1791
  * Route an email to the appropriate Agent
1701
1792
  * @param email The email to route
@@ -1715,28 +1806,41 @@ export async function routeAgentEmail<Env>(
1715
1806
  return;
1716
1807
  }
1717
1808
 
1718
- const namespaceBinding = env[routingInfo.agentName as keyof Env];
1719
- if (!namespaceBinding) {
1720
- throw new Error(
1721
- `Agent namespace '${routingInfo.agentName}' not found in environment`
1722
- );
1809
+ // Build a map that includes both original names and kebab-case versions
1810
+ if (!agentMapCache.has(env as Record<string, unknown>)) {
1811
+ const map: Record<string, unknown> = {};
1812
+ for (const [key, value] of Object.entries(env as Record<string, unknown>)) {
1813
+ if (
1814
+ value &&
1815
+ typeof value === "object" &&
1816
+ "idFromName" in value &&
1817
+ typeof value.idFromName === "function"
1818
+ ) {
1819
+ // Add both the original name and kebab-case version
1820
+ map[key] = value;
1821
+ map[camelCaseToKebabCase(key)] = value;
1822
+ }
1823
+ }
1824
+ agentMapCache.set(env as Record<string, unknown>, map);
1723
1825
  }
1724
1826
 
1725
- // Type guard to check if this is actually a DurableObjectNamespace (AgentNamespace)
1726
- if (
1727
- typeof namespaceBinding !== "object" ||
1728
- !("idFromName" in namespaceBinding) ||
1729
- typeof namespaceBinding.idFromName !== "function"
1730
- ) {
1827
+ const agentMap = agentMapCache.get(env as Record<string, unknown>)!;
1828
+ const namespace = agentMap[routingInfo.agentName];
1829
+
1830
+ if (!namespace) {
1831
+ // Provide helpful error message listing available agents
1832
+ const availableAgents = Object.keys(agentMap)
1833
+ .filter((key) => !key.includes("-")) // Show only original names, not kebab-case duplicates
1834
+ .join(", ");
1731
1835
  throw new Error(
1732
- `Environment binding '${routingInfo.agentName}' is not an AgentNamespace (found: ${typeof namespaceBinding})`
1836
+ `Agent namespace '${routingInfo.agentName}' not found in environment. Available agents: ${availableAgents}`
1733
1837
  );
1734
1838
  }
1735
1839
 
1736
- // Safe cast after runtime validation
1737
- const namespace = namespaceBinding as unknown as AgentNamespace<Agent<Env>>;
1738
-
1739
- const agent = await getAgentByName(namespace, routingInfo.agentId);
1840
+ const agent = await getAgentByName(
1841
+ namespace as unknown as AgentNamespace<Agent<Env>>,
1842
+ routingInfo.agentId
1843
+ );
1740
1844
 
1741
1845
  // let's make a serialisable version of the email
1742
1846
  const serialisableEmail: AgentEmail = {
@@ -1815,12 +1919,17 @@ export type EmailSendOptions = {
1815
1919
  * @param options Options for Agent creation
1816
1920
  * @returns Promise resolving to an Agent instance stub
1817
1921
  */
1818
- export async function getAgentByName<Env, T extends Agent<Env>>(
1922
+ export async function getAgentByName<
1923
+ Env,
1924
+ T extends Agent<Env>,
1925
+ Props extends Record<string, unknown> = Record<string, unknown>
1926
+ >(
1819
1927
  namespace: AgentNamespace<T>,
1820
1928
  name: string,
1821
1929
  options?: {
1822
1930
  jurisdiction?: DurableObjectJurisdiction;
1823
1931
  locationHint?: DurableObjectLocationHint;
1932
+ props?: Props;
1824
1933
  }
1825
1934
  ) {
1826
1935
  return getServerByName<Env, T>(namespace, name, options);
@@ -1852,7 +1961,7 @@ export class StreamingResponse {
1852
1961
  id: this._id,
1853
1962
  result: chunk,
1854
1963
  success: true,
1855
- type: "rpc"
1964
+ type: MessageType.RPC
1856
1965
  };
1857
1966
  this._connection.send(JSON.stringify(response));
1858
1967
  }
@@ -1871,7 +1980,7 @@ export class StreamingResponse {
1871
1980
  id: this._id,
1872
1981
  result: finalChunk,
1873
1982
  success: true,
1874
- type: "rpc"
1983
+ type: MessageType.RPC
1875
1984
  };
1876
1985
  this._connection.send(JSON.stringify(response));
1877
1986
  }