agents 0.0.0-77368ff → 0.0.0-789141e

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 (72) hide show
  1. package/README.md +130 -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 +12 -8
  5. package/dist/ai-chat-agent.js +466 -66
  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 +67 -70
  11. package/dist/ai-react.js +246 -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-PVQZBKN7.js → chunk-C2OEBJZ2.js} +14 -7
  22. package/dist/chunk-C2OEBJZ2.js.map +1 -0
  23. package/dist/chunk-JJBFIGUC.js +5202 -0
  24. package/dist/chunk-JJBFIGUC.js.map +1 -0
  25. package/dist/chunk-PR4QN5HX.js +43 -0
  26. package/dist/chunk-PR4QN5HX.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-TYAY6AU6.js +159 -0
  30. package/dist/chunk-TYAY6AU6.js.map +1 -0
  31. package/dist/chunk-UJVEAURM.js +150 -0
  32. package/dist/chunk-UJVEAURM.js.map +1 -0
  33. package/dist/{chunk-Z2OUUKK4.js → chunk-XGMKNUJA.js} +188 -115
  34. package/dist/chunk-XGMKNUJA.js.map +1 -0
  35. package/dist/chunk-ZMMHNOMZ.js +942 -0
  36. package/dist/chunk-ZMMHNOMZ.js.map +1 -0
  37. package/dist/client-DVoPb3-C.d.ts +5120 -0
  38. package/dist/client.js +3 -1
  39. package/dist/codemode/ai.d.ts +25 -0
  40. package/dist/codemode/ai.js +5200 -0
  41. package/dist/codemode/ai.js.map +1 -0
  42. package/dist/index.d.ts +548 -31
  43. package/dist/index.js +8 -4
  44. package/dist/mcp/client.d.ts +10 -1052
  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 +59 -87
  49. package/dist/mcp/index.js +948 -637
  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 -13
  56. package/dist/observability/index.js +6 -4
  57. package/dist/react.d.ts +14 -7
  58. package/dist/react.js +107 -7
  59. package/dist/react.js.map +1 -1
  60. package/dist/schedule.d.ts +83 -9
  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 +38 -10
  66. package/src/index.ts +273 -136
  67. package/dist/chunk-KUH345EY.js.map +0 -1
  68. package/dist/chunk-PVQZBKN7.js.map +0 -1
  69. package/dist/chunk-UNG3FXYX.js +0 -525
  70. package/dist/chunk-UNG3FXYX.js.map +0 -1
  71. package/dist/chunk-Z2OUUKK4.js.map +0 -1
  72. 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";
@@ -21,10 +22,13 @@ import {
21
22
  routePartykitRequest
22
23
  } from "partyserver";
23
24
  import { camelCaseToKebabCase } from "./client";
24
- import { MCPClientManager } from "./mcp/client";
25
- // import type { MCPClientConnection } from "./mcp/client-connection";
25
+ import { MCPClientManager, type MCPClientOAuthResult } from "./mcp/client";
26
+ import type { MCPConnectionState } from "./mcp/client-connection";
26
27
  import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider";
28
+ import type { TransportType } from "./mcp/types";
27
29
  import { genericObservability, type Observability } from "./observability";
30
+ import { DisposableStore } from "./core/events";
31
+ import { MessageType } from "./ai-types";
28
32
 
29
33
  export type { Connection, ConnectionContext, WSMessage } from "partyserver";
30
34
 
