agents 0.6.0 → 0.7.0

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.
@@ -0,0 +1,231 @@
1
+ //#region src/observability/base.d.ts
2
+ /**
3
+ * Base event structure for all observability events
4
+ */
5
+ type BaseEvent<
6
+ T extends string,
7
+ Payload extends Record<string, unknown> = Record<string, never>
8
+ > = {
9
+ type: T;
10
+ /**
11
+ * The payload of the event
12
+ */
13
+ payload: Payload;
14
+ /**
15
+ * The timestamp of the event in milliseconds since epoch
16
+ */
17
+ timestamp: number;
18
+ };
19
+ //#endregion
20
+ //#region src/observability/mcp.d.ts
21
+ /**
22
+ * MCP-specific observability events
23
+ * These track the lifecycle of MCP connections and operations
24
+ */
25
+ type MCPObservabilityEvent =
26
+ | BaseEvent<
27
+ "mcp:client:preconnect",
28
+ {
29
+ serverId: string;
30
+ }
31
+ >
32
+ | BaseEvent<
33
+ "mcp:client:connect",
34
+ {
35
+ url: string;
36
+ transport: string;
37
+ state: string;
38
+ error?: string;
39
+ }
40
+ >
41
+ | BaseEvent<
42
+ "mcp:client:authorize",
43
+ {
44
+ serverId: string;
45
+ authUrl: string;
46
+ clientId?: string;
47
+ }
48
+ >
49
+ | BaseEvent<
50
+ "mcp:client:discover",
51
+ {
52
+ url?: string;
53
+ state?: string;
54
+ error?: string;
55
+ capability?: string;
56
+ }
57
+ >;
58
+ //#endregion
59
+ //#region src/observability/agent.d.ts
60
+ /**
61
+ * Agent-specific observability events
62
+ * These track the lifecycle and operations of an Agent
63
+ */
64
+ type AgentObservabilityEvent =
65
+ | BaseEvent<"state:update">
66
+ | BaseEvent<
67
+ "rpc",
68
+ {
69
+ method: string;
70
+ streaming?: boolean;
71
+ }
72
+ >
73
+ | BaseEvent<
74
+ "rpc:error",
75
+ {
76
+ method: string;
77
+ error: string;
78
+ }
79
+ >
80
+ | BaseEvent<"message:request">
81
+ | BaseEvent<"message:response">
82
+ | BaseEvent<"message:clear">
83
+ | BaseEvent<
84
+ "message:cancel",
85
+ {
86
+ requestId: string;
87
+ }
88
+ >
89
+ | BaseEvent<
90
+ "message:error",
91
+ {
92
+ error: string;
93
+ }
94
+ >
95
+ | BaseEvent<
96
+ "tool:result",
97
+ {
98
+ toolCallId: string;
99
+ toolName: string;
100
+ }
101
+ >
102
+ | BaseEvent<
103
+ "tool:approval",
104
+ {
105
+ toolCallId: string;
106
+ approved: boolean;
107
+ }
108
+ >
109
+ | BaseEvent<
110
+ "schedule:create",
111
+ {
112
+ callback: string;
113
+ id: string;
114
+ }
115
+ >
116
+ | BaseEvent<
117
+ "schedule:execute",
118
+ {
119
+ callback: string;
120
+ id: string;
121
+ }
122
+ >
123
+ | BaseEvent<
124
+ "schedule:cancel",
125
+ {
126
+ callback: string;
127
+ id: string;
128
+ }
129
+ >
130
+ | BaseEvent<
131
+ "schedule:retry",
132
+ {
133
+ callback: string;
134
+ id: string;
135
+ attempt: number;
136
+ maxAttempts: number;
137
+ }
138
+ >
139
+ | BaseEvent<
140
+ "schedule:error",
141
+ {
142
+ callback: string;
143
+ id: string;
144
+ error: string;
145
+ attempts: number;
146
+ }
147
+ >
148
+ | BaseEvent<
149
+ "queue:retry",
150
+ {
151
+ callback: string;
152
+ id: string;
153
+ attempt: number;
154
+ maxAttempts: number;
155
+ }
156
+ >
157
+ | BaseEvent<
158
+ "queue:error",
159
+ {
160
+ callback: string;
161
+ id: string;
162
+ error: string;
163
+ attempts: number;
164
+ }
165
+ >
166
+ | BaseEvent<"destroy">
167
+ | BaseEvent<
168
+ "connect",
169
+ {
170
+ connectionId: string;
171
+ }
172
+ >
173
+ | BaseEvent<
174
+ "workflow:start",
175
+ {
176
+ workflowId: string;
177
+ workflowName?: string;
178
+ }
179
+ >
180
+ | BaseEvent<
181
+ "workflow:event",
182
+ {
183
+ workflowId: string;
184
+ eventType?: string;
185
+ }
186
+ >
187
+ | BaseEvent<
188
+ "workflow:approved",
189
+ {
190
+ workflowId: string;
191
+ reason?: string;
192
+ }
193
+ >
194
+ | BaseEvent<
195
+ "workflow:rejected",
196
+ {
197
+ workflowId: string;
198
+ reason?: string;
199
+ }
200
+ >
201
+ | BaseEvent<
202
+ "workflow:terminated",
203
+ {
204
+ workflowId: string;
205
+ workflowName?: string;
206
+ }
207
+ >
208
+ | BaseEvent<
209
+ "workflow:paused",
210
+ {
211
+ workflowId: string;
212
+ workflowName?: string;
213
+ }
214
+ >
215
+ | BaseEvent<
216
+ "workflow:resumed",
217
+ {
218
+ workflowId: string;
219
+ workflowName?: string;
220
+ }
221
+ >
222
+ | BaseEvent<
223
+ "workflow:restarted",
224
+ {
225
+ workflowId: string;
226
+ workflowName?: string;
227
+ }
228
+ >;
229
+ //#endregion
230
+ export { MCPObservabilityEvent as n, AgentObservabilityEvent as t };
231
+ //# sourceMappingURL=agent-DnmmRjyv.d.ts.map
@@ -1,4 +1,3 @@
1
- import { nanoid } from "nanoid";
2
1
  import { getServerByName } from "partyserver";
