agents 0.0.0-c69f616 → 0.0.0-c6d9bf1

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 (71) hide show
  1. package/README.md +129 -7
  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 +10 -8
  5. package/dist/ai-chat-agent.js +444 -60
  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 +66 -70
  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-OJFA7RKX.js → chunk-254F4GDT.js} +188 -115
  18. package/dist/chunk-254F4GDT.js.map +1 -0
  19. package/dist/{chunk-HY7ZLHJB.js → chunk-3OT2NNEW.js} +412 -69
  20. package/dist/chunk-3OT2NNEW.js.map +1 -0
  21. package/dist/chunk-5Y6BEZDY.js +276 -0
  22. package/dist/chunk-5Y6BEZDY.js.map +1 -0
  23. package/dist/chunk-BER7KXUJ.js +18 -0
  24. package/dist/chunk-BER7KXUJ.js.map +1 -0
  25. package/dist/chunk-JJBFIGUC.js +5202 -0
  26. package/dist/chunk-JJBFIGUC.js.map +1 -0
  27. package/dist/chunk-PR4QN5HX.js +43 -0
  28. package/dist/chunk-PR4QN5HX.js.map +1 -0
  29. package/dist/{chunk-KUH345EY.js → chunk-QEVM4BVL.js} +5 -5
  30. package/dist/chunk-QEVM4BVL.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-Z44WASMA.js} +11 -3
  36. package/dist/chunk-Z44WASMA.js.map +1 -0
  37. package/dist/{client-CH-eFIfq.d.ts → client-DVoPb3-C.d.ts} +762 -99
  38. package/dist/client.js +3 -1
  39. package/dist/codemode/ai.d.ts +25 -0
  40. package/dist/codemode/ai.js +5112 -0
  41. package/dist/codemode/ai.js.map +1 -0
  42. package/dist/index.d.ts +548 -32
  43. package/dist/index.js +8 -4
  44. package/dist/mcp/client.d.ts +2 -1
  45. package/dist/mcp/client.js +2 -1
  46. package/dist/mcp/do-oauth-client-provider.d.ts +1 -0
  47. package/dist/mcp/do-oauth-client-provider.js +2 -1
  48. package/dist/mcp/index.d.ts +50 -72
  49. package/dist/mcp/index.js +909 -718
  50. package/dist/mcp/index.js.map +1 -1
  51. package/dist/mcp/x402.d.ts +39 -0
  52. package/dist/mcp/x402.js +3195 -0
  53. package/dist/mcp/x402.js.map +1 -0
  54. package/dist/mcp-BH1fJeiU.d.ts +58 -0
  55. package/dist/observability/index.d.ts +34 -14
  56. package/dist/observability/index.js +6 -4
  57. package/dist/react.d.ts +13 -7
  58. package/dist/react.js +107 -7
  59. package/dist/react.js.map +1 -1
  60. package/dist/schedule.d.ts +79 -5
  61. package/dist/schedule.js +17 -2
  62. package/dist/schedule.js.map +1 -1
  63. package/dist/secp256k1-M22GZP2U.js +2193 -0
  64. package/dist/secp256k1-M22GZP2U.js.map +1 -0
  65. package/package.json +32 -9
  66. package/src/index.ts +272 -136
  67. package/dist/chunk-HY7ZLHJB.js.map +0 -1
  68. package/dist/chunk-KUH345EY.js.map +0 -1
  69. package/dist/chunk-OJFA7RKX.js.map +0 -1
  70. package/dist/chunk-PVQZBKN7.js.map +0 -1
  71. package/dist/index-BVoermIz.d.ts +0 -615
package/src/index.ts CHANGED
@@ -22,10 +22,13 @@ import {
22
22
  routePartykitRequest
23
23
  } from "partyserver";
24
24
  import { camelCaseToKebabCase } from "./client";
