agents 0.0.0-2a6e66e → 0.0.0-2bea91f

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 (70) 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 +8 -6
  5. package/dist/ai-chat-agent.js +442 -48
  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 +64 -69
  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-CV3L6FQZ.js → chunk-254F4GDT.js} +159 -106
  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-DgyzBU_8.d.ts → client-DVoPb3-C.d.ts} +555 -36
  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 +48 -38
  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 -83
  49. package/dist/mcp/index.js +904 -760
  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 +12 -24
  56. package/dist/observability/index.js +6 -4
  57. package/dist/react.d.ts +11 -6
  58. package/dist/react.js +107 -7
  59. package/dist/react.js.map +1 -1
  60. package/dist/schedule.d.ts +76 -2
  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 +27 -7
  66. package/src/index.ts +237 -126
  67. package/dist/chunk-CV3L6FQZ.js.map +0 -1
  68. package/dist/chunk-HY7ZLHJB.js.map +0 -1
  69. package/dist/chunk-KUH345EY.js.map +0 -1
  70. package/dist/chunk-PVQZBKN7.js.map +0 -1
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));
@@ -530,7 +586,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
530
586
  id,
531
587
  result,
532
588
  success: true,
533
- type: "rpc"
589
+ type: MessageType.RPC
534
590
  };
535
591
  connection.send(JSON.stringify(response));
536
592
  } catch (e) {
@@ -540,7 +596,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
540
596
  e instanceof Error ? e.message : "Unknown error occurred",
541
597
  id: parsed.id,
542
598
  success: false,
543
- type: "rpc"
599
+ type: MessageType.RPC
544
600
  };
545
601
  connection.send(JSON.stringify(response));
546
602
  console.error("RPC error:", e);
@@ -559,44 +615,42 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
559
615
  // must fix this
560
616
  return agentContext.run(
561
617
  { agent: this, connection, request: ctx.request, email: undefined },
562
- async () => {
563
- setTimeout(() => {
564
- if (this.state) {
565
- connection.send(
566
- JSON.stringify({
567
- state: this.state,
568
- type: "cf_agent_state"
569
- })
570
- );
571
- }
572
-
618
+ () => {
619
+ if (this.state) {
573
620
  connection.send(
574
621
  JSON.stringify({
575
- mcp: this.getMcpServers(),
576
- type: "cf_agent_mcp_servers"
622
+ state: this.state,
623
+ type: MessageType.CF_AGENT_STATE
577
624
  })
578
625
  );
626
+ }
579
627
 
580
- this.observability?.emit(
581
- {
582
- displayMessage: "Connection established",
583
- id: nanoid(),
584
- payload: {
585
- connectionId: connection.id
586
- },
587
- timestamp: Date.now(),
588
- 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
589
641
  },
590
- this.ctx
591
- );
592
- return this._tryCatch(() => _onConnect(connection, ctx));
593
- }, 20);
642
+ timestamp: Date.now(),
643
+ type: "connect"
644
+ },
645
+ this.ctx
646
+ );
647
+ return this._tryCatch(() => _onConnect(connection, ctx));
594
648
  }
595
649
  );
596
650
  };
597
651
 
598
652
  const _onStart = this.onStart.bind(this);
599
- this.onStart = async () => {
653
+ this.onStart = async (props?: Props) => {
600
654
  return agentContext.run(
601
655
  {
602
656
  agent: this,
@@ -610,15 +664,20 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
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
- this.broadcast(
614
- JSON.stringify({
615
- mcp: this.getMcpServers(),
616
- type: "cf_agent_mcp_servers"
617
- })
618
- );
667
+ this.broadcastMcpServers();
619
668
 
620
669
  // from DO storage, reconnect to all servers not currently in the oauth flow using our saved auth information
621
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
+
622
681
  servers.forEach((server) => {
623
682
  this._connectToMcpServerInternal(
624
683
  server.name,
@@ -634,12 +693,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
634
693
  )
635
694
  .then(() => {
636
695
  // Broadcast updated MCP servers state after each server connects
637
- this.broadcast(
638
- JSON.stringify({
639
- mcp: this.getMcpServers(),
640
- type: "cf_agent_mcp_servers"
641
- })
642
- );
696
+ this.broadcastMcpServers();
643
697
  })
644
698
  .catch((error) => {
645
699
  console.error(
@@ -647,16 +701,11 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
647
701
  error
648
702
  );
649
703
  // Still broadcast even if connection fails, so clients know about the failure
650
- this.broadcast(
651
- JSON.stringify({
652
- mcp: this.getMcpServers(),
653
- type: "cf_agent_mcp_servers"
654
- })
655
- );
704
+ this.broadcastMcpServers();
656
705
  });
657
706
  });
658
707
  }
659
- return _onStart();
708
+ return _onStart(props);
660
709
  });
661
710
  }