@@ -42,7 +46,7 @@ export type RPCRequest = {
42
46
  * State update message from client
43
47
  */
44
48
  export type StateUpdateMessage = {
45
- type: "cf_agent_state";
49
+ type: MessageType.CF_AGENT_STATE;
46
50
  state: unknown;
47
51
  };
48
52
 
@@ -50,7 +54,7 @@ export type StateUpdateMessage = {
50
54
  * RPC response message to client
51
55
  */
52
56
  export type RPCResponse = {
53
- type: "rpc";
57
+ type: MessageType.RPC;
54
58
  id: string;
55
59
  } & (
56
60
  | {
@@ -77,7 +81,7 @@ function isRPCRequest(msg: unknown): msg is RPCRequest {
77
81
  typeof msg === "object" &&
78
82
  msg !== null &&
79
83
  "type" in msg &&
80
- msg.type === "rpc" &&
84
+ msg.type === MessageType.RPC &&
81
85
  "id" in msg &&
82
86
  typeof msg.id === "string" &&
83
87
  "method" in msg &&
@@ -95,7 +99,7 @@ function isStateUpdateMessage(msg: unknown): msg is StateUpdateMessage {
95
99
  typeof msg === "object" &&
96
100
  msg !== null &&
97
101
  "type" in msg &&
98
- msg.type === "cf_agent_state" &&
102
+ msg.type === MessageType.CF_AGENT_STATE &&
99
103
  "state" in msg
100
104
  );
101
105
  }
@@ -116,7 +120,7 @@ const callableMetadata = new Map<Function, CallableMetadata>();
116
120
  * Decorator that marks a method as callable by clients
117
121
  * @param metadata Optional metadata about the callable method
118
122
  */
119
- export function unstable_callable(metadata: CallableMetadata = {}) {
123
+ export function callable(metadata: CallableMetadata = {}) {
120
124
  return function callableDecorator<This, Args extends unknown[], Return>(
121
125
  target: (this: This, ...args: Args) => Return,
122
126
  // biome-ignore lint/correctness/noUnusedFunctionParameters: later
@@ -130,6 +134,23 @@ export function unstable_callable(metadata: CallableMetadata = {}) {
130
134
  };
131
135
  }
132
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
+
133
154
  export type QueueItem<T = string> = {
134
155
  id: string;
135
156
  payload: T;
@@ -178,11 +199,13 @@ function getNextCronTime(cron: string) {
178
199
  return interval.getNextDate();
179
200
  }
180
201
 
202
+ export type { TransportType } from "./mcp/types";
203
+
181
204
  /**
182
205
  * MCP Server state update message from server -> Client
183
206
  */
184
207
  export type MCPServerMessage = {
185
- type: "cf_agent_mcp_servers";
208
+ type: MessageType.CF_AGENT_MCP_SERVERS;
186
209
  mcp: MCPServersState;
187
210
  };
188
211
 
@@ -202,7 +225,7 @@ export type MCPServer = {
202
225
  // This state is specifically about the temporary process of getting a token (if needed).
203
226
  // Scope outside of that can't be relied upon because when the DO sleeps, there's no way
204
227
  // to communicate a change to a non-ready state.
205
- state: "authenticating" | "connecting" | "ready" | "discovering" | "failed";
228
+ state: MCPConnectionState;
206
229
  instructions: string | null;
207
230
  capabilities: ServerCapabilities | null;
208
231
  };
@@ -271,7 +294,13 @@ function withAgentContext<T extends (...args: any[]) => any>(
271
294
  method: T
272
295
  ): (this: Agent<unknown, unknown>, ...args: Parameters<T>) => ReturnType<T> {
273
296
  return function (...args: Parameters<T>): ReturnType<T> {
274
- 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
275
304
  return agentContext.run({ agent: this, connection, request, email }, () => {
276
305
  return method.apply(this, args);
277
306
  });
@@ -283,13 +312,21 @@ function withAgentContext<T extends (...args: any[]) => any>(
283
312
  * @template Env Environment type containing bindings
284
313
  * @template State State type to store within the Agent
285
314
  */
286
- export class Agent<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> {
287
320
  private _state = DEFAULT_STATE as State;
321
+ private _disposables = new DisposableStore();
288
322
 
289
323
  private _ParentClass: typeof Agent<Env, State> =
290
324
  Object.getPrototypeOf(this).constructor;
291
325
 
292
- 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
+ );
293
330
 
294
331
  /**
295
332
  * Initial state for the Agent
@@ -382,8 +419,25 @@ export class Agent<Env, State = unknown> extends Server<Env> {
382
419
  constructor(ctx: AgentContext, env: Env) {
383
420
  super(ctx, env);
384
421
 
385
- // Auto-wrap custom methods with agent context
386
- 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
+ );
387
441
 
388
442
  this.sql`
389
443
  CREATE TABLE IF NOT EXISTS cf_agents_state (
@@ -440,21 +494,24 @@ export class Agent<Env, State = unknown> extends Server<Env> {
440
494
  { agent: this, connection: undefined, request, email: undefined },
441
495
  async () => {
442
496
  if (this.mcp.isCallbackRequest(request)) {
443
- await this.mcp.handleCallbackRequest(request);
444
-
445
- // after the MCP connection handshake, we can send updated mcp state
446
- this.broadcast(
447
- JSON.stringify({
448
- mcp: this.getMcpServers(),
449
- type: "cf_agent_mcp_servers"
450
- })
451
- );
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
+ }
452
512
 
453
- // We probably should let the user configure this response/redirect, but this is fine for now.
454
- return new Response("<script>window.close();</script>", {
455
- headers: { "content-type": "text/html" },
456
- status: 200
457
- });
513
+ // Handle OAuth callback response using MCPClientManager configuration
514
+ return this.handleOAuthCallbackResponse(result, request);
458
515
  }
459
516
 
460
517
  return this._tryCatch(() => _onRequest(request));
@@ -515,10 +572,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
515
572
  displayMessage: `RPC call to ${method}`,
516
573
  id: nanoid(),
517
574
  payload: {
518
- args,
519
575
  method,
520
- streaming: metadata?.streaming,
521
- success: true
576
+ streaming: metadata?.streaming
522
577
  },
523
578
  timestamp: Date.now(),
524
579
  type: "rpc"
@@ -531,7 +586,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
531
586
  id,
532
587
  result,
533
588
  success: true,
534
- type: "rpc"
589
+ type: MessageType.RPC
535
590
  };
536
591
  connection.send(JSON.stringify(response));
537
592
  } catch (e) {
@@ -541,7 +596,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
541
596
  e instanceof Error ? e.message : "Unknown error occurred",
542
597
  id: parsed.id,
543
598
  success: false,
544
- type: "rpc"
599
+ type: MessageType.RPC
545
600
  };
546
601
  connection.send(JSON.stringify(response));
547
602
  console.error("RPC error:", e);
@@ -560,44 +615,42 @@ export class Agent<Env, State = unknown> extends Server<Env> {
560
615
  // must fix this
561
616
  return agentContext.run(
562
617
  { 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
-
618
+ () => {
619
+ if (this.state) {
574
620
  connection.send(
575
621
  JSON.stringify({
576
- mcp: this.getMcpServers(),
577
- type: "cf_agent_mcp_servers"
622
+ state: this.state,
623
+ type: MessageType.CF_AGENT_STATE
578
624
  })
579
625
  );
626
+ }
580
627
 
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"
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
590
641
  },
591
- this.ctx
592
- );
593
- return this._tryCatch(() => _onConnect(connection, ctx));
594
- }, 20);
642
+ timestamp: Date.now(),
643
+ type: "connect"
644
+ },
645
+ this.ctx
646
+ );
647
+ return this._tryCatch(() => _onConnect(connection, ctx));
595
648
  }
596
649
  );
597
650
  };
598
651
 
599
652
  const _onStart = this.onStart.bind(this);
600
- this.onStart = async () => {
653
+ this.onStart = async (props?: Props) => {
601
654
  return agentContext.run(
602
655
  {
603
656
  agent: this,
@@ -606,15 +659,27 @@ export class Agent<Env, State = unknown> extends Server<Env> {
606
659
  email: undefined
607
660
  },
608
661
  async () => {
609
- const servers = this.sql<MCPServerRow>`
662
+ await this._tryCatch(() => {
663
+ const servers = this.sql<MCPServerRow>`
610
664
  SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers;
611
665
  `;
612
666
 
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(
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(
618
683
  server.name,
619
684
  server.server_url,
620
685
  server.callback_url,
@@ -625,18 +690,23 @@ export class Agent<Env, State = unknown> extends Server<Env> {
625
690
  id: server.id,
626
691
  oauthClientId: server.client_id ?? undefined
627
692
  }
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());
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
+ });
640
710
  }
641
711
  );
642
712
  };
@@ -646,7 +716,6 @@ export class Agent<Env, State = unknown> extends Server<Env> {
646
716
  state: State,
647
717
  source: Connection | "server" = "server"
648
718
  ) {
649
- const previousState = this._state;
650
719
  this._state = state;
651
720
  this.sql`
652
721
  INSERT OR REPLACE INTO cf_agents_state (id, state)
@@ -659,7 +728,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
659
728
  this.broadcast(
660
729
  JSON.stringify({
661
730
  state: state,
662
- type: "cf_agent_state"
731
+ type: MessageType.CF_AGENT_STATE
663
732
  }),
664
733
  source !== "server" ? [source.id] : []
665
734
  );
@@ -672,10 +741,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
672
741
  {
673
742
  displayMessage: "State updated",
674
743
  id: nanoid(),
675
- payload: {
676
- previousState,
677
- state
678
- },
744
+ payload: {},
679
745
  timestamp: Date.now(),
680
746
  type: "state:update"
681
747
  },
@@ -815,41 +881,37 @@ export class Agent<Env, State = unknown> extends Server<Env> {
815
881
  while (proto && proto !== Object.prototype && depth < 10) {
816
882
  const methodNames = Object.getOwnPropertyNames(proto);
817
883
  for (const methodName of methodNames) {
818
- // 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,
819
887
  if (
820
888
  baseMethods.has(methodName) ||
821
889
  methodName.startsWith("_") ||
822
- typeof this[methodName as keyof this] !== "function"
890
+ !descriptor ||
891
+ !!descriptor.get ||
892
+ typeof descriptor.value !== "function"
823
893
  ) {
824
894
  continue;
825
895
  }
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
896
 
848
- // set the wrapped function on the prototype
849
- this.constructor.prototype[methodName as keyof this] =
850
- wrappedFunction;
851
- }
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
+ );
852
911
  }
912
+
913
+ // set the wrapped function on the prototype
914
+ this.constructor.prototype[methodName as keyof this] = wrappedFunction;
853
915
  }
854
916
 
855
917
  proto = Object.getPrototypeOf(proto);
@@ -1036,7 +1098,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1036
1098
  {
1037
1099
  displayMessage: `Schedule ${schedule.id} created`,
1038
1100
  id: nanoid(),
1039
- payload: schedule,
1101
+ payload: {
1102
+ callback: callback as string,
1103
+ id: id
1104
+ },
1040
1105
  timestamp: Date.now(),
1041
1106
  type: "schedule:create"
1042
1107
  },
@@ -1206,7 +1271,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1206
1271
  {
1207
1272
  displayMessage: `Schedule ${id} cancelled`,
1208
1273
  id: nanoid(),
1209
- payload: schedule,
1274
+ payload: {
1275
+ callback: schedule.callback,
1276
+ id: schedule.id
1277
+ },
1210
1278
  timestamp: Date.now(),
1211
1279
  type: "schedule:cancel"
1212
1280
  },
@@ -1271,7 +1339,10 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1271
1339
  {
1272
1340
  displayMessage: `Schedule ${row.id} executed`,
1273
1341
  id: nanoid(),
1274
- payload: row,
1342
+ payload: {
1343
+ callback: row.callback,
1344
+ id: row.id
1345
+ },
1275
1346
  timestamp: Date.now(),
1276
1347
  type: "schedule:execute"
1277
1348
  },
@@ -1323,6 +1394,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1323
1394
  // delete all alarms
1324
1395
  await this.ctx.storage.deleteAlarm();
1325
1396
  await this.ctx.storage.deleteAll();
1397
+ this._disposables.dispose();
1398
+ await this.mcp.dispose?.();
1326
1399
  this.ctx.abort("destroyed"); // enforce that the agent is evicted
1327
1400
 
1328
1401
  this.observability?.emit(
@@ -1348,25 +1421,42 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1348
1421
  /**
1349
1422
  * Connect to a new MCP Server
1350
1423
  *
1424
+ * @param serverName Name of the MCP server
1351
1425
  * @param url MCP Server SSE URL
1352
- * @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.
1353
1427
  * @param agentsPrefix agents routing prefix if not using `agents`
1354
- * @param options MCP client and transport (header) options
1428
+ * @param options MCP client and transport options
1355
1429
  * @returns authUrl
1356
1430
  */
1357
1431
  async addMcpServer(
1358
1432
  serverName: string,
1359
1433
  url: string,
1360
- callbackHost: string,
1434
+ callbackHost?: string,
1361
1435
  agentsPrefix = "agents",
1362
1436
  options?: {
1363
1437
  client?: ConstructorParameters<typeof Client>[1];
1364
1438
  transport?: {
1365
- headers: HeadersInit;
1439
+ headers?: HeadersInit;
1440
+ type?: TransportType;
1366
1441
  };
1367
1442
  }
1368
1443
  ): Promise<{ id: string; authUrl: string | undefined }> {
1369
- 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`;
1370
1460
 
1371
1461
  const result = await this._connectToMcpServerInternal(
1372
1462
  serverName,
@@ -1374,6 +1464,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1374
1464
  callbackUrl,
1375
1465
  options
1376
1466
  );
1467
+
1377
1468
  this.sql`
1378
1469
  INSERT
1379
1470
  OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
@@ -1388,17 +1479,12 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1388
1479
  );
1389
1480
  `;
1390
1481
 
1391
- this.broadcast(
1392
- JSON.stringify({
1393
- mcp: this.getMcpServers(),
1394
- type: "cf_agent_mcp_servers"
1395
- })
1396
- );
1482
+ this.broadcastMcpServers();
1397
1483
 
1398
1484
  return result;
1399
1485
  }
1400
1486
 
1401
- async _connectToMcpServerInternal(
1487
+ private async _connectToMcpServerInternal(
1402
1488
  _serverName: string,
1403
1489
  url: string,
1404
1490
  callbackUrl: string,
@@ -1414,6 +1500,7 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1414
1500
  */
1415
1501
  transport?: {
1416
1502
  headers?: HeadersInit;
1503
+ type?: TransportType;
1417
1504
  };
1418
1505
  },
1419
1506
  reconnect?: {
@@ -1438,6 +1525,9 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1438
1525
  }
1439
1526
  }
1440
1527
 
1528
+ // Use the transport type specified in options, or default to "auto"
1529
+ const transportType: TransportType = options?.transport?.type ?? "auto";
1530
+
1441
1531
  // allows passing through transport headers if necessary
1442
1532
  // this handles some non-standard bearer auth setups (i.e. MCP server behind CF access instead of OAuth)
1443
1533
  let headerTransportOpts: SSEClientTransportOptions = {};
@@ -1461,7 +1551,8 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1461
1551
  reconnect,
1462
1552
  transport: {
1463
1553
  ...headerTransportOpts,
1464
- authProvider
1554
+ authProvider,
1555
+ type: transportType
1465
1556
  }
1466
1557
  });
1467
1558
 
@@ -1474,15 +1565,11 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1474
1565
 
1475
1566
  async removeMcpServer(id: string) {
1476
1567
  this.mcp.closeConnection(id);
1568
+ this.mcp.unregisterCallbackUrl(id);
1477
1569
  this.sql`
1478
1570
  DELETE FROM cf_agents_mcp_servers WHERE id = ${id};
1479
1571
  `;
1480
- this.broadcast(
1481
- JSON.stringify({
1482
- mcp: this.getMcpServers(),
1483
- type: "cf_agent_mcp_servers"
1484
- })
1485
- );
1572
+ this.broadcastMcpServers();
1486
1573
  }
1487
1574
 
1488
1575
  getMcpServers(): MCPServersState {
@@ -1514,8 +1601,53 @@ export class Agent<Env, State = unknown> extends Server<Env> {
1514
1601
 
1515
1602
  return mcpState;
1516
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
+ }
1517
1646
  }
1518
1647
 
1648
+ // A set of classes that have been wrapped with agent context
1649
+ const wrappedClasses = new Set<typeof Agent.prototype.constructor>();
1650
+
1519
1651
  /**
1520
1652
  * Namespace for creating Agent instances
1521
1653
  * @template Agentic Type of the Agent class
@@ -1831,12 +1963,17 @@ export type EmailSendOptions = {
1831
1963
  * @param options Options for Agent creation
1832
1964
  * @returns Promise resolving to an Agent instance stub
1833
1965
  */
1834
- 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
+ >(
1835
1971
  namespace: AgentNamespace<T>,
1836
1972
  name: string,
1837
1973
  options?: {
1838
1974
  jurisdiction?: DurableObjectJurisdiction;
1839
1975
  locationHint?: DurableObjectLocationHint;
1976
+ props?: Props;
1840
1977
  }
1841
1978
  ) {
1842
1979
  return getServerByName<Env, T>(namespace, name, options);
@@ -1868,7 +2005,7 @@ export class StreamingResponse {
1868
2005
  id: this._id,
1869
2006
  result: chunk,
1870
2007
  success: true,
1871
- type: "rpc"
2008
+ type: MessageType.RPC
1872
2009
  };
1873
2010
  this._connection.send(JSON.stringify(response));
1874
2011
  }
@@ -1887,7 +2024,7 @@ export class StreamingResponse {
1887
2024
  id: this._id,
1888
2025
  result: finalChunk,
1889
2026
  success: true,
1890
- type: "rpc"
2027
+ type: MessageType.RPC
1891
2028
  };
1892
2029
  this._connection.send(JSON.stringify(response));
1893
2030
  }