3
2
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
3
  import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
@@ -66,9 +65,18 @@ function isTransportNotImplemented(error) {
66
65
  //#endregion
67
66
  //#region src/mcp/rpc.ts
68
67
  const RPC_DO_PREFIX = "rpc:";
68
+ function makeInvalidRequestError(id) {
69
+ return {
70
+ jsonrpc: "2.0",
71
+ id: id ?? null,
72
+ error: {
73
+ code: -32600,
74
+ message: "Invalid Request"
75
+ }
76
+ };
77
+ }
69
78
  function validateBatch(batch) {
70
79
  if (batch.length === 0) throw new Error("Invalid JSON-RPC batch: array must not be empty");
71
- for (const msg of batch) JSONRPCMessageSchema.parse(msg);
72
80
  }
73
81
  var RPCClientTransport = class {
74
82
  constructor(options) {
@@ -155,7 +163,11 @@ var RPCServerTransport = class {
155
163
  }
156
164
  return responses.length === 0 ? void 0 : responses;
157
165
  }
158
- JSONRPCMessageSchema.parse(message);
166
+ try {
167
+ JSONRPCMessageSchema.parse(message);
168
+ } catch {
169
+ return makeInvalidRequestError(typeof message === "object" && message !== null && "id" in message ? message.id : null);
170
+ }
159
171
  this._pendingResponse = null;
160
172
  if (!("id" in message)) {
161
173
  this.onmessage?.(message);
@@ -249,29 +261,25 @@ var MCPClientConnection = class {
249
261
  this.lastConnectedTransport = res.transport;
250
262
  this._onObservabilityEvent.fire({
251
263
  type: "mcp:client:connect",
252
- displayMessage: `Connected successfully using ${res.transport} transport for ${this.url.toString()}`,
253
264
  payload: {
254
265
  url: this.url.toString(),
255
266
  transport: res.transport,
256
267
  state: this.connectionState
257
268
  },
258
- timestamp: Date.now(),
259
- id: nanoid()
269
+ timestamp: Date.now()
260
270
  });
261
271
  return;
262
272
  } else if (res.state === MCPConnectionState.FAILED && res.error) {
263
273
  const errorMessage = toErrorMessage(res.error);
264
274
  this._onObservabilityEvent.fire({
265
275
  type: "mcp:client:connect",
266
- displayMessage: `Failed to connect to ${this.url.toString()}: ${errorMessage}`,
267
276
  payload: {
268
277
  url: this.url.toString(),
269
278
  transport: transportType,
270
279
  state: this.connectionState,
271
280
  error: errorMessage
272
281
  },
273
- timestamp: Date.now(),
274
- id: nanoid()
282
+ timestamp: Date.now()
275
283
  });
276
284
  return errorMessage;
277
285
  }
@@ -369,13 +377,11 @@ var MCPClientConnection = class {
369
377
  } catch (error) {
370
378
  this._onObservabilityEvent.fire({
371
379
  type: "mcp:client:discover",
372
- displayMessage: `Failed to discover capabilities for ${this.url.toString()}: ${toErrorMessage(error)}`,
373
380
  payload: {
374
381
  url: this.url.toString(),
375
382
  error: toErrorMessage(error)
376
383
  },
377
- timestamp: Date.now(),
378
- id: nanoid()
384
+ timestamp: Date.now()
379
385
  });
380
386
  throw error;
381
387
  }
@@ -393,13 +399,11 @@ var MCPClientConnection = class {
393
399
  if (this.connectionState !== MCPConnectionState.CONNECTED && this.connectionState !== MCPConnectionState.READY) {
394
400
  this._onObservabilityEvent.fire({
395
401
  type: "mcp:client:discover",
396
- displayMessage: `Discovery skipped for ${this.url.toString()}, state is ${this.connectionState}`,
397
402
  payload: {
398
403
  url: this.url.toString(),
399
404
  state: this.connectionState
400
405
  },
401
- timestamp: Date.now(),
402
- id: nanoid()
406
+ timestamp: Date.now()
403
407
  });
404
408
  return {
405
409
  success: false,
@@ -433,10 +437,8 @@ var MCPClientConnection = class {
433
437
  this.connectionState = MCPConnectionState.READY;
434
438
  this._onObservabilityEvent.fire({
435
439
  type: "mcp:client:discover",
436
- displayMessage: `Discovery completed for ${this.url.toString()}`,
437
440
  payload: { url: this.url.toString() },
438
- timestamp: Date.now(),
439
- id: nanoid()
441
+ timestamp: Date.now()
440
442
  });
441
443
  return { success: true };
442
444
  } catch (e) {
@@ -582,14 +584,12 @@ var MCPClientConnection = class {
582
584
  const url = this.url.toString();
583
585
  this._onObservabilityEvent.fire({
584
586
  type: "mcp:client:discover",
585
- displayMessage: `The server advertised support for the capability ${method.split("/")[0]}, but returned "Method not found" for '${method}' for ${url}`,
586
587
  payload: {
587
588
  url,
588
589
  capability: method.split("/")[0],
589
590
  error: toErrorMessage(e)
590
591
  },
591
- timestamp: Date.now(),
592
- id: nanoid()
592
+ timestamp: Date.now()
593
593
  });
594
594
  return empty;
595
595
  }
@@ -600,4 +600,4 @@ var MCPClientConnection = class {
600
600
 
601
601
  //#endregion
602
602
  export { RPC_DO_PREFIX as a, Emitter as c, RPCServerTransport as i, MCPConnectionState as n, toErrorMessage as o, RPCClientTransport as r, DisposableStore as s, MCPClientConnection as t };
603
- //# sourceMappingURL=client-connection-BqngKttp.js.map
603
+ //# sourceMappingURL=client-connection-D3Wcd6Q6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-connection-D3Wcd6Q6.js","names":[],"sources":["../src/core/events.ts","../src/mcp/errors.ts","../src/mcp/rpc.ts","../src/mcp/client-connection.ts"],"sourcesContent":["export interface Disposable {\n dispose(): void;\n}\n\nexport function toDisposable(fn: () => void): Disposable {\n return { dispose: fn };\n}\n\nexport class DisposableStore implements Disposable {\n private readonly _items: Disposable[] = [];\n\n add<T extends Disposable>(d: T): T {\n this._items.push(d);\n return d;\n }\n\n dispose(): void {\n while (this._items.length) {\n try {\n this._items.pop()!.dispose();\n } catch {\n // best-effort cleanup\n }\n }\n }\n}\n\nexport type Event<T> = (listener: (e: T) => void) => Disposable;\n\nexport class Emitter<T> implements Disposable {\n private _listeners: Set<(e: T) => void> = new Set();\n\n readonly event: Event<T> = (listener) => {\n this._listeners.add(listener);\n return toDisposable(() => this._listeners.delete(listener));\n };\n\n fire(data: T): void {\n for (const listener of [...this._listeners]) {\n try {\n listener(data);\n } catch (err) {\n // do not let one bad listener break others\n console.error(\"Emitter listener error:\", err);\n }\n }\n }\n\n dispose(): void {\n this._listeners.clear();\n }\n}\n","export function toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction getErrorCode(error: unknown): number | undefined {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n typeof (error as { code: unknown }).code === \"number\"\n ) {\n return (error as { code: number }).code;\n }\n return undefined;\n}\n\nexport function isUnauthorized(error: unknown): boolean {\n const code = getErrorCode(error);\n if (code === 401) return true;\n\n const msg = toErrorMessage(error);\n return msg.includes(\"Unauthorized\") || msg.includes(\"401\");\n}\n\n// MCP SDK change (v1.24.0, commit 6b90e1a):\n// - Old: Error POSTing to endpoint (HTTP 404): Not Found\n// - New: StreamableHTTPError with code: 404 and message Error POSTing to endpoint: Not Found\nexport function isTransportNotImplemented(error: unknown): boolean {\n const code = getErrorCode(error);\n if (code === 404 || code === 405) return true;\n\n const msg = toErrorMessage(error);\n return (\n msg.includes(\"404\") ||\n msg.includes(\"405\") ||\n msg.includes(\"Not Implemented\") ||\n msg.includes(\"not implemented\")\n );\n}\n","import type {\n Transport,\n TransportSendOptions\n} from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type {\n JSONRPCMessage,\n MessageExtraInfo\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { JSONRPCMessageSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { getServerByName } from \"partyserver\";\nimport type { McpAgent } from \".\";\n\nexport const RPC_DO_PREFIX = \"rpc:\";\n\nfunction makeInvalidRequestError(id: unknown): JSONRPCMessage {\n return {\n jsonrpc: \"2.0\",\n id: id ?? null,\n error: {\n code: -32600,\n message: \"Invalid Request\"\n }\n } as JSONRPCMessage;\n}\n\nfunction validateBatch(batch: JSONRPCMessage[]): void {\n if (batch.length === 0) {\n throw new Error(\"Invalid JSON-RPC batch: array must not be empty\");\n }\n}\n\nexport interface RPCClientTransportOptions<T extends McpAgent = McpAgent> {\n namespace: DurableObjectNamespace<T>;\n name: string;\n props?: Record<string, unknown>;\n}\n\nexport class RPCClientTransport implements Transport {\n private _namespace: DurableObjectNamespace<McpAgent>;\n private _name: string;\n private _props?: Record<string, unknown>;\n private _stub?: DurableObjectStub<McpAgent>;\n private _started = false;\n private _protocolVersion?: string;\n\n sessionId?: string;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;\n\n constructor(options: RPCClientTransportOptions<McpAgent>) {\n this._namespace = options.namespace;\n this._name = options.name;\n this._props = options.props;\n }\n\n setProtocolVersion(version: string): void {\n this._protocolVersion = version;\n }\n\n getProtocolVersion(): string | undefined {\n return this._protocolVersion;\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\"Transport already started\");\n }\n\n const doName = `${RPC_DO_PREFIX}${this._name}`;\n this._stub = await getServerByName(this._namespace, doName, {\n props: this._props\n });\n\n this._started = true;\n }\n\n async close(): Promise<void> {\n this._started = false;\n this._stub = undefined;\n this.onclose?.();\n }\n\n async send(\n message: JSONRPCMessage | JSONRPCMessage[],\n options?: TransportSendOptions\n ): Promise<void> {\n if (!this._started || !this._stub) {\n throw new Error(\"Transport not started\");\n }\n\n try {\n const result: JSONRPCMessage | JSONRPCMessage[] | undefined =\n await this._stub.handleMcpMessage(message);\n\n if (!result) {\n return;\n }\n\n const extra: MessageExtraInfo | undefined = options?.relatedRequestId\n ? { requestInfo: { headers: {} } }\n : undefined;\n\n const messages = Array.isArray(result) ? result : [result];\n for (const msg of messages) {\n this.onmessage?.(msg, extra);\n }\n } catch (error) {\n this.onerror?.(error as Error);\n throw error;\n }\n }\n}\n\nexport interface RPCServerTransportOptions {\n timeout?: number;\n}\n\nexport class RPCServerTransport implements Transport {\n private _started = false;\n private _pendingResponse: JSONRPCMessage | JSONRPCMessage[] | null = null;\n private _responseResolver: (() => void) | null = null;\n private _protocolVersion?: string;\n private _timeout: number;\n\n sessionId?: string;\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;\n\n constructor(options?: RPCServerTransportOptions) {\n this._timeout = options?.timeout ?? 60000;\n }\n\n setProtocolVersion(version: string): void {\n this._protocolVersion = version;\n }\n\n getProtocolVersion(): string | undefined {\n return this._protocolVersion;\n }\n\n async start(): Promise<void> {\n if (this._started) {\n throw new Error(\"Transport already started\");\n }\n this._started = true;\n }\n\n async close(): Promise<void> {\n this._started = false;\n this.onclose?.();\n if (this._responseResolver) {\n this._responseResolver();\n this._responseResolver = null;\n }\n }\n\n async send(\n message: JSONRPCMessage,\n _options?: TransportSendOptions\n ): Promise<void> {\n if (!this._started) {\n throw new Error(\"Transport not started\");\n }\n\n if (!this._pendingResponse) {\n this._pendingResponse = message;\n } else if (Array.isArray(this._pendingResponse)) {\n this._pendingResponse.push(message);\n } else {\n this._pendingResponse = [this._pendingResponse, message];\n }\n\n if (this._responseResolver) {\n const resolver = this._responseResolver;\n queueMicrotask(() => resolver());\n }\n }\n\n async handle(\n message: JSONRPCMessage | JSONRPCMessage[]\n ): Promise<JSONRPCMessage | JSONRPCMessage[] | undefined> {\n if (!this._started) {\n throw new Error(\"Transport not started\");\n }\n\n if (Array.isArray(message)) {\n validateBatch(message);\n\n const responses: JSONRPCMessage[] = [];\n for (const msg of message) {\n const response = await this.handle(msg);\n if (response !== undefined) {\n if (Array.isArray(response)) {\n responses.push(...response);\n } else {\n responses.push(response);\n }\n }\n }\n\n return responses.length === 0 ? undefined : responses;\n }\n\n try {\n JSONRPCMessageSchema.parse(message);\n } catch {\n const id =\n typeof message === \"object\" && message !== null && \"id\" in message\n ? (message as { id: unknown }).id\n : null;\n return makeInvalidRequestError(id);\n }\n\n this._pendingResponse = null;\n\n const isNotification = !(\"id\" in message);\n if (isNotification) {\n this.onmessage?.(message);\n return undefined;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n const responsePromise = new Promise<void>((resolve, reject) => {\n timeoutId = setTimeout(() => {\n this._responseResolver = null;\n reject(\n new Error(\n `Request timeout: No response received within ${this._timeout}ms`\n )\n );\n }, this._timeout);\n\n this._responseResolver = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n this._responseResolver = null;\n resolve();\n };\n });\n\n this.onmessage?.(message);\n\n try {\n await responsePromise;\n } catch (error) {\n this._pendingResponse = null;\n this._responseResolver = null;\n throw error;\n }\n\n const response = this._pendingResponse;\n this._pendingResponse = null;\n\n return response ?? undefined;\n }\n}\n","import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport {\n SSEClientTransport,\n type SSEClientTransportOptions\n} from \"@modelcontextprotocol/sdk/client/sse.js\";\nimport {\n StreamableHTTPClientTransport,\n type StreamableHTTPClientTransportOptions\n} from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\n// Import types directly from MCP SDK\nimport type {\n Prompt,\n Resource,\n Tool\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport {\n type ClientCapabilities,\n type ElicitRequest,\n ElicitRequestSchema,\n type ElicitResult,\n type ListPromptsResult,\n type ListResourceTemplatesResult,\n type ListResourcesResult,\n type ListToolsResult,\n PromptListChangedNotificationSchema,\n ResourceListChangedNotificationSchema,\n type ResourceTemplate,\n type ServerCapabilities,\n ToolListChangedNotificationSchema\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Emitter, type Event } from \"../core/events\";\nimport type { MCPObservabilityEvent } from \"../observability/mcp\";\nimport type { AgentMcpOAuthProvider } from \"./do-oauth-client-provider\";\nimport {\n isTransportNotImplemented,\n isUnauthorized,\n toErrorMessage\n} from \"./errors\";\nimport { RPCClientTransport, type RPCClientTransportOptions } from \"./rpc\";\nimport type {\n BaseTransportType,\n HttpTransportType,\n TransportType,\n McpClientOptions\n} from \"./types\";\n\n/**\n * Connection state machine for MCP client connections.\n *\n * State transitions:\n * - Non-OAuth: init() → CONNECTING → DISCOVERING → READY\n * - OAuth: init() → AUTHENTICATING → (callback) → CONNECTING → DISCOVERING → READY\n * - Any state can transition to FAILED on error\n */\nexport const MCPConnectionState = {\n /** Waiting for OAuth authorization to complete */\n AUTHENTICATING: \"authenticating\",\n /** Establishing transport connection to MCP server */\n CONNECTING: \"connecting\",\n /** Transport connection established */\n CONNECTED: \"connected\",\n /** Discovering server capabilities (tools, resources, prompts) */\n DISCOVERING: \"discovering\",\n /** Fully connected and ready to use */\n READY: \"ready\",\n /** Connection failed at some point */\n FAILED: \"failed\"\n} as const;\n\n/**\n * Connection state type for MCP client connections.\n */\nexport type MCPConnectionState =\n (typeof MCPConnectionState)[keyof typeof MCPConnectionState];\n\n/**\n * Transport options for MCP client connections.\n * Combines transport-specific options with auth provider and type selection.\n */\nexport type MCPTransportOptions = (\n | SSEClientTransportOptions\n | StreamableHTTPClientTransportOptions\n | RPCClientTransportOptions\n) & {\n authProvider?: AgentMcpOAuthProvider;\n type?: TransportType;\n};\n\nexport type MCPClientConnectionResult = {\n state: MCPConnectionState;\n error?: Error;\n transport?: BaseTransportType;\n};\n\n/**\n * Result of a discovery operation.\n * success indicates whether discovery completed successfully.\n * error is present when success is false.\n */\nexport type MCPDiscoveryResult = {\n success: boolean;\n error?: string;\n};\n\nexport class MCPClientConnection {\n client: Client;\n connectionState: MCPConnectionState = MCPConnectionState.CONNECTING;\n connectionError: string | null = null;\n lastConnectedTransport: BaseTransportType | undefined;\n instructions?: string;\n tools: Tool[] = [];\n prompts: Prompt[] = [];\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n serverCapabilities: ServerCapabilities | undefined;\n\n /** Tracks in-flight discovery to allow cancellation */\n private _discoveryAbortController: AbortController | undefined;\n\n private readonly _onObservabilityEvent = new Emitter<MCPObservabilityEvent>();\n public readonly onObservabilityEvent: Event<MCPObservabilityEvent> =\n this._onObservabilityEvent.event;\n\n constructor(\n public url: URL,\n info: ConstructorParameters<typeof Client>[0],\n public options: {\n transport: MCPTransportOptions;\n client: McpClientOptions;\n } = { client: {}, transport: {} }\n ) {\n const clientOptions = {\n ...options.client,\n capabilities: {\n ...options.client?.capabilities,\n elicitation: {}\n } as ClientCapabilities\n };\n\n this.client = new Client(info, clientOptions);\n }\n\n /**\n * Initialize a client connection, if authentication is required, the connection will be in the AUTHENTICATING state\n * Sets connection state based on the result and emits observability events\n *\n * @returns Error message if connection failed, undefined otherwise\n */\n async init(): Promise<string | undefined> {\n const transportType = this.options.transport.type;\n if (!transportType) {\n throw new Error(\"Transport type must be specified\");\n }\n\n const res = await this.tryConnect(transportType);\n\n // Set the connection state\n this.connectionState = res.state;\n\n // Handle the result and emit appropriate events\n if (res.state === MCPConnectionState.CONNECTED && res.transport) {\n // Set up elicitation request handler after successful connection\n this.client.setRequestHandler(\n ElicitRequestSchema,\n async (request: ElicitRequest) => {\n return await this.handleElicitationRequest(request);\n }\n );\n\n this.lastConnectedTransport = res.transport;\n\n this._onObservabilityEvent.fire({\n type: \"mcp:client:connect\",\n payload: {\n url: this.url.toString(),\n transport: res.transport,\n state: this.connectionState\n },\n timestamp: Date.now()\n });\n return undefined;\n } else if (res.state === MCPConnectionState.FAILED && res.error) {\n const errorMessage = toErrorMessage(res.error);\n this._onObservabilityEvent.fire({\n type: \"mcp:client:connect\",\n payload: {\n url: this.url.toString(),\n transport: transportType,\n state: this.connectionState,\n error: errorMessage\n },\n timestamp: Date.now()\n });\n return errorMessage;\n }\n return undefined;\n }\n\n /**\n * Finish OAuth by probing transports based on configured type.\n * - Explicit: finish on that transport\n * - Auto: try streamable-http, then sse on 404/405/Not Implemented\n */\n private async finishAuthProbe(code: string): Promise<void> {\n if (!this.options.transport.authProvider) {\n throw new Error(\"No auth provider configured\");\n }\n\n const configuredType = this.options.transport.type;\n if (!configuredType) {\n throw new Error(\"Transport type must be specified\");\n }\n\n const finishAuth = async (base: HttpTransportType) => {\n const transport = this.getTransport(base);\n if (\n \"finishAuth\" in transport &&\n typeof transport.finishAuth === \"function\"\n ) {\n await transport.finishAuth(code);\n }\n };\n\n if (configuredType === \"rpc\") {\n throw new Error(\"RPC transport does not support authentication\");\n }\n\n if (configuredType === \"sse\" || configuredType === \"streamable-http\") {\n await finishAuth(configuredType);\n return;\n }\n\n // For \"auto\" mode, try streamable-http first, then fall back to SSE\n try {\n await finishAuth(\"streamable-http\");\n } catch (e) {\n if (isTransportNotImplemented(e)) {\n await finishAuth(\"sse\");\n return;\n }\n throw e;\n }\n }\n\n /**\n * Complete OAuth authorization\n */\n async completeAuthorization(code: string): Promise<void> {\n if (this.connectionState !== MCPConnectionState.AUTHENTICATING) {\n throw new Error(\n \"Connection must be in authenticating state to complete authorization\"\n );\n }\n\n try {\n // Finish OAuth by probing transports per configuration\n await this.finishAuthProbe(code);\n\n // Mark as connecting\n this.connectionState = MCPConnectionState.CONNECTING;\n } catch (error) {\n this.connectionState = MCPConnectionState.FAILED;\n throw error;\n }\n }\n\n /**\n * Discover server capabilities and register tools, resources, prompts, and templates.\n * This method does the work but does not manage connection state - that's handled by discover().\n */\n async discoverAndRegister(): Promise<void> {\n this.serverCapabilities = this.client.getServerCapabilities();\n if (!this.serverCapabilities) {\n throw new Error(\"The MCP Server failed to return server capabilities\");\n }\n\n // Build list of operations to perform based on server capabilities\n type DiscoveryResult =\n | string\n | undefined\n | Tool[]\n | Resource[]\n | Prompt[]\n | ResourceTemplate[];\n const operations: Promise<DiscoveryResult>[] = [];\n const operationNames: string[] = [];\n\n // Instructions (always try to fetch if available)\n operations.push(Promise.resolve(this.client.getInstructions()));\n operationNames.push(\"instructions\");\n\n // Only register capabilities that the server advertises\n if (this.serverCapabilities.tools) {\n operations.push(this.registerTools());\n operationNames.push(\"tools\");\n }\n\n if (this.serverCapabilities.resources) {\n operations.push(this.registerResources());\n operationNames.push(\"resources\");\n }\n\n if (this.serverCapabilities.prompts) {\n operations.push(this.registerPrompts());\n operationNames.push(\"prompts\");\n }\n\n if (this.serverCapabilities.resources) {\n operations.push(this.registerResourceTemplates());\n operationNames.push(\"resource templates\");\n }\n\n try {\n const results = await Promise.all(operations);\n for (let i = 0; i < results.length; i++) {\n const result = results[i];\n const name = operationNames[i];\n\n switch (name) {\n case \"instructions\":\n this.instructions = result as string | undefined;\n break;\n case \"tools\":\n this.tools = result as Tool[];\n break;\n case \"resources\":\n this.resources = result as Resource[];\n break;\n case \"prompts\":\n this.prompts = result as Prompt[];\n break;\n case \"resource templates\":\n this.resourceTemplates = result as ResourceTemplate[];\n break;\n }\n }\n } catch (error) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:discover\",\n payload: {\n url: this.url.toString(),\n error: toErrorMessage(error)\n },\n timestamp: Date.now()\n });\n\n throw error;\n }\n }\n\n /**\n * Discover server capabilities with timeout and cancellation support.\n * If called while a previous discovery is in-flight, the previous discovery will be aborted.\n *\n * @param options Optional configuration\n * @param options.timeoutMs Timeout in milliseconds (default: 15000)\n * @returns Result indicating success/failure with optional error message\n */\n async discover(\n options: { timeoutMs?: number } = {}\n ): Promise<MCPDiscoveryResult> {\n const { timeoutMs = 15000 } = options;\n\n // Check if state allows discovery\n if (\n this.connectionState !== MCPConnectionState.CONNECTED &&\n this.connectionState !== MCPConnectionState.READY\n ) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:discover\",\n payload: {\n url: this.url.toString(),\n state: this.connectionState\n },\n timestamp: Date.now()\n });\n return {\n success: false,\n error: `Discovery skipped - connection in ${this.connectionState} state`\n };\n }\n\n // Cancel any previous in-flight discovery\n if (this._discoveryAbortController) {\n this._discoveryAbortController.abort();\n this._discoveryAbortController = undefined;\n }\n\n // Create a new AbortController for this discovery\n const abortController = new AbortController();\n this._discoveryAbortController = abortController;\n\n this.connectionState = MCPConnectionState.DISCOVERING;\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n // Create timeout promise\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`Discovery timed out after ${timeoutMs}ms`)),\n timeoutMs\n );\n });\n\n // Check if aborted before starting\n if (abortController.signal.aborted) {\n throw new Error(\"Discovery was cancelled\");\n }\n\n // Create an abort promise that rejects when signal fires\n const abortPromise = new Promise<never>((_, reject) => {\n abortController.signal.addEventListener(\"abort\", () => {\n reject(new Error(\"Discovery was cancelled\"));\n });\n });\n\n await Promise.race([\n this.discoverAndRegister(),\n timeoutPromise,\n abortPromise\n ]);\n\n // Clear timeout on success\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n\n // Discovery succeeded - transition to ready\n this.connectionState = MCPConnectionState.READY;\n\n this._onObservabilityEvent.fire({\n type: \"mcp:client:discover\",\n payload: {\n url: this.url.toString()\n },\n timestamp: Date.now()\n });\n\n return { success: true };\n } catch (e) {\n // Always clear the timeout\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n\n // Return to CONNECTED state so user can retry discovery\n this.connectionState = MCPConnectionState.CONNECTED;\n\n const error = e instanceof Error ? e.message : String(e);\n return { success: false, error };\n } finally {\n // Clean up the abort controller\n this._discoveryAbortController = undefined;\n }\n }\n\n /**\n * Cancel any in-flight discovery operation.\n * Called when closing the connection.\n */\n cancelDiscovery(): void {\n if (this._discoveryAbortController) {\n this._discoveryAbortController.abort();\n this._discoveryAbortController = undefined;\n }\n }\n\n /**\n * Notification handler registration for tools\n * Should only be called if serverCapabilities.tools exists\n */\n async registerTools(): Promise<Tool[]> {\n if (this.serverCapabilities?.tools?.listChanged) {\n this.client.setNotificationHandler(\n ToolListChangedNotificationSchema,\n async (_notification) => {\n this.tools = await this.fetchTools();\n }\n );\n }\n\n return this.fetchTools();\n }\n\n /**\n * Notification handler registration for resources\n * Should only be called if serverCapabilities.resources exists\n */\n async registerResources(): Promise<Resource[]> {\n if (this.serverCapabilities?.resources?.listChanged) {\n this.client.setNotificationHandler(\n ResourceListChangedNotificationSchema,\n async (_notification) => {\n this.resources = await this.fetchResources();\n }\n );\n }\n\n return this.fetchResources();\n }\n\n /**\n * Notification handler registration for prompts\n * Should only be called if serverCapabilities.prompts exists\n */\n async registerPrompts(): Promise<Prompt[]> {\n if (this.serverCapabilities?.prompts?.listChanged) {\n this.client.setNotificationHandler(\n PromptListChangedNotificationSchema,\n async (_notification) => {\n this.prompts = await this.fetchPrompts();\n }\n );\n }\n\n return this.fetchPrompts();\n }\n\n async registerResourceTemplates(): Promise<ResourceTemplate[]> {\n return this.fetchResourceTemplates();\n }\n\n async fetchTools() {\n let toolsAgg: Tool[] = [];\n let toolsResult: ListToolsResult = { tools: [] };\n do {\n toolsResult = await this.client\n .listTools({\n cursor: toolsResult.nextCursor\n })\n .catch(this._capabilityErrorHandler({ tools: [] }, \"tools/list\"));\n toolsAgg = toolsAgg.concat(toolsResult.tools);\n } while (toolsResult.nextCursor);\n return toolsAgg;\n }\n\n async fetchResources() {\n let resourcesAgg: Resource[] = [];\n let resourcesResult: ListResourcesResult = { resources: [] };\n do {\n resourcesResult = await this.client\n .listResources({\n cursor: resourcesResult.nextCursor\n })\n .catch(\n this._capabilityErrorHandler({ resources: [] }, \"resources/list\")\n );\n resourcesAgg = resourcesAgg.concat(resourcesResult.resources);\n } while (resourcesResult.nextCursor);\n return resourcesAgg;\n }\n\n async fetchPrompts() {\n let promptsAgg: Prompt[] = [];\n let promptsResult: ListPromptsResult = { prompts: [] };\n do {\n promptsResult = await this.client\n .listPrompts({\n cursor: promptsResult.nextCursor\n })\n .catch(this._capabilityErrorHandler({ prompts: [] }, \"prompts/list\"));\n promptsAgg = promptsAgg.concat(promptsResult.prompts);\n } while (promptsResult.nextCursor);\n return promptsAgg;\n }\n\n async fetchResourceTemplates() {\n let templatesAgg: ResourceTemplate[] = [];\n let templatesResult: ListResourceTemplatesResult = {\n resourceTemplates: []\n };\n do {\n templatesResult = await this.client\n .listResourceTemplates({\n cursor: templatesResult.nextCursor\n })\n .catch(\n this._capabilityErrorHandler(\n { resourceTemplates: [] },\n \"resources/templates/list\"\n )\n );\n templatesAgg = templatesAgg.concat(templatesResult.resourceTemplates);\n } while (templatesResult.nextCursor);\n return templatesAgg;\n }\n\n /**\n * Handle elicitation request from server\n * Automatically uses the Agent's built-in elicitation handling if available\n */\n async handleElicitationRequest(\n _request: ElicitRequest\n ): Promise<ElicitResult> {\n // Elicitation handling must be implemented by the platform\n // For MCP servers, this should be handled by McpAgent.elicitInput()\n throw new Error(\n \"Elicitation handler must be implemented for your platform. Override handleElicitationRequest method.\"\n );\n }\n /**\n * Get the transport for the client\n * @param transportType - The transport type to get\n * @returns The transport for the client\n */\n getTransport(transportType: BaseTransportType) {\n switch (transportType) {\n case \"streamable-http\":\n return new StreamableHTTPClientTransport(\n this.url,\n this.options.transport as StreamableHTTPClientTransportOptions\n );\n case \"sse\":\n return new SSEClientTransport(\n this.url,\n this.options.transport as SSEClientTransportOptions\n );\n case \"rpc\":\n return new RPCClientTransport(\n this.options.transport as RPCClientTransportOptions\n );\n default:\n throw new Error(`Unsupported transport type: ${transportType}`);\n }\n }\n\n private async tryConnect(\n transportType: TransportType\n ): Promise<MCPClientConnectionResult> {\n const transports: BaseTransportType[] =\n transportType === \"auto\" ? [\"streamable-http\", \"sse\"] : [transportType];\n\n for (const currentTransportType of transports) {\n const isLastTransport =\n currentTransportType === transports[transports.length - 1];\n const hasFallback =\n transportType === \"auto\" &&\n currentTransportType === \"streamable-http\" &&\n !isLastTransport;\n\n const transport = this.getTransport(currentTransportType);\n\n try {\n await this.client.connect(transport);\n\n return {\n state: MCPConnectionState.CONNECTED,\n transport: currentTransportType\n };\n } catch (e) {\n const error = e instanceof Error ? e : new Error(String(e));\n\n if (isUnauthorized(error)) {\n return {\n state: MCPConnectionState.AUTHENTICATING\n };\n }\n\n if (isTransportNotImplemented(error) && hasFallback) {\n // Try the next transport\n continue;\n }\n\n return {\n state: MCPConnectionState.FAILED,\n error\n };\n }\n }\n\n // Should never reach here\n return {\n state: MCPConnectionState.FAILED,\n error: new Error(\"No transports available\")\n };\n }\n\n private _capabilityErrorHandler<T>(empty: T, method: string) {\n return (e: { code: number }) => {\n // server is badly behaved and returning invalid capabilities. This commonly occurs for resource templates\n if (e.code === -32601) {\n const url = this.url.toString();\n this._onObservabilityEvent.fire({\n type: \"mcp:client:discover\",\n payload: {\n url,\n capability: method.split(\"/\")[0],\n error: toErrorMessage(e)\n },\n timestamp: Date.now()\n });\n return empty;\n }\n throw e;\n };\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as MCPObservabilityEvent } from "./agent-BHM7Gp_m.js";
1
+ import { n as MCPObservabilityEvent } from "./agent-DnmmRjyv.js";
2
2
  import { AgentMcpOAuthProvider } from "./mcp/do-oauth-client-provider.js";
3
3
  import { McpAgent } from "./mcp/index.js";
4
4
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -601,4 +601,4 @@ export {
601
601
  MaybePromise as x,
602
602
  StreamableHTTPEdgeClientTransport as y
603
603
  };
604
- //# sourceMappingURL=client-storage-D5nDLKMW.d.ts.map
604
+ //# sourceMappingURL=client-storage-tusTuoSF.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { RetryOptions } from "../retries.js";
2
- import "../client-storage-D5nDLKMW.js";
2
+ import "../client-storage-tusTuoSF.js";
3
3
  import { Agent, Schedule } from "../index.js";
4
4
 
5
5
  //#region src/experimental/forever.d.ts
@@ -52,7 +52,7 @@ type RawFiberRow = {
52
52
  created_at: number;
53
53
  };
54
54
  type Constructor<T = object> = new (...args: any[]) => T;
55
- type AgentLike = Constructor<Pick<Agent<Cloudflare.Env>, "sql" | "scheduleEvery" | "cancelSchedule" | "alarm">>;
55
+ type AgentLike = Constructor<Pick<Agent<Cloudflare.Env>, "sql" | "scheduleEvery" | "cancelSchedule" | "alarm" | "keepAlive" | "keepAliveWhile">>;
56
56
  declare function withFibers<TBase extends AgentLike>(Base: TBase, options?: {
57
57
  debugFibers?: boolean;
58
58
  }): {
@@ -61,8 +61,7 @@ declare function withFibers<TBase extends AgentLike>(Base: TBase, options?: {
61
61
  _fiberRecoveryInProgress: boolean; /** @internal */
62
62
  _fiberLastCleanupTime: number; /** @internal */
63
63
  _fiberDebug(msg: string, ...args: unknown[]): void; /** @internal */
64
- _cf_fiberHeartbeat(): Promise<void>;
65
- keepAlive(): Promise<() => void>;
64
+ _cf_keepAliveHeartbeat(): Promise<void>;
66
65
  spawnFiber(methodName: keyof /*elided*/any, payload?: unknown, options?: {
67
66
  maxRetries?: number;
68
67
  }): string;
@@ -93,26 +92,16 @@ declare function withFibers<TBase extends AgentLike>(Base: TBase, options?: {
93
92
  _checkInterruptedFibers(): Promise<void>; /** @internal */
94
93
  _cleanupOrphanedHeartbeats(): void; /** @internal */
95
94
  _maybeCleanupFibers(): void;
95
+ readonly alarm: () => Promise<void>;
96
96
  sql: <T = Record<string, string | number | boolean | null>>(strings: TemplateStringsArray, ...values: (string | number | boolean | null)[]) => T[];
97
97
  scheduleEvery: <T = string>(intervalSeconds: number, callback: keyof Agent<Cloudflare.Env, unknown, Record<string, unknown>>, payload?: T | undefined, options?: {
98
98
  retry?: RetryOptions;
99
99
  }) => Promise<Schedule<T>>;
100
100
  cancelSchedule: (id: string) => Promise<boolean>;
101
- readonly alarm: () => Promise<void>;
101
+ keepAlive: () => Promise<() => void>;
102
+ keepAliveWhile: <T>(fn: () => Promise<T>) => Promise<T>;
102
103
  };
103
104
  } & TBase;
104
- /**
105
- * Keep a Durable Object alive via a scheduled heartbeat.
106
- * Returns a disposer function that cancels the heartbeat.
107
- *
108
- * Standalone version usable by any Agent subclass without requiring
109
- * the full fiber mixin. The agent must have a no-op method with the
110
- * given callbackName for the scheduler to invoke.
111
- *
112
- * @param agent - The agent instance (must have scheduleEvery and cancelSchedule)
113
- * @param callbackName - Name of a no-op method on the agent class (must exist)
114
- */
115
- declare function keepAlive(agent: Pick<Agent<Cloudflare.Env>, "scheduleEvery" | "cancelSchedule">, callbackName: string): Promise<() => void>;
116
105
  //#endregion
117
- export { FiberCompleteContext, FiberContext, FiberRecoveryContext, FiberState, keepAlive, withFibers };
106
+ export { FiberCompleteContext, FiberContext, FiberRecoveryContext, FiberState, withFibers };
118
107
  //# sourceMappingURL=forever.d.ts.map
@@ -33,8 +33,6 @@ import { nanoid } from "nanoid";
33
33
  *
34
34
  * @experimental This API is not yet stable and may change.
35
35
  */
36
- console.warn("[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Do not use in production.");
37
- const KEEP_ALIVE_INTERVAL_MS = 1e4;
38
36
  const FIBER_CLEANUP_INTERVAL_MS = 600 * 1e3;
39
37
  const FIBER_CLEANUP_COMPLETED_MS = 1440 * 60 * 1e3;
40
38
  const FIBER_CLEANUP_FAILED_MS = 10080 * 60 * 1e3;
@@ -47,6 +45,7 @@ function withFibers(Base, options) {
47
45
  this._fiberActiveFibers = /* @__PURE__ */ new Set();
48
46
  this._fiberRecoveryInProgress = false;
49
47
  this._fiberLastCleanupTime = 0;
48
+ console.warn("[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Do not use in production.");
50
49
  this.sql`
51
50
  CREATE TABLE IF NOT EXISTS cf_agents_fibers (
52
51
  id TEXT PRIMARY KEY NOT NULL,
@@ -69,21 +68,9 @@ function withFibers(Base, options) {
69
68
  /** @internal */ _fiberDebug(msg, ...args) {
70
69
  if (debugEnabled) console.debug(`[fiber] ${msg}`, ...args);
71
70
  }
72
- /** @internal */ async _cf_fiberHeartbeat() {
71
+ /** @internal */ async _cf_keepAliveHeartbeat() {
73
72
  await this._checkInterruptedFibers();
74
73
  }
75
- async keepAlive() {
76
- const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1e3);
77
- const schedule = await this.scheduleEvery(heartbeatSeconds, "_cf_fiberHeartbeat");
78
- this._fiberDebug("keepAlive started, schedule=%s", schedule.id);
79
- let disposed = false;
80
- return () => {
81
- if (disposed) return;
82
- disposed = true;
83
- this._fiberDebug("keepAlive disposed, schedule=%s", schedule.id);
84
- this.cancelSchedule(schedule.id);
85
- };
86
- }
87
74
  spawnFiber(methodName, payload, options) {
88
75
  this._maybeCleanupFibers();
89
76
  const name = methodName;
@@ -333,7 +320,7 @@ function withFibers(Base, options) {
333
320
  /** @internal */ _cleanupOrphanedHeartbeats() {
334
321
  this.sql`
335
322
  DELETE FROM cf_agents_schedules
336
- WHERE callback = '_cf_fiberHeartbeat'
323
+ WHERE callback = '_cf_keepAliveHeartbeat'
337
324
  `;
338
325
  this._fiberDebug("cleaned up orphaned heartbeat schedules");
339
326
  }
@@ -354,28 +341,7 @@ function withFibers(Base, options) {
354
341
  }
355
342
  return FiberAgent;
356
343
  }
357
- /**
358
- * Keep a Durable Object alive via a scheduled heartbeat.
359
- * Returns a disposer function that cancels the heartbeat.
360
- *
361
- * Standalone version usable by any Agent subclass without requiring
362
- * the full fiber mixin. The agent must have a no-op method with the
363
- * given callbackName for the scheduler to invoke.
364
- *
365
- * @param agent - The agent instance (must have scheduleEvery and cancelSchedule)
366
- * @param callbackName - Name of a no-op method on the agent class (must exist)
367
- */
368
- async function keepAlive(agent, callbackName) {
369
- const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1e3);
370
- const schedule = await agent.scheduleEvery(heartbeatSeconds, callbackName);
371
- let disposed = false;
372
- return () => {
373
- if (disposed) return;
374
- disposed = true;
375
- agent.cancelSchedule(schedule.id);
376
- };
377
- }
378
344
 
379
345
  //#endregion
380
- export { keepAlive, withFibers };
346
+ export { withFibers };
381
347
  //# sourceMappingURL=forever.js.map