662
711
  );
@@ -679,7 +728,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
679
728
  this.broadcast(
680
729
  JSON.stringify({
681
730
  state: state,
682
- type: "cf_agent_state"
731
+ type: MessageType.CF_AGENT_STATE
683
732
  }),
684
733
  source !== "server" ? [source.id] : []
685
734
  );
@@ -832,41 +881,37 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
832
881
  while (proto && proto !== Object.prototype && depth < 10) {
833
882
  const methodNames = Object.getOwnPropertyNames(proto);
834
883
  for (const methodName of methodNames) {
835
- // 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,
836
887
  if (
837
888
  baseMethods.has(methodName) ||
838
889
  methodName.startsWith("_") ||
839
- typeof this[methodName as keyof this] !== "function"
890
+ !descriptor ||
891
+ !!descriptor.get ||
892
+ typeof descriptor.value !== "function"
840
893
  ) {
841
894
  continue;
842
895
  }
843
- // If the method doesn't exist in base prototypes, it's a custom method
844
- if (!baseMethods.has(methodName)) {
845
- const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
846
- if (descriptor && typeof descriptor.value === "function") {
847
- // Wrap the custom method with context
848
-
849
- const wrappedFunction = withAgentContext(
850
- // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
851
- this[methodName as keyof this] as (...args: any[]) => any
852
- // biome-ignore lint/suspicious/noExplicitAny: I can't typescript
853
- ) as any;
854
-
855
- // if the method is callable, copy the metadata from the original method
856
- if (this._isCallable(methodName)) {
857
- callableMetadata.set(
858
- wrappedFunction,
859
- callableMetadata.get(
860
- this[methodName as keyof this] as Function
861
- )!
862
- );
863
- }
864
896
 
865
- // set the wrapped function on the prototype
866
- this.constructor.prototype[methodName as keyof this] =
867
- wrappedFunction;
868
- }
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
+ );
869
911
  }
912
+
913
+ // set the wrapped function on the prototype
914
+ this.constructor.prototype[methodName as keyof this] = wrappedFunction;
870
915
  }
871
916
 
872
917
  proto = Object.getPrototypeOf(proto);
@@ -1349,6 +1394,8 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1349
1394
  // delete all alarms
1350
1395
  await this.ctx.storage.deleteAlarm();
1351
1396
  await this.ctx.storage.deleteAll();
1397
+ this._disposables.dispose();
1398
+ await this.mcp.dispose?.();
1352
1399
  this.ctx.abort("destroyed"); // enforce that the agent is evicted
1353
1400
 
1354
1401
  this.observability?.emit(
@@ -1374,25 +1421,42 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1374
1421
  /**
1375
1422
  * Connect to a new MCP Server
1376
1423
  *
1424
+ * @param serverName Name of the MCP server
1377
1425
  * @param url MCP Server SSE URL
1378
- * @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.
1379
1427
  * @param agentsPrefix agents routing prefix if not using `agents`
1380
- * @param options MCP client and transport (header) options
1428
+ * @param options MCP client and transport options
1381
1429
  * @returns authUrl
1382
1430
  */
1383
1431
  async addMcpServer(
1384
1432
  serverName: string,
1385
1433
  url: string,
1386
- callbackHost: string,
1434
+ callbackHost?: string,
1387
1435
  agentsPrefix = "agents",
1388
1436
  options?: {
1389
1437
  client?: ConstructorParameters<typeof Client>[1];
1390
1438
  transport?: {
1391
- headers: HeadersInit;
1439
+ headers?: HeadersInit;
1440
+ type?: TransportType;
1392
1441
  };
1393
1442
  }
1394
1443
  ): Promise<{ id: string; authUrl: string | undefined }> {
1395
- 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`;
1396
1460
 
1397
1461
  const result = await this._connectToMcpServerInternal(
1398
1462
  serverName,
@@ -1400,6 +1464,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1400
1464
  callbackUrl,
1401
1465
  options
1402
1466
  );
1467
+
1403
1468
  this.sql`
1404
1469
  INSERT
1405
1470
  OR REPLACE INTO cf_agents_mcp_servers (id, name, server_url, client_id, auth_url, callback_url, server_options)
@@ -1414,17 +1479,12 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1414
1479
  );
1415
1480
  `;
1416
1481
 
1417
- this.broadcast(
1418
- JSON.stringify({
1419
- mcp: this.getMcpServers(),
1420
- type: "cf_agent_mcp_servers"
1421
- })
1422
- );
1482
+ this.broadcastMcpServers();
1423
1483
 
1424
1484
  return result;
1425
1485
  }
1426
1486
 
1427
- async _connectToMcpServerInternal(
1487
+ private async _connectToMcpServerInternal(
1428
1488
  _serverName: string,
1429
1489
  url: string,
1430
1490
  callbackUrl: string,
@@ -1440,6 +1500,7 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1440
1500
  */
1441
1501
  transport?: {
1442
1502
  headers?: HeadersInit;
1503
+ type?: TransportType;
1443
1504
  };
1444
1505
  },
1445
1506
  reconnect?: {
@@ -1464,6 +1525,9 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1464
1525
  }
1465
1526
  }
1466
1527
 
1528
+ // Use the transport type specified in options, or default to "auto"
1529
+ const transportType: TransportType = options?.transport?.type ?? "auto";
1530
+
1467
1531
  // allows passing through transport headers if necessary
1468
1532
  // this handles some non-standard bearer auth setups (i.e. MCP server behind CF access instead of OAuth)
1469
1533
  let headerTransportOpts: SSEClientTransportOptions = {};
@@ -1487,7 +1551,8 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1487
1551
  reconnect,
1488
1552
  transport: {
1489
1553
  ...headerTransportOpts,
1490
- authProvider
1554
+ authProvider,
1555
+ type: transportType
1491
1556
  }
1492
1557
  });
1493
1558
 
@@ -1500,15 +1565,11 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1500
1565
 
1501
1566
  async removeMcpServer(id: string) {
1502
1567
  this.mcp.closeConnection(id);
1568
+ this.mcp.unregisterCallbackUrl(id);
1503
1569
  this.sql`
1504
1570
  DELETE FROM cf_agents_mcp_servers WHERE id = ${id};
1505
1571
  `;
1506
- this.broadcast(
1507
- JSON.stringify({
1508
- mcp: this.getMcpServers(),
1509
- type: "cf_agent_mcp_servers"
1510
- })
1511
- );
1572
+ this.broadcastMcpServers();
1512
1573
  }
1513
1574
 
1514
1575
  getMcpServers(): MCPServersState {
@@ -1540,8 +1601,53 @@ export class Agent<Env = typeof env, State = unknown> extends Server<Env> {
1540
1601
 
1541
1602
  return mcpState;
1542
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
+ }
1543
1646
  }
1544
1647
 
1648
+ // A set of classes that have been wrapped with agent context
1649
+ const wrappedClasses = new Set<typeof Agent.prototype.constructor>();
1650
+
1545
1651
  /**
1546
1652
  * Namespace for creating Agent instances
1547
1653
  * @template Agentic Type of the Agent class
@@ -1857,12 +1963,17 @@ export type EmailSendOptions = {
1857
1963
  * @param options Options for Agent creation
1858
1964
  * @returns Promise resolving to an Agent instance stub
1859
1965
  */
1860
- 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
+ >(
1861
1971
  namespace: AgentNamespace<T>,
1862
1972
  name: string,
1863
1973
  options?: {
1864
1974
  jurisdiction?: DurableObjectJurisdiction;
1865
1975
  locationHint?: DurableObjectLocationHint;
1976
+ props?: Props;
1866
1977
  }
1867
1978
  ) {
1868
1979
  return getServerByName<Env, T>(namespace, name, options);
@@ -1894,7 +2005,7 @@ export class StreamingResponse {
1894
2005
  id: this._id,
1895
2006
  result: chunk,
1896
2007
  success: true,
1897
- type: "rpc"
2008
+ type: MessageType.RPC
1898
2009
  };
1899
2010
  this._connection.send(JSON.stringify(response));
1900
2011
  }
@@ -1913,7 +2024,7 @@ export class StreamingResponse {
1913
2024
  id: this._id,
1914
2025
  result: finalChunk,
1915
2026
  success: true,
1916
- type: "rpc"
2027
+ type: MessageType.RPC
1917
2028
  };
1918
2029
  this._connection.send(JSON.stringify(response));
1919
2030
  }