25
- import { MCPClientManager } from "./mcp/client";
26
- // import type { MCPClientConnection } from "./mcp/client-connection";
25
+ import { MCPClientManager, type MCPClientOAuthResult } from "./mcp/client";
26
+ import type { MCPConnectionState } from "./mcp/client-connection";
27
27
  import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
28
+ import type { TransportType } from "./mcp/types";
28
29
  import { genericObservability, type Observability } from "./observability";
30
+ import { DisposableStore } from "./core/events";
31
+ import { MessageType } from "./ai-types";
29
32
 
30
33
  export type { Connection, ConnectionContext, WSMessage } from "partyserver";
31
34
 
@@ -43,7 +46,7 @@ export type RPCRequest = {
43
46
  * State update message from client
44
47
  */
45
48
  export type StateUpdateMessage = {
46
- type: "cf_agent_state";
49
+ type: MessageType.CF_AGENT_STATE;
47
50
  state: unknown;
48
51
  };
49
52
 
@@ -51,7 +54,7 @@ export type StateUpdateMessage = {
51
54
  * RPC response message to client
52
55
  */
53
56
  export type RPCResponse = {
54
- type: "rpc";
57
+ type: MessageType.RPC;
55
58
  id: string;
56
59
  } & (
57
60
  | {
@@ -78,7 +81,7 @@ function isRPCRequest(msg: unknown): msg is RPCRequest {
78
81
  typeof msg === "object" &&
79
82
  msg !== null &&
80
83
  "type" in msg &&
81
- msg.type === "rpc" &&
84
+ msg.type === MessageType.RPC &&
82
85
  "id" in msg &&
83
86
  typeof msg.id === "string" &&
84
87
  "method" in msg &&
@@ -96,7 +99,7 @@ function isStateUpdateMessage(msg: unknown): msg is StateUpdateMessage {
96
99
  typeof msg === "object" &&
97
100
  msg !== null &&
98
101
  "type" in msg &&
99
- msg.type === "cf_agent_state" &&
102
+ msg.type === MessageType.CF_AGENT_STATE &&
100
103
  "state" in msg
101
104
  );
102
105
  }
@@ -117,7 +120,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
117
120
  * Decorator that marks a method as callable by clients
118
121
  * @param metadata Optional metadata about the callable method
119
122
  */
120
- export function unstable_callable(metadata: CallableMetadata = {}) {
123
+ export function callable(metadata: CallableMetadata = {}) {
121
124
  return function callableDecorator<This, Args extends unknown[], Return>(
122
125
  target: (this: This, ...args: Args) => Return,
123
126
  // biome-ignore lint/correctness/noUnusedFunctionParameters: later
@@ -131,6 +134,23 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
131
134
  };
132
135
  }
133
136
 
137
+ let didWarnAboutUnstableCallable = false;
138
+
139
+ /**
140
+ * Decorator that marks a method as callable by clients
141
+ * @deprecated this has been renamed to callable, and unstable_callable will be removed in the next major version
142
+ * @param metadata Optional metadata about the callable method
143
+ */
144
+ export const unstable_callable = (metadata: CallableMetadata = {}) => {
145
+ if (!didWarnAboutUnstableCallable) {
146
+ didWarnAboutUnstableCallable = true;
147
+ console.warn(
148
+ "unstable_callable is deprecated, use callable instead. unstable_callable will be removed in the next major version."
149
+ );
150
+ }
151
+ callable(metadata);
152
+ };
153
+
134
154
  export type QueueItem<T = string> = {
135
155
  id: string;
136
156
  payload: T;
@@ -179,11 +199,13 @@ function getNextCronTime(cron: string) {
179
199
  return interval.getNextDate();
180
200
  }
181
201
 
202
+ export type { TransportType } from "./mcp/types";
203
+
182
204
  /**
183
205
  * MCP Server state update message from server -> Client
184
206
  */
185
207
  export type MCPServerMessage = {
186
- type: "cf_agent_mcp_servers";
208
+ type: MessageType.CF_AGENT_MCP_SERVERS;
187
209
  mcp: MCPServersState;
188
210
  };
189
211
 
@@ -203,7 +225,7 @@ export type MCPServer = {
203
225
  // This state is specifically about the temporary process of getting a token (if needed).
204
226
  // Scope outside of that can't be relied upon because when the DO sleeps, there's no way
205
227
  // to communicate a change to a non-ready state.
206
- state: "authenticating" | "connecting" | "ready" | "discovering" | "failed";
228
+ state: MCPConnectionState;
207
229
  instructions: string | null;
208
230
  capabilities: ServerCapabilities | null;
209
231
  };
@@ -272,7 +294,13 @@ function withAgentContext<T extends (...args: any[]) => any>(
272
294
  method: T
273
295
  ): (this: Agent<unknown, unknown>, ...args: Parameters<T>) => ReturnType<T> {
274
296
  return function (...args: Parameters<T>): ReturnType<T> {
275
- const { connection, request, email } = getCurrentAgent();
297
+ const { connection, request, email, agent } = getCurrentAgent();
298
+
299
+ if (agent === this) {
300
+ // already wrapped, so we can just call the method
301
+ return method.apply(this, args);
302
+ }
303
+ // not wrapped, so we need to wrap it
276
304
  return agentContext.run({ agent: this, connection, request, email }, () => {
277
305
  return method.apply(this, args);
278
306
  });
@@ -284,13 +312,21 @@ function withAgentContext<T extends (...args: any[]) => any>(
284
312
  * @template Env Environment type containing bindings
285
313
  * @template State State type to store within the Agent
286
314
  */
287
- export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
315
+ export class Agent<
316
+ Env = typeof env,
317
+ State = unknown,
318
+ Props extends Record<string, unknown> = Record<string, unknown>
319
+ > extends Server<Env, Props> {
288
320
  private _state = DEFAULT_STATE as State;
321
+ private _disposables = new DisposableStore();
289
322
 
290
323
  private _ParentClass: typeof Agent<Env, State> =
291
324
  Object.getPrototypeOf(this).constructor;
292
325
 
293
- mcp: MCPClientManager = new MCPClientManager(this._ParentClass.name, "0.0.1");
326
+ readonly mcp: MCPClientManager = new MCPClientManager(
327
+ this._ParentClass.name,
328
+ "0.0.1"
329
+ );
294
330
 
295
331
  /**
296
332
  * Initial state for the Agent
@@ -383,8 +419,25 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
383
419
  constructor(ctx: AgentContext, env: Env) {
384
420
  super(ctx, env);
385
421
 
386
- // Auto-wrap custom methods with agent context
387
- this._autoWrapCustomMethods();
422
+ if (!wrappedClasses.has(this.constructor)) {
423
+ // Auto-wrap custom methods with agent context
424
+ this._autoWrapCustomMethods();
425
+ wrappedClasses.add(this.constructor);
426
+ }
427
+
428
+ // Broadcast server state after background connects (for OAuth servers)
429
+ this._disposables.add(
430
+ this.mcp.onConnected(async () => {
431
+ this.broadcastMcpServers();
432
+ })
433
+ );
434
+
435
+ // Emit MCP observability events
436
+ this._disposables.add(
437
+ this.mcp.onObservabilityEvent((event) => {
438
+ this.observability?.emit(event);
439
+ })
440
+ );
388
441
 
389
442
  this.sql`
390
443
  CREATE TABLE IF NOT EXISTS cf_agents_state (
@@ -441,21 +494,24 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
441
494
  { agent: this, connection: undefined, request, email: undefined },
442
495
  async () => {
443
496
  if (this.mcp.isCallbackRequest(request)) {
444
- await this.mcp.handleCallbackRequest(request);
445
-
446
- // after the MCP connection handshake, we can send updated mcp state
447
- this.broadcast(
448
- JSON.stringify({
449
- mcp: this.getMcpServers(),
450
- type: "cf_agent_mcp_servers"
451
- })
452
- );
497
+ const result = await this.mcp.handleCallbackRequest(request);
498
+ this.broadcastMcpServers();
499
+
500
+ if (result.authSuccess) {
501
+ // Start background connection if auth was successful
502
+ this.mcp
503
+ .establishConnection(result.serverId)
504
+ .catch((error) => {
505
+ console.error("Background connection failed:", error);
506
+ })
507
+ .finally(() => {
508
+ // Broadcast after background connection resolves (success/failure)
509
+ this.broadcastMcpServers();
510
+ });
511
+ }
453
512
 
454
- // We probably should let the user configure this response/redirect, but this is fine for now.
455
- return new Response("<script>window.close();</script>", {
456
- headers: { "content-type": "text/html" },
457
- status: 200
458
- });
513
+ // Handle OAuth callback response using MCPClientManager configuration
514
+ return this.handleOAuthCallbackResponse(result, request);
459
515
  }
460
516
 
461
517
  return this._tryCatch(() => _onRequest(request));
@@ -516,10 +572,8 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
516
572
  displayMessage: `RPC call to ${method}`,
517
573
  id: nanoid(),
518
574
  payload: {
519
- args,
520
575
  method,
521
- streaming: metadata?.streaming,
522
- success: true
576
+ streaming: metadata?.streaming
523
577
  },
524
578
  timestamp: Date.now(),
525
579
  type: "rpc"
@@ -532,7 +586,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
532
586
  id,
533
587
  result,
534
588
  success: true,
535
- type: "rpc"
589
+ type: MessageType.RPC
536
590
  };
537
591
  connection.send(JSON.stringify(response));
538
592
  } catch (e) {
@@ -542,7 +596,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
542
596
  e instanceof Error ? e.message : "Unknown error occurred",
543
597
  id: parsed.id,
544
598
  success: false,
545
- type: "rpc"
599
+ type: MessageType.RPC
546
600
  };
547
601
  connection.send(JSON.stringify(response));
548
602
  console.error("RPC error:", e);
@@ -561,44 +615,42 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
561
615
  // must fix this
562
616
  return agentContext.run(
563
617
  { agent: this, connection, request: ctx.request, email: undefined },
564
- async () => {
565
- setTimeout(() => {
566
- if (this.state) {
567
- connection.send(
568
- JSON.stringify({
569
- state: this.state,
570
- type: "cf_agent_state"
571
- })
572
- );
573
- }
574
-
618
+ () => {
619
+ if (this.state) {
575
620
  connection.send(
576
621
  JSON.stringify({
577
- mcp: this.getMcpServers(),
578
- type: "cf_agent_mcp_servers"
622
+ state: this.state,
623
+ type: MessageType.CF_AGENT_STATE
579
624
  })
580
625
  );
626
+ }
581
627
 
582
- this.observability?.emit(
583
- {
584
- displayMessage: "Connection established",
585
- id: nanoid(),
586
- payload: {
587
- connectionId: connection.id
588
- },
589
- timestamp: Date.now(),
590
- type: "connect"
628
+ connection.send(
629
+ JSON.stringify({
630
+ mcp: this.getMcpServers(),
631
+ type: MessageType.CF_AGENT_MCP_SERVERS
632
+ })
633
+ );
634
+
635
+ this.observability?.emit(
636
+ {
637
+ displayMessage: "Connection established",
638
+ id: nanoid(),
639
+ payload: {
640
+ connectionId: connection.id
591
641
  },
592
- this.ctx
593
- );
594
- return this._tryCatch(() => _onConnect(connection, ctx));
595
- }, 20);
642
+ timestamp: Date.now(),
643
+ type: "connect"
644
+ },
645
+ this.ctx
646
+ );
647
+ return this._tryCatch(() => _onConnect(connection, ctx));
596
648
  }
597
649
  );
598
650
  };
599
651
 
600
652
  const _onStart = this.onStart.bind(this);
601
- this.onStart = async () => {
653
+ this.onStart = async (props?: Props) => {
602
654
  return agentContext.run(
603
655
  {
604
656
  agent: this,
@@ -607,15 +659,27 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
607
659
  email: undefined
608
660
  },
609
661
  async () => {
610
- const servers = this.sql<MCPServerRow>`
662
+ await this._tryCatch(() => {
663
+ const servers = this.sql<MCPServerRow>`
611
664
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
612
665
  `;
613
666
 
614
- // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
615
- if (servers && Array.isArray(servers) && servers.length > 0) {
616
- Promise.allSettled(
617
- servers.map((server) => {
618
- return this._connectToMcpServerInternal(
667
+ this.broadcastMcpServers();
668
+
669
+ // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
670
+ if (servers && Array.isArray(servers) && servers.length > 0) {
671
+ // Restore callback URLs for OAuth-enabled servers
672
+ servers.forEach((server) => {
673
+ if (server.callback_url) {
674
+ // Register the full redirect URL including serverId to avoid ambiguous matches
675
+ this.mcp.registerCallbackUrl(
676
+ `${server.callback_url}/${server.id}`
677
+ );
678
+ }
679
+ });
680
+
681
+ servers.forEach((server) => {
682
+ this._connectToMcpServerInternal(
619
683
  server.name,
620
684
  server.server_url,
621
685
  server.callback_url,
@@ -626,18 +690,23 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
626
690
  id: server.id,
627
691
  oauthClientId: server.client_id ?? undefined
628
692
  }
629
- );
630
- })
631
- ).then((_results) => {
632
- this.broadcast(
633
- JSON.stringify({
634
- mcp: this.getMcpServers(),
635
- type: "cf_agent_mcp_servers"
636
- })
637
- );
638
- });
639
- }
640
- await this._tryCatch(() => _onStart());
693
+ )
694
+ .then(() => {
695
+ // Broadcast updated MCP servers state after each server connects
696
+ this.broadcastMcpServers();
697
+ })
698
+ .catch((error) => {
699
+ console.error(
700
+ `Error connecting to MCP server: ${server.name} (${server.server_url})`,
701
+ error
702
+ );
703
+ // Still broadcast even if connection fails, so clients know about the failure
704
+ this.broadcastMcpServers();
705
+ });
706
+ });
707
+ }
708
+ return _onStart(props);
709
+ });
641
710
  }
642
711
  );
643
712
  };
@@ -647,7 +716,6 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
647
716
  state: State,
648
717
  source: Connection | "server" = "server"
649
718
  ) {
650
- const previousState = this._state;
651
719
  this._state = state;
652
720
  this.sql`
653
721
  INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -660,7 +728,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
660
728
  this.broadcast(
661
729
  JSON.stringify({
662
730
  state: state,
663
- type: "cf_agent_state"
731
+ type: MessageType.CF_AGENT_STATE
664
732
  }),
665
733
  source !== "server" ? [source.id] : []
666
734
  );
@@ -673,10 +741,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
673
741
  {
674
742
  displayMessage: "State updated",
675
743
  id: nanoid(),
676
- payload: {
677
- previousState,
678
- state
679
- },
744
+ payload: {},
680
745
  timestamp: Date.now(),
681
746
  type: "state:update"
682
747
  },
@@ -816,41 +881,37 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
816
881
  while (proto && proto !== Object.prototype && depth < 10) {
817
882
  const methodNames = Object.getOwnPropertyNames(proto);
818
883
  for (const methodName of methodNames) {
819
- // Skip if it's a private method or not a function
884
+ const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
885
+
886
+ // Skip if it's a private method, a base method, a getter, or not a function,
820
887
  if (
821
888
  baseMethods.has(methodName) ||
822
889
  methodName.startsWith("_") ||
823
- typeof this[methodName as keyof this] !== "function"
890
+ !descriptor ||
891
+ !!descriptor.get ||
892
+ typeof descriptor.value !== "function"
824
893
  ) {
825
894
  continue;
826
895
  }
827
- // If the method doesn't exist in base prototypes, it's a custom method
828
- if (!baseMethods.has(methodName)) {
829
- const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
830
- if (descriptor && typeof descriptor.value === "function") {
831
- // Wrap the custom method with context
832
-
833
- const wrappedFunction = withAgentContext(
834
- // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
835
- this[methodName as keyof this] as (...args: any[]) => any
836
- // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
837
- ) as any;
838
-
839
- // if the method is callable, copy the metadata from the original method
840
- if (this._isCallable(methodName)) {
841
- callableMetadata.set(
842
- wrappedFunction,
843
- callableMetadata.get(
844
- this[methodName as keyof this] as Function
845
- )!
846
- );
847
- }
848
896
 
849
- // set the wrapped function on the prototype
850
- this.constructor.prototype[methodName as keyof this] =
851
- wrappedFunction;
852
- }
897
+ // Now, methodName is confirmed to be a custom method/function
898
+ // Wrap the custom method with context
899
+ const wrappedFunction = withAgentContext(
900
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
901
+ this[methodName as keyof this] as (...args: any[]) => any
902
+ // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
903
+ ) as any;
904
+
905
+ // if the method is callable, copy the metadata from the original method
906
+ if (this._isCallable(methodName)) {
907
+ callableMetadata.set(
908
+ wrappedFunction,
909
+ callableMetadata.get(this[methodName as keyof this] as Function)!
910
+ );
853
911
  }
912
+
913
+ // set the wrapped function on the prototype
914
+ this.constructor.prototype[methodName as keyof this] = wrappedFunction;
854
915
  }
855
916
 
856
917
  proto = Object.getPrototypeOf(proto);
@@ -1037,7 +1098,10 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1037
1098
  {
1038
1099
  displayMessage: `Schedule ${schedule.id} created`,
1039
1100
  id: nanoid(),
1040
- payload: schedule,
1101
+ payload: {
1102
+ callback: callback as string,
1103
+ id: id
1104
+ },
1041
1105
  timestamp: Date.now(),
1042
1106
  type: "schedule:create"
1043
1107
  },
@@ -1207,7 +1271,10 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1207
1271
  {
1208
1272
  displayMessage: `Schedule ${id} cancelled`,
1209
1273
  id: nanoid(),
1210
- payload: schedule,
1274
+ payload: {
1275
+ callback: schedule.callback,
1276
+ id: schedule.id
1277
+ },
1211
1278
  timestamp: Date.now(),
1212
1279
  type: "schedule:cancel"
1213
1280
  },
@@ -1272,7 +1339,10 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1272
1339
  {
1273
1340
  displayMessage: `Schedule ${row.id} executed`,
1274
1341
  id: nanoid(),
1275
- payload: row,
1342
+ payload: {
1343
+ callback: row.callback,
1344
+ id: row.id
1345
+ },
1276
1346
  timestamp: Date.now(),
1277
1347
  type: "schedule:execute"
1278
1348
  },
@@ -1324,6 +1394,8 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1324
1394
  // delete all alarms
1325
1395
  await this.ctx.storage.deleteAlarm();
1326
1396
  await this.ctx.storage.deleteAll();
1397
+ this._disposables.dispose();
1398
+ await this.mcp.dispose?.();
1327
1399
  this.ctx.abort("destroyed"); // enforce that the agent is evicted
1328
1400
 
1329
1401
  this.observability?.emit(
@@ -1349,25 +1421,42 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1349
1421
  /**
1350
1422
  * Connect to a new MCP Server
1351
1423
  *
1424
+ * @param serverName Name of the MCP server
1352
1425
  * @param url MCP Server SSE URL
1353
- * @param callbackHost Base host for the agent, used for the redirect URI.
1426
+ * @param callbackHost Base host for the agent, used for the redirect URI. If not provided, will be derived from the current request.
1354
1427
  * @param agentsPrefix agents routing prefix if not using `agents`
1355
- * @param options MCP client and transport (header) options
1428
+ * @param options MCP client and transport options
1356
1429
  * @returns authUrl
1357
1430
  */
1358
1431
  async addMcpServer(
1359
1432
  serverName: string,
1360
1433
  url: string,
1361
- callbackHost: string,
1434
+ callbackHost?: string,
1362
1435
  agentsPrefix = "agents",
1363
1436
  options?: {
1364
1437
  client?: ConstructorParameters<typeof Client>[1];
1365
1438
  transport?: {
1366
- headers: HeadersInit;
1439
+ headers?: HeadersInit;
1440
+ type?: TransportType;
1367
1441
  };
1368
1442
  }
1369
1443
  ): Promise<{ id: string; authUrl: string | undefined }> {
1370
- const callbackUrl = `${callbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
1444
+ // If callbackHost is not provided, derive it from the current request
1445
+ let resolvedCallbackHost = callbackHost;
1446
+ if (!resolvedCallbackHost) {
1447
+ const { request } = getCurrentAgent();
1448
+ if (!request) {
1449
+ throw new Error(
1450
+ "callbackHost is required when not called within a request context"
1451
+ );
1452
+ }
1453
+
1454
+ // Extract the origin from the request
1455
+ const requestUrl = new URL(request.url);
1456
+ resolvedCallbackHost = `${requestUrl.protocol}//${requestUrl.host}`;
1457
+ }
1458
+
1459
+ const callbackUrl = `${resolvedCallbackHost}/${agentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
1371
1460
 
1372
1461
  const result = await this._connectToMcpServerInternal(
1373
1462
  serverName,
@@ -1375,6 +1464,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1375
1464
  callbackUrl,
1376
1465
  options
1377
1466
  );
1467
+
1378
1468
  this.sql`
1379
1469
  INSERT
1380
1470
  OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
@@ -1389,17 +1479,12 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1389
1479
  );
1390
1480
  `;
1391
1481
 
1392
- this.broadcast(
1393
- JSON.stringify({
1394
- mcp: this.getMcpServers(),
1395
- type: "cf_agent_mcp_servers"
1396
- })
1397
- );
1482
+ this.broadcastMcpServers();
1398
1483
 
1399
1484
  return result;
1400
1485
  }
1401
1486
 
1402
- async _connectToMcpServerInternal(
1487
+ private async _connectToMcpServerInternal(
1403
1488
  _serverName: string,
1404
1489
  url: string,
1405
1490
  callbackUrl: string,
@@ -1415,6 +1500,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1415
1500
  */
1416
1501
  transport?: {
1417
1502
  headers?: HeadersInit;
1503
+ type?: TransportType;
1418
1504
  };
1419
1505
  },
1420
1506
  reconnect?: {
@@ -1439,6 +1525,9 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1439
1525
  }
1440
1526
  }
1441
1527
 
1528
+ // Use the transport type specified in options, or default to "auto"
1529
+ const transportType: TransportType = options?.transport?.type ?? "auto";
1530
+
1442
1531
  // allows passing through transport headers if necessary
1443
1532
  // this handles some non-standard bearer auth setups (i.e. MCP server behind CF access instead of OAuth)
1444
1533
  let headerTransportOpts: SSEClientTransportOptions = {};
@@ -1462,7 +1551,8 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1462
1551
  reconnect,
1463
1552
  transport: {
1464
1553
  ...headerTransportOpts,
1465
- authProvider
1554
+ authProvider,
1555
+ type: transportType
1466
1556
  }
1467
1557
  });
1468
1558
 
@@ -1475,15 +1565,11 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1475
1565
 
1476
1566
  async removeMcpServer(id: string) {
1477
1567
  this.mcp.closeConnection(id);
1568
+ this.mcp.unregisterCallbackUrl(id);
1478
1569
  this.sql`
1479
1570
  DELETE FROM cf_agents_mcp_servers WHERE id = ${id};
1480
1571
  `;
1481
- this.broadcast(
1482
- JSON.stringify({
1483
- mcp: this.getMcpServers(),
1484
- type: "cf_agent_mcp_servers"
1485
- })
1486
- );
1572
+ this.broadcastMcpServers();
1487
1573
  }
1488
1574
 
1489
1575
  getMcpServers(): MCPServersState {
@@ -1515,8 +1601,53 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1515
1601
 
1516
1602
  return mcpState;
1517
1603
  }
1604
+
1605
+ private broadcastMcpServers() {
1606
+ this.broadcast(
1607
+ JSON.stringify({
1608
+ mcp: this.getMcpServers(),
1609
+ type: MessageType.CF_AGENT_MCP_SERVERS
1610
+ })
1611
+ );
1612
+ }
1613
+
1614
+ /**
1615
+ * Handle OAuth callback response using MCPClientManager configuration
1616
+ * @param result OAuth callback result
1617
+ * @param request The original request (needed for base URL)
1618
+ * @returns Response for the OAuth callback
1619
+ */
1620
+ private handleOAuthCallbackResponse(
1621
+ result: MCPClientOAuthResult,
1622
+ request: Request
1623
+ ): Response {
1624
+ const config = this.mcp.getOAuthCallbackConfig();
1625
+
1626
+ // Use custom handler if configured
1627
+ if (config?.customHandler) {
1628
+ return config.customHandler(result);
1629
+ }
1630
+
1631
+ // Use redirect URLs if configured
1632
+ if (config?.successRedirect && result.authSuccess) {
1633
+ return Response.redirect(config.successRedirect);
1634
+ }
1635
+
1636
+ if (config?.errorRedirect && !result.authSuccess) {
1637
+ return Response.redirect(
1638
+ `${config.errorRedirect}?error=${encodeURIComponent(result.authError || "Unknown error")}`
1639
+ );
1640
+ }
1641
+
1642
+ // Default behavior - redirect to base URL
1643
+ const baseUrl = new URL(request.url).origin;
1644
+ return Response.redirect(baseUrl);
1645
+ }
1518
1646
  }
1519
1647
 
1648
+ // A set of classes that have been wrapped with agent context
1649
+ const wrappedClasses = new Set<typeof Agent.prototype.constructor>();
1650
+
1520
1651
  /**
1521
1652
  * Namespace for creating Agent instances
1522
1653
  * @template Agentic Type of the Agent class
@@ -1832,12 +1963,17 @@ export type EmailSendOptions = {
1832
1963
  * @param options Options for Agent creation
1833
1964
  * @returns Promise resolving to an Agent instance stub
1834
1965
  */
1835
- export async function getAgentByName<Env, T extends Agent<Env>>(
1966
+ export async function getAgentByName<
1967
+ Env,
1968
+ T extends Agent<Env>,
1969
+ Props extends Record<string, unknown> = Record<string, unknown>
1970
+ >(
1836
1971
  namespace: AgentNamespace<T>,
1837
1972
  name: string,
1838
1973
  options?: {
1839
1974
  jurisdiction?: DurableObjectJurisdiction;
1840
1975
  locationHint?: DurableObjectLocationHint;
1976
+ props?: Props;
1841
1977
  }
1842
1978
  ) {
1843
1979
  return getServerByName<Env, T>(namespace, name, options);
@@ -1869,7 +2005,7 @@ export class StreamingResponse {
1869
2005
  id: this._id,
1870
2006
  result: chunk,
1871
2007
  success: true,
1872
- type: "rpc"
2008
+ type: MessageType.RPC
1873
2009
  };
1874
2010
  this._connection.send(JSON.stringify(response));
1875
2011
  }
@@ -1888,7 +2024,7 @@ export class StreamingResponse {
1888
2024
  id: this._id,
1889
2025
  result: finalChunk,
1890
2026
  success: true,
1891
- type: "rpc"
2027
+ type: MessageType.RPC
1892
2028
  };
1893
2029
  this._connection.send(JSON.stringify(response));
1894
2030
  }