agents 0.11.1 → 0.11.3
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.
- package/dist/{client-QBjFV5de.js → client-PEDsNnfY.js} +47 -23
- package/dist/client-PEDsNnfY.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/{index-C_XD19E3.d.ts → index-D49HdAiY.d.ts} +38 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.js +42 -19
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/package.json +1 -1
- package/dist/client-QBjFV5de.js.map +0 -1
|
@@ -728,6 +728,51 @@ function isPrivateIPv4(octets) {
|
|
|
728
728
|
return false;
|
|
729
729
|
}
|
|
730
730
|
/**
|
|
731
|
+
* fe80::/10 — IPv6 link-local (RFC 4291 §2.5.6).
|
|
732
|
+
*
|
|
733
|
+
* The /10 boundary fixes the first 10 bits (1111111010), which means valid
|
|
734
|
+
* first hextets range from fe80 through febf. Only hex digits 8, 9, a, b
|
|
735
|
+
* have high two bits "10" — anything else (e.g. fe7f, fec0) is out of range.
|
|
736
|
+
* The fourth hex digit is unconstrained by the /10 boundary.
|
|
737
|
+
*
|
|
738
|
+
* Historical bug: `startsWith("fe80")` only matched the narrower fe80::/16
|
|
739
|
+
* prefix and let fe81::/feab::/febf:: slip through. See issue #1325.
|
|
740
|
+
*/
|
|
741
|
+
const IPV6_LINK_LOCAL = /^fe[89ab][0-9a-f]/;
|
|
742
|
+
/**
|
|
743
|
+
* Check whether a bracket-stripped, lowercased IPv6 address belongs to a
|
|
744
|
+
* private/reserved range. Also unwraps IPv4-mapped IPv6 (::ffff:...) and
|
|
745
|
+
* delegates to isPrivateIPv4 for those.
|
|
746
|
+
*
|
|
747
|
+
* Loopback (::1) and unspecified (::) are NOT blocked here:
|
|
748
|
+
* - ::1 is intentionally allowed (parallel to 127.x.x.x for local dev)
|
|
749
|
+
* - :: (== [::]) is blocked via BLOCKED_HOSTNAMES at the hostname level
|
|
750
|
+
*/
|
|
751
|
+
function isPrivateIPv6(addr) {
|
|
752
|
+
if (addr.startsWith("fc") || addr.startsWith("fd")) return true;
|
|
753
|
+
if (IPV6_LINK_LOCAL.test(addr)) return true;
|
|
754
|
+
if (addr.startsWith("::ffff:")) {
|
|
755
|
+
const mapped = addr.slice(7);
|
|
756
|
+
const dotParts = mapped.split(".");
|
|
757
|
+
if (dotParts.length === 4 && dotParts.every((p) => /^\d{1,3}$/.test(p))) {
|
|
758
|
+
if (isPrivateIPv4(dotParts.map(Number))) return true;
|
|
759
|
+
} else {
|
|
760
|
+
const hexParts = mapped.split(":");
|
|
761
|
+
if (hexParts.length === 2) {
|
|
762
|
+
const hi = parseInt(hexParts[0], 16);
|
|
763
|
+
const lo = parseInt(hexParts[1], 16);
|
|
764
|
+
if (isPrivateIPv4([
|
|
765
|
+
hi >> 8 & 255,
|
|
766
|
+
hi & 255,
|
|
767
|
+
lo >> 8 & 255,
|
|
768
|
+
lo & 255
|
|
769
|
+
])) return true;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
731
776
|
* Check whether a hostname looks like a private/internal IP address.
|
|
732
777
|
* Blocks RFC 1918, link-local, unique-local, unspecified,
|
|
733
778
|
* and cloud metadata endpoints. Also detects IPv4-mapped IPv6 addresses.
|
|
@@ -746,28 +791,7 @@ function isBlockedUrl(url) {
|
|
|
746
791
|
if (isPrivateIPv4(ipv4Parts.map(Number))) return true;
|
|
747
792
|
}
|
|
748
793
|
if (hostname.startsWith("[") && hostname.endsWith("]")) {
|
|
749
|
-
|
|
750
|
-
if (addr.startsWith("fc") || addr.startsWith("fd")) return true;
|
|
751
|
-
if (addr.startsWith("fe80")) return true;
|
|
752
|
-
if (addr.startsWith("::ffff:")) {
|
|
753
|
-
const mapped = addr.slice(7);
|
|
754
|
-
const dotParts = mapped.split(".");
|
|
755
|
-
if (dotParts.length === 4 && dotParts.every((p) => /^\d{1,3}$/.test(p))) {
|
|
756
|
-
if (isPrivateIPv4(dotParts.map(Number))) return true;
|
|
757
|
-
} else {
|
|
758
|
-
const hexParts = mapped.split(":");
|
|
759
|
-
if (hexParts.length === 2) {
|
|
760
|
-
const hi = parseInt(hexParts[0], 16);
|
|
761
|
-
const lo = parseInt(hexParts[1], 16);
|
|
762
|
-
if (isPrivateIPv4([
|
|
763
|
-
hi >> 8 & 255,
|
|
764
|
-
hi & 255,
|
|
765
|
-
lo >> 8 & 255,
|
|
766
|
-
lo & 255
|
|
767
|
-
])) return true;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
794
|
+
if (isPrivateIPv6(hostname.slice(1, -1).toLowerCase())) return true;
|
|
771
795
|
}
|
|
772
796
|
return false;
|
|
773
797
|
}
|
|
@@ -1592,4 +1616,4 @@ function getNamespacedData(mcpClients, type) {
|
|
|
1592
1616
|
//#endregion
|
|
1593
1617
|
export { RPCServerTransport as a, RPCClientTransport as i, getNamespacedData as n, RPC_DO_PREFIX as o, MCPConnectionState as r, DisposableStore as s, MCPClientManager as t };
|
|
1594
1618
|
|
|
1595
|
-
//# sourceMappingURL=client-
|
|
1619
|
+
//# sourceMappingURL=client-PEDsNnfY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-PEDsNnfY.js","names":[],"sources":["../src/core/events.ts","../src/mcp/errors.ts","../src/mcp/rpc.ts","../src/mcp/client-connection.ts","../src/mcp/client.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 /**\n * @internal Called by McpAgent.handleMcpMessage() — not for external use.\n *\n * Wait for the next send() call and return whatever it produces.\n *\n * Used after resolving an elicitation response: the tool handler is still\n * running and will eventually call send() with either another elicitation\n * request or the final tool result. This method captures that send() using\n * the same _responseResolver / _pendingResponse / timeout mechanism as\n * handle().\n */\n async _awaitPendingResponse(): Promise<\n JSONRPCMessage | JSONRPCMessage[] | undefined\n > {\n if (!this._started) {\n throw new Error(\"Transport not started\");\n }\n\n this._pendingResponse = null;\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 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 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 private _transport?:\n | StreamableHTTPClientTransport\n | SSEClientTransport\n | RPCClientTransport;\n prompts: Prompt[] = [];\n resources: Resource[] = [];\n resourceTemplates: ResourceTemplate[] = [];\n serverCapabilities: ServerCapabilities | undefined;\n\n /** True when resuming a streamable-http session without cached capabilities */\n private _probingCapabilities = false;\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 const discoveredCapabilities = this.client.getServerCapabilities();\n const shouldProbeCapabilities =\n !discoveredCapabilities && this.isResumedStreamableHttpSession();\n\n this.serverCapabilities = discoveredCapabilities;\n this._probingCapabilities = shouldProbeCapabilities;\n\n if (!discoveredCapabilities && !shouldProbeCapabilities) {\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 // For resumed streamable-http sessions, the MCP SDK skips initialize()\n // when a sessionId already exists, so a fresh Client instance may not have\n // cached server capabilities yet. In that case, probe the list endpoints\n // directly and treat -32601 as capability absence.\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 if (discoveredCapabilities?.tools || shouldProbeCapabilities) {\n operations.push(this.registerTools());\n operationNames.push(\"tools\");\n }\n\n if (discoveredCapabilities?.resources || shouldProbeCapabilities) {\n operations.push(this.registerResources());\n operationNames.push(\"resources\");\n }\n\n if (discoveredCapabilities?.prompts || shouldProbeCapabilities) {\n operations.push(this.registerPrompts());\n operationNames.push(\"prompts\");\n }\n\n if (discoveredCapabilities?.resources || shouldProbeCapabilities) {\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 (\n this.serverCapabilities?.tools?.listChanged ||\n this._probingCapabilities\n ) {\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 (\n this.serverCapabilities?.resources?.listChanged ||\n this._probingCapabilities\n ) {\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 (\n this.serverCapabilities?.prompts?.listChanged ||\n this._probingCapabilities\n ) {\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 private isResumedStreamableHttpSession(): boolean {\n return (\n this._transport instanceof StreamableHTTPClientTransport &&\n typeof this._transport.sessionId === \"string\"\n );\n }\n\n get sessionId(): string | undefined {\n if (this._transport instanceof StreamableHTTPClientTransport) {\n return this._transport.sessionId;\n }\n\n return undefined;\n }\n\n private getTransportName(\n transport?:\n | StreamableHTTPClientTransport\n | SSEClientTransport\n | RPCClientTransport\n ): string | undefined {\n if (transport instanceof StreamableHTTPClientTransport) {\n return \"streamable-http\";\n }\n\n if (transport instanceof SSEClientTransport) {\n return \"sse\";\n }\n\n if (transport instanceof RPCClientTransport) {\n return \"rpc\";\n }\n\n return this.lastConnectedTransport;\n }\n\n async close(): Promise<void> {\n const transport = this._transport;\n this._transport = undefined;\n const url = this.url.toString();\n const transportName = this.getTransportName(transport);\n\n if (\n transport instanceof StreamableHTTPClientTransport &&\n transport.sessionId\n ) {\n try {\n await transport.terminateSession();\n } catch (error) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:close\",\n payload: {\n url,\n transport: transportName,\n state: \"error\",\n error: toErrorMessage(error),\n phase: \"terminate-session\"\n },\n timestamp: Date.now()\n });\n }\n }\n\n try {\n await this.client.close();\n } catch (error) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:close\",\n payload: {\n url,\n transport: transportName,\n state: \"error\",\n error: toErrorMessage(error),\n phase: \"client-close\"\n },\n timestamp: Date.now()\n });\n throw error;\n }\n\n this._onObservabilityEvent.fire({\n type: \"mcp:client:close\",\n payload: {\n url,\n transport: transportName,\n state: \"closed\"\n },\n timestamp: Date.now()\n });\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 this._transport = 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","import type { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport type { RequestOptions } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n CallToolRequest,\n CallToolResultSchema,\n CompatibilityCallToolResultSchema,\n GetPromptRequest,\n Prompt,\n ReadResourceRequest,\n Resource,\n ResourceTemplate,\n Tool\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { CfWorkerJsonSchemaValidator } from \"@modelcontextprotocol/sdk/validation/cfworker-provider.js\";\nimport { type RetryOptions, tryN } from \"../retries\";\nimport type { ToolSet } from \"ai\";\nimport { z } from \"zod\";\nimport { nanoid } from \"nanoid\";\nimport { Emitter, type Event, DisposableStore } from \"../core/events\";\nimport type { MCPObservabilityEvent } from \"../observability/mcp\";\nimport {\n MCPClientConnection,\n MCPConnectionState,\n type MCPTransportOptions\n} from \"./client-connection\";\nimport { toErrorMessage } from \"./errors\";\nimport { RPC_DO_PREFIX } from \"./rpc\";\nimport type { TransportType } from \"./types\";\nimport type { MCPServerRow } from \"./client-storage\";\nimport type { AgentMcpOAuthProvider } from \"./do-oauth-client-provider\";\nimport { DurableObjectOAuthClientProvider } from \"./do-oauth-client-provider\";\n\nconst defaultClientOptions: ConstructorParameters<typeof Client>[1] = {\n jsonSchemaValidator: new CfWorkerJsonSchemaValidator()\n};\n\n/**\n * Blocked hostname patterns for SSRF protection.\n * Prevents MCP client from connecting to internal/private network addresses\n * while allowing loopback hosts for local development.\n */\nconst BLOCKED_HOSTNAMES = new Set([\n \"0.0.0.0\",\n \"[::]\",\n \"metadata.google.internal\"\n]);\n\n/**\n * Check whether four IPv4 octets belong to a private/reserved range.\n * Blocks RFC 1918, link-local, cloud metadata, and unspecified addresses.\n */\nfunction isPrivateIPv4(octets: number[]): boolean {\n const [a, b] = octets;\n // 10.0.0.0/8\n if (a === 10) return true;\n // 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n // 169.254.0.0/16 (link-local / cloud metadata)\n if (a === 169 && b === 254) return true;\n // 0.0.0.0/8\n if (a === 0) return true;\n return false;\n}\n\n/**\n * fe80::/10 — IPv6 link-local (RFC 4291 §2.5.6).\n *\n * The /10 boundary fixes the first 10 bits (1111111010), which means valid\n * first hextets range from fe80 through febf. Only hex digits 8, 9, a, b\n * have high two bits \"10\" — anything else (e.g. fe7f, fec0) is out of range.\n * The fourth hex digit is unconstrained by the /10 boundary.\n *\n * Historical bug: `startsWith(\"fe80\")` only matched the narrower fe80::/16\n * prefix and let fe81::/feab::/febf:: slip through. See issue #1325.\n */\nconst IPV6_LINK_LOCAL = /^fe[89ab][0-9a-f]/;\n\n/**\n * Check whether a bracket-stripped, lowercased IPv6 address belongs to a\n * private/reserved range. Also unwraps IPv4-mapped IPv6 (::ffff:...) and\n * delegates to isPrivateIPv4 for those.\n *\n * Loopback (::1) and unspecified (::) are NOT blocked here:\n * - ::1 is intentionally allowed (parallel to 127.x.x.x for local dev)\n * - :: (== [::]) is blocked via BLOCKED_HOSTNAMES at the hostname level\n */\nfunction isPrivateIPv6(addr: string): boolean {\n // fc00::/7 — unique local addresses (fc00:: through fdff::).\n // /7 fixes first 7 bits \"1111110\", so the 8-bit prefix is either\n // fc (11111100) or fd (11111101). No other first hextets qualify.\n if (addr.startsWith(\"fc\") || addr.startsWith(\"fd\")) return true;\n\n // fe80::/10 — link-local addresses (fe80:: through febf:...).\n if (IPV6_LINK_LOCAL.test(addr)) return true;\n\n // IPv4-mapped IPv6 (::ffff:x.x.x.x or ::ffff:XXYY:ZZWW).\n // The WHATWG URL parser does NOT canonicalize hex-form tails to dotted\n // form — [::ffff:a00:1] stays as \"::ffff:a00:1\" and will only be caught\n // by the hex branch. Both forms must therefore be handled here.\n if (addr.startsWith(\"::ffff:\")) {\n const mapped = addr.slice(7);\n const dotParts = mapped.split(\".\");\n if (dotParts.length === 4 && dotParts.every((p) => /^\\d{1,3}$/.test(p))) {\n if (isPrivateIPv4(dotParts.map(Number))) return true;\n } else {\n const hexParts = mapped.split(\":\");\n if (hexParts.length === 2) {\n const hi = parseInt(hexParts[0], 16);\n const lo = parseInt(hexParts[1], 16);\n if (\n isPrivateIPv4([\n (hi >> 8) & 0xff,\n hi & 0xff,\n (lo >> 8) & 0xff,\n lo & 0xff\n ])\n )\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Check whether a hostname looks like a private/internal IP address.\n * Blocks RFC 1918, link-local, unique-local, unspecified,\n * and cloud metadata endpoints. Also detects IPv4-mapped IPv6 addresses.\n */\nfunction isBlockedUrl(url: string): boolean {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return true; // Malformed URLs are blocked\n }\n\n const hostname = parsed.hostname;\n\n if (BLOCKED_HOSTNAMES.has(hostname)) return true;\n\n // IPv4 checks\n const ipv4Parts = hostname.split(\".\");\n if (ipv4Parts.length === 4 && ipv4Parts.every((p) => /^\\d{1,3}$/.test(p))) {\n if (isPrivateIPv4(ipv4Parts.map(Number))) return true;\n }\n\n // IPv6 private range checks.\n // URL parser keeps brackets: hostname for [fc00::1] is \"[fc00::1]\".\n // The parser also lowercases/canonicalizes the address, but we\n // lowercase again defensively in case this helper is ever called with\n // a non-parser-produced hostname.\n if (hostname.startsWith(\"[\") && hostname.endsWith(\"]\")) {\n if (isPrivateIPv6(hostname.slice(1, -1).toLowerCase())) return true;\n }\n\n return false;\n}\n\n/**\n * Options that can be stored in the server_options column\n * This is what gets JSON.stringify'd and stored in the database\n */\nexport type MCPServerOptions = {\n client?: ConstructorParameters<typeof Client>[1];\n transport?: {\n headers?: HeadersInit;\n type?: TransportType;\n sessionId?: string;\n };\n /** Retry options for connection and reconnection attempts */\n retry?: RetryOptions;\n};\n\n/**\n * Result of an OAuth callback request\n */\nexport type MCPOAuthCallbackResult =\n | { serverId: string; authSuccess: true; authError?: undefined }\n | { serverId?: string; authSuccess: false; authError: string };\n\n/**\n * Options for registering an MCP server\n */\nexport type RegisterServerOptions = {\n url: string;\n name: string;\n callbackUrl?: string;\n client?: ConstructorParameters<typeof Client>[1];\n transport?: MCPTransportOptions;\n authUrl?: string;\n clientId?: string;\n /** Retry options for connection and reconnection attempts */\n retry?: RetryOptions;\n};\n\n/**\n * Result of attempting to connect to an MCP server.\n * Discriminated union ensures error is present only on failure.\n */\nexport type MCPConnectionResult =\n | {\n state: typeof MCPConnectionState.FAILED;\n error: string;\n }\n | {\n state: typeof MCPConnectionState.AUTHENTICATING;\n authUrl: string;\n clientId?: string;\n }\n | {\n state: typeof MCPConnectionState.CONNECTED;\n };\n\n/**\n * Result of discovering server capabilities.\n * success indicates whether discovery completed successfully.\n * state is the current connection state at time of return.\n * error is present when success is false.\n */\nexport type MCPDiscoverResult = {\n success: boolean;\n state: MCPConnectionState;\n error?: string;\n};\n\nexport type MCPClientOAuthCallbackConfig = {\n successRedirect?: string;\n errorRedirect?: string;\n customHandler?: (result: MCPClientOAuthResult) => Response;\n};\n\nexport type MCPClientOAuthResult =\n | { serverId: string; authSuccess: true; authError?: undefined }\n | {\n serverId?: string;\n authSuccess: false;\n /** May contain untrusted content from external OAuth providers. Escape appropriately for your output context. */\n authError: string;\n };\n\nexport type MCPClientManagerOptions = {\n storage: DurableObjectStorage;\n createAuthProvider?: (callbackUrl: string) => AgentMcpOAuthProvider;\n};\n\n/**\n * Filter options for scoping tools, prompts, resources, and resource templates\n * to a subset of connected MCP servers. All specified criteria are AND'd together.\n */\nexport type MCPServerFilter = {\n /** Include only connections matching this server ID (or IDs). */\n serverId?: string | string[];\n /** Include only connections whose stored name matches (or is in) this value. */\n serverName?: string | string[];\n /** Include only connections currently in this state (or states). */\n state?: MCPConnectionState | MCPConnectionState[];\n};\n\n/**\n * Utility class that aggregates multiple MCP clients into one\n */\nexport class MCPClientManager {\n public mcpConnections: Record<string, MCPClientConnection> = {};\n private _didWarnAboutUnstableGetAITools = false;\n private _oauthCallbackConfig?: MCPClientOAuthCallbackConfig;\n private _connectionDisposables = new Map<string, DisposableStore>();\n private _storage: DurableObjectStorage;\n private _createAuthProviderFn?: (\n callbackUrl: string\n ) => AgentMcpOAuthProvider;\n private _isRestored = false;\n private _pendingConnections = new Map<string, Promise<void>>();\n\n /** @internal Protected for testing purposes. */\n protected readonly _onObservabilityEvent =\n new Emitter<MCPObservabilityEvent>();\n public readonly onObservabilityEvent: Event<MCPObservabilityEvent> =\n this._onObservabilityEvent.event;\n\n private readonly _onServerStateChanged = new Emitter<void>();\n /**\n * Event that fires whenever any MCP server state changes (registered, connected, removed, etc.)\n * This is useful for broadcasting server state to clients.\n */\n public readonly onServerStateChanged: Event<void> =\n this._onServerStateChanged.event;\n\n /**\n * @param _name Name of the MCP client\n * @param _version Version of the MCP Client\n * @param options Storage adapter for persisting MCP server state\n */\n constructor(\n private _name: string,\n private _version: string,\n options: MCPClientManagerOptions\n ) {\n if (!options.storage) {\n throw new Error(\n \"MCPClientManager requires a valid DurableObjectStorage instance\"\n );\n }\n this._storage = options.storage;\n this._createAuthProviderFn = options.createAuthProvider;\n }\n\n // SQL helper - runs a query and returns results as array\n private sql<T extends Record<string, SqlStorageValue>>(\n query: string,\n ...bindings: SqlStorageValue[]\n ): T[] {\n return [...this._storage.sql.exec<T>(query, ...bindings)];\n }\n\n // Storage operations\n private saveServerToStorage(server: MCPServerRow): void {\n this.sql(\n `INSERT OR REPLACE INTO cf_agents_mcp_servers (\n id, name, server_url, client_id, auth_url, callback_url, server_options\n ) VALUES (?, ?, ?, ?, ?, ?, ?)`,\n server.id,\n server.name,\n server.server_url,\n server.client_id ?? null,\n server.auth_url ?? null,\n server.callback_url,\n server.server_options ?? null\n );\n }\n\n private removeServerFromStorage(serverId: string): void {\n this.sql(\"DELETE FROM cf_agents_mcp_servers WHERE id = ?\", serverId);\n }\n\n private getServersFromStorage(): MCPServerRow[] {\n return this.sql<MCPServerRow>(\n \"SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers\"\n );\n }\n\n private filterConnections(\n filter?: MCPServerFilter\n ): Record<string, MCPClientConnection> {\n if (!filter) return this.mcpConnections;\n\n const serverIds = filter.serverId\n ? Array.isArray(filter.serverId)\n ? filter.serverId\n : [filter.serverId]\n : undefined;\n\n const serverNames = filter.serverName\n ? Array.isArray(filter.serverName)\n ? filter.serverName\n : [filter.serverName]\n : undefined;\n\n const states = filter.state\n ? Array.isArray(filter.state)\n ? filter.state\n : [filter.state]\n : undefined;\n\n let nameMatchedIds: Set<string> | undefined;\n if (serverNames) {\n const servers = this.getServersFromStorage();\n nameMatchedIds = new Set(\n servers.filter((s) => serverNames.includes(s.name)).map((s) => s.id)\n );\n }\n\n return Object.fromEntries(\n Object.entries(this.mcpConnections).filter(([id, conn]) => {\n if (serverIds && !serverIds.includes(id)) return false;\n if (nameMatchedIds && !nameMatchedIds.has(id)) return false;\n if (states && !states.includes(conn.connectionState)) return false;\n return true;\n })\n );\n }\n\n /**\n * Get the retry options for a server from stored server_options\n */\n private getServerRetryOptions(serverId: string): RetryOptions | undefined {\n const rows = this.sql<MCPServerRow>(\n \"SELECT server_options FROM cf_agents_mcp_servers WHERE id = ?\",\n serverId\n );\n if (!rows.length || !rows[0].server_options) return undefined;\n const parsed: MCPServerOptions = JSON.parse(rows[0].server_options);\n return parsed.retry;\n }\n\n private clearServerAuthUrl(serverId: string): void {\n this.sql(\n \"UPDATE cf_agents_mcp_servers SET auth_url = NULL WHERE id = ?\",\n serverId\n );\n }\n\n private updateStoredSessionId(id: string, sessionId?: string): void {\n const servers = this.getServersFromStorage();\n const serverRow = servers.find((server) => server.id === id);\n if (!serverRow) {\n return;\n }\n\n const parsedOptions: MCPServerOptions = serverRow.server_options\n ? JSON.parse(serverRow.server_options)\n : {};\n\n const currentSessionId = parsedOptions.transport?.sessionId;\n if (currentSessionId === sessionId) {\n return;\n }\n\n const nextTransport = {\n ...(parsedOptions.transport ?? {}),\n ...(sessionId ? { sessionId } : {})\n };\n\n if (!sessionId) {\n delete nextTransport.sessionId;\n }\n\n this.saveServerToStorage({\n ...serverRow,\n server_options: JSON.stringify({\n ...parsedOptions,\n transport: nextTransport\n })\n });\n }\n\n private failConnection(\n serverId: string,\n error: string\n ): MCPOAuthCallbackResult {\n this.clearServerAuthUrl(serverId);\n if (this.mcpConnections[serverId]) {\n this.mcpConnections[serverId].connectionState = MCPConnectionState.FAILED;\n this.mcpConnections[serverId].connectionError = error;\n }\n this._onServerStateChanged.fire();\n return { serverId, authSuccess: false, authError: error };\n }\n\n /**\n * Create an auth provider for a server\n * @internal\n */\n private createAuthProvider(\n serverId: string,\n callbackUrl: string,\n clientName: string,\n clientId?: string\n ): AgentMcpOAuthProvider {\n if (!this._storage) {\n throw new Error(\n \"Cannot create auth provider: storage is not initialized\"\n );\n }\n const authProvider = new DurableObjectOAuthClientProvider(\n this._storage,\n clientName,\n callbackUrl\n );\n authProvider.serverId = serverId;\n if (clientId) {\n authProvider.clientId = clientId;\n }\n return authProvider;\n }\n\n /**\n * Get saved RPC servers from storage (servers with rpc:// URLs).\n * These are restored separately by the Agent class since they need env bindings.\n */\n getRpcServersFromStorage(): MCPServerRow[] {\n return this.getServersFromStorage().filter((s) =>\n s.server_url.startsWith(RPC_DO_PREFIX)\n );\n }\n\n /**\n * Save an RPC server to storage for hibernation recovery.\n * The bindingName is stored in server_options so the Agent can look up\n * the namespace from env during restore.\n */\n saveRpcServerToStorage(\n id: string,\n name: string,\n normalizedName: string,\n bindingName: string,\n props?: Record<string, unknown>\n ): void {\n this.saveServerToStorage({\n id,\n name,\n server_url: `${RPC_DO_PREFIX}${normalizedName}`,\n client_id: null,\n auth_url: null,\n callback_url: \"\",\n server_options: JSON.stringify({ bindingName, props })\n });\n }\n\n /**\n * Restore MCP server connections from storage\n * This method is called on Agent initialization to restore previously connected servers.\n * RPC servers (rpc:// URLs) are skipped here -- they are restored by the Agent class\n * which has access to env bindings.\n *\n * @param clientName Name to use for OAuth client (typically the agent instance name)\n */\n async restoreConnectionsFromStorage(clientName: string): Promise<void> {\n if (this._isRestored) {\n return;\n }\n\n const servers = this.getServersFromStorage();\n\n if (!servers || servers.length === 0) {\n this._isRestored = true;\n return;\n }\n\n for (const server of servers) {\n if (server.server_url.startsWith(RPC_DO_PREFIX)) {\n continue;\n }\n\n const existingConn = this.mcpConnections[server.id];\n\n // Skip if connection already exists and is in a good state\n if (existingConn) {\n if (existingConn.connectionState === MCPConnectionState.READY) {\n console.warn(\n `[MCPClientManager] Server ${server.id} already has a ready connection. Skipping recreation.`\n );\n continue;\n }\n\n // Don't interrupt in-flight OAuth or connections\n if (\n existingConn.connectionState === MCPConnectionState.AUTHENTICATING ||\n existingConn.connectionState === MCPConnectionState.CONNECTING ||\n existingConn.connectionState === MCPConnectionState.DISCOVERING\n ) {\n // Let the existing flow complete\n continue;\n }\n\n // If failed, clean up the old connection before recreating\n if (existingConn.connectionState === MCPConnectionState.FAILED) {\n try {\n await existingConn.close();\n } catch (error) {\n console.warn(\n `[MCPClientManager] Error closing failed connection ${server.id}:`,\n error\n );\n } finally {\n this.cleanupClosedConnection(server.id);\n }\n }\n }\n\n const parsedOptions: MCPServerOptions | null = server.server_options\n ? JSON.parse(server.server_options)\n : null;\n\n let authProvider: AgentMcpOAuthProvider | undefined;\n if (server.callback_url) {\n authProvider = this._createAuthProviderFn\n ? this._createAuthProviderFn(server.callback_url)\n : this.createAuthProvider(\n server.id,\n server.callback_url,\n clientName,\n server.client_id ?? undefined\n );\n authProvider.serverId = server.id;\n if (server.client_id) {\n authProvider.clientId = server.client_id;\n }\n }\n\n // Create the in-memory connection object (no need to save to storage - we just read from it!)\n const conn = this.createConnection(server.id, server.server_url, {\n client: parsedOptions?.client ?? {},\n transport: {\n ...(parsedOptions?.transport ?? {}),\n type: parsedOptions?.transport?.type ?? (\"auto\" as TransportType),\n authProvider\n }\n });\n\n // If auth_url exists, OAuth flow is in progress - set state and wait for callback\n if (server.auth_url) {\n conn.connectionState = MCPConnectionState.AUTHENTICATING;\n continue;\n }\n\n // Start connection in background (don't await) to avoid blocking the DO\n this._trackConnection(\n server.id,\n this._restoreServer(server.id, parsedOptions?.retry)\n );\n }\n\n this._isRestored = true;\n }\n\n /**\n * Track a pending connection promise for a server.\n * The promise is removed from the map when it settles.\n */\n private _trackConnection(serverId: string, promise: Promise<void>): void {\n const tracked = promise.finally(() => {\n // Only delete if it's still the same promise (not replaced by a newer one)\n if (this._pendingConnections.get(serverId) === tracked) {\n this._pendingConnections.delete(serverId);\n }\n });\n this._pendingConnections.set(serverId, tracked);\n }\n\n /**\n * Wait for all in-flight connection and discovery operations to settle.\n * This is useful when you need MCP tools to be available before proceeding,\n * e.g. before calling getAITools() after the agent wakes from hibernation.\n *\n * Returns once every pending connection has either connected and discovered,\n * failed, or timed out. Never rejects.\n *\n * @param options.timeout - Maximum time in milliseconds to wait.\n * `0` returns immediately without waiting.\n * `undefined` (default) waits indefinitely.\n */\n async waitForConnections(options?: { timeout?: number }): Promise<void> {\n if (this._pendingConnections.size === 0) {\n return;\n }\n if (options?.timeout != null && options.timeout <= 0) {\n return;\n }\n const settled = Promise.allSettled(this._pendingConnections.values());\n if (options?.timeout != null && options.timeout > 0) {\n let timerId: ReturnType<typeof setTimeout>;\n const timer = new Promise<void>((resolve) => {\n timerId = setTimeout(resolve, options.timeout);\n });\n await Promise.race([settled, timer]);\n clearTimeout(timerId!);\n } else {\n await settled;\n }\n }\n\n /**\n * Internal method to restore a single server connection and discovery\n */\n private async _restoreServer(\n serverId: string,\n retry?: RetryOptions\n ): Promise<void> {\n // Always try to connect - the connection logic will determine if OAuth is needed\n // If stored OAuth tokens are valid, connection will succeed automatically\n // If tokens are missing/invalid, connection will fail with Unauthorized\n // and state will be set to \"authenticating\"\n const maxAttempts = retry?.maxAttempts ?? 3;\n const baseDelayMs = retry?.baseDelayMs ?? 500;\n const maxDelayMs = retry?.maxDelayMs ?? 5000;\n\n const connectResult = await tryN(\n maxAttempts,\n async () => this.connectToServer(serverId),\n { baseDelayMs, maxDelayMs }\n ).catch((error) => {\n console.error(\n `Error connecting to ${serverId} after ${maxAttempts} attempts:`,\n error\n );\n return null;\n });\n\n if (connectResult?.state === MCPConnectionState.CONNECTED) {\n const discoverResult = await this.discoverIfConnected(serverId);\n if (discoverResult && !discoverResult.success) {\n console.error(`Error discovering ${serverId}:`, discoverResult.error);\n }\n }\n }\n\n /**\n * Connect to and register an MCP server\n *\n * @deprecated This method is maintained for backward compatibility.\n * For new code, use registerServer() and connectToServer() separately.\n *\n * @param url Server URL\n * @param options Connection options\n * @returns Object with server ID, auth URL (if OAuth), and client ID (if OAuth)\n */\n async connect(\n url: string,\n options: {\n // Allows you to reconnect to a server (in the case of an auth reconnect)\n reconnect?: {\n // server id\n id: string;\n oauthClientId?: string;\n oauthCode?: string;\n };\n // we're overriding authProvider here because we want to be able to access the auth URL\n transport?: MCPTransportOptions;\n client?: ConstructorParameters<typeof Client>[1];\n } = {}\n ): Promise<{\n id: string;\n authUrl?: string;\n clientId?: string;\n }> {\n const id = options.reconnect?.id ?? nanoid(8);\n\n if (options.transport?.authProvider) {\n options.transport.authProvider.serverId = id;\n // reconnect with auth\n if (options.reconnect?.oauthClientId) {\n options.transport.authProvider.clientId =\n options.reconnect?.oauthClientId;\n }\n }\n\n if (isBlockedUrl(url)) {\n throw new Error(\n `Blocked URL: ${url} — MCP client connections to private/internal addresses are not allowed`\n );\n }\n\n // During OAuth reconnect, reuse existing connection to preserve state\n if (!options.reconnect?.oauthCode || !this.mcpConnections[id]) {\n const normalizedTransport = {\n ...options.transport,\n type: options.transport?.type ?? (\"auto\" as TransportType)\n };\n\n this.mcpConnections[id] = new MCPClientConnection(\n new URL(url),\n {\n name: this._name,\n version: this._version\n },\n {\n client: options.client ?? {},\n transport: normalizedTransport\n }\n );\n\n // Pipe connection-level observability events to the manager-level emitter\n // and track the subscription for cleanup.\n const store = new DisposableStore();\n // If we somehow already had disposables for this id, clear them first\n const existing = this._connectionDisposables.get(id);\n if (existing) existing.dispose();\n this._connectionDisposables.set(id, store);\n store.add(\n this.mcpConnections[id].onObservabilityEvent((event) => {\n this._onObservabilityEvent.fire(event);\n })\n );\n }\n\n // Initialize connection first. this will try connect\n await this.mcpConnections[id].init();\n\n // Handle OAuth completion if we have a reconnect code\n if (options.reconnect?.oauthCode) {\n try {\n await this.mcpConnections[id].completeAuthorization(\n options.reconnect.oauthCode\n );\n\n // Reinitialize connection\n await this.mcpConnections[id].init();\n } catch (error) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:connect\",\n payload: {\n url: url,\n transport: options.transport?.type ?? \"auto\",\n state: this.mcpConnections[id].connectionState,\n error: toErrorMessage(error)\n },\n timestamp: Date.now()\n });\n // Re-throw to signal failure to the caller\n throw error;\n }\n }\n\n // If connection is in authenticating state, return auth URL for OAuth flow\n const authUrl = options.transport?.authProvider?.authUrl;\n if (\n this.mcpConnections[id].connectionState ===\n MCPConnectionState.AUTHENTICATING &&\n authUrl &&\n options.transport?.authProvider?.redirectUrl\n ) {\n return {\n authUrl,\n clientId: options.transport?.authProvider?.clientId,\n id\n };\n }\n\n // If connection is connected, discover capabilities\n const discoverResult = await this.discoverIfConnected(id);\n if (discoverResult && !discoverResult.success) {\n throw new Error(\n `Failed to discover server capabilities: ${discoverResult.error}`\n );\n }\n\n return {\n id\n };\n }\n\n /**\n * Create an in-memory connection object and set up observability\n * Does NOT save to storage - use registerServer() for that\n * @returns The connection object (existing or newly created)\n */\n private createConnection(\n id: string,\n url: string,\n options: {\n client?: ConstructorParameters<typeof Client>[1];\n transport: MCPTransportOptions;\n }\n ): MCPClientConnection {\n // Return existing connection if already exists\n if (this.mcpConnections[id]) {\n return this.mcpConnections[id];\n }\n\n const normalizedTransport = {\n ...options.transport,\n type: options.transport?.type ?? (\"auto\" as TransportType)\n };\n\n this.mcpConnections[id] = new MCPClientConnection(\n new URL(url),\n {\n name: this._name,\n version: this._version\n },\n {\n client: { ...defaultClientOptions, ...options.client },\n transport: normalizedTransport\n }\n );\n\n // Pipe connection-level observability events to the manager-level emitter\n const store = new DisposableStore();\n const existing = this._connectionDisposables.get(id);\n if (existing) existing.dispose();\n this._connectionDisposables.set(id, store);\n store.add(\n this.mcpConnections[id].onObservabilityEvent((event) => {\n this._onObservabilityEvent.fire(event);\n })\n );\n\n return this.mcpConnections[id];\n }\n\n /**\n * Register an MCP server connection without connecting\n * Creates the connection object, sets up observability, and saves to storage\n *\n * @param id Server ID\n * @param options Registration options including URL, name, callback URL, and connection config\n * @returns Server ID\n */\n async registerServer(\n id: string,\n options: RegisterServerOptions\n ): Promise<string> {\n if (isBlockedUrl(options.url)) {\n throw new Error(\n `Blocked URL: ${options.url} — MCP client connections to private/internal addresses are not allowed`\n );\n }\n\n // Create the in-memory connection\n this.createConnection(id, options.url, {\n client: options.client,\n transport: {\n ...options.transport,\n type: options.transport?.type ?? (\"auto\" as TransportType)\n }\n });\n\n // Save to storage (exclude authProvider since it's recreated during restore)\n const { authProvider: _, ...transportWithoutAuth } =\n options.transport ?? {};\n this.saveServerToStorage({\n id,\n name: options.name,\n server_url: options.url,\n callback_url: options.callbackUrl ?? \"\",\n client_id: options.clientId ?? null,\n auth_url: options.authUrl ?? null,\n server_options: JSON.stringify({\n client: options.client,\n transport: transportWithoutAuth,\n retry: options.retry\n })\n });\n\n this._onServerStateChanged.fire();\n\n return id;\n }\n\n /**\n * Connect to an already registered MCP server and initialize the connection.\n *\n * For OAuth servers, returns `{ state: \"authenticating\", authUrl, clientId? }`.\n * The user must complete the OAuth flow via the authUrl, which triggers a\n * callback handled by `handleCallbackRequest()`.\n *\n * For non-OAuth servers, establishes the transport connection and returns\n * `{ state: \"connected\" }`. Call `discoverIfConnected()` afterwards to\n * discover capabilities and transition to \"ready\" state.\n *\n * @param id Server ID (must be registered first via registerServer())\n * @returns Connection result with current state and OAuth info (if applicable)\n */\n async connectToServer(id: string): Promise<MCPConnectionResult> {\n const conn = this.mcpConnections[id];\n if (!conn) {\n throw new Error(\n `Server ${id} is not registered. Call registerServer() first.`\n );\n }\n\n const error = await conn.init();\n this.updateStoredSessionId(id, conn.sessionId);\n this._onServerStateChanged.fire();\n\n switch (conn.connectionState) {\n case MCPConnectionState.FAILED:\n return {\n state: conn.connectionState,\n error: error ?? \"Unknown connection error\"\n };\n\n case MCPConnectionState.AUTHENTICATING: {\n const authUrl = conn.options.transport.authProvider?.authUrl;\n const redirectUrl = conn.options.transport.authProvider?.redirectUrl;\n\n if (!authUrl || !redirectUrl) {\n return {\n state: MCPConnectionState.FAILED,\n error: `OAuth configuration incomplete: missing ${!authUrl ? \"authUrl\" : \"redirectUrl\"}`\n };\n }\n\n const clientId = conn.options.transport.authProvider?.clientId;\n\n // Update storage with auth URL and client ID\n const servers = this.getServersFromStorage();\n const serverRow = servers.find((s) => s.id === id);\n if (serverRow) {\n this.saveServerToStorage({\n ...serverRow,\n auth_url: authUrl,\n client_id: clientId ?? null\n });\n // Broadcast again so clients receive the auth_url\n this._onServerStateChanged.fire();\n }\n\n this._onObservabilityEvent.fire({\n type: \"mcp:client:authorize\",\n payload: { serverId: id, authUrl, clientId },\n timestamp: Date.now()\n });\n\n return {\n state: conn.connectionState,\n authUrl,\n clientId\n };\n }\n\n case MCPConnectionState.CONNECTED:\n return { state: conn.connectionState };\n\n default:\n return {\n state: MCPConnectionState.FAILED,\n error: `Unexpected connection state after init: ${conn.connectionState}`\n };\n }\n }\n\n private extractServerIdFromState(state: string | null): string | null {\n if (!state) return null;\n const parts = state.split(\".\");\n return parts.length === 2 ? parts[1] : null;\n }\n\n isCallbackRequest(req: Request): boolean {\n if (req.method !== \"GET\") {\n return false;\n }\n\n const url = new URL(req.url);\n const state = url.searchParams.get(\"state\");\n const serverId = this.extractServerIdFromState(state);\n if (!serverId) {\n return false;\n }\n\n // Match by server ID AND verify the request origin + pathname matches the registered callback URL.\n // This prevents unrelated GET requests with a `state` param from being intercepted.\n const servers = this.getServersFromStorage();\n return servers.some((server) => {\n if (server.id !== serverId) return false;\n try {\n const storedUrl = new URL(server.callback_url);\n return (\n storedUrl.origin === url.origin && storedUrl.pathname === url.pathname\n );\n } catch {\n return false;\n }\n });\n }\n\n private validateCallbackRequest(\n req: Request\n ):\n | { valid: true; serverId: string; code: string; state: string }\n | { valid: false; serverId?: string; error: string } {\n const url = new URL(req.url);\n const code = url.searchParams.get(\"code\");\n const state = url.searchParams.get(\"state\");\n const error = url.searchParams.get(\"error\");\n const errorDescription = url.searchParams.get(\"error_description\");\n\n // Early validation - return errors because we can't identify the connection\n if (!state) {\n return {\n valid: false,\n error: \"Unauthorized: no state provided\"\n };\n }\n\n const serverId = this.extractServerIdFromState(state);\n if (!serverId) {\n return {\n valid: false,\n error:\n \"No serverId found in state parameter. Expected format: {nonce}.{serverId}\"\n };\n }\n\n if (error) {\n return {\n serverId: serverId,\n valid: false,\n error: errorDescription || error\n };\n }\n\n if (!code) {\n return {\n serverId: serverId,\n valid: false,\n error: \"Unauthorized: no code provided\"\n };\n }\n\n const servers = this.getServersFromStorage();\n const serverExists = servers.some((server) => server.id === serverId);\n if (!serverExists) {\n return {\n serverId: serverId,\n valid: false,\n error: `No server found with id \"${serverId}\". Was the request matched with \\`isCallbackRequest()\\`?`\n };\n }\n\n if (this.mcpConnections[serverId] === undefined) {\n return {\n serverId: serverId,\n valid: false,\n error: `No connection found for serverId \"${serverId}\".`\n };\n }\n\n return {\n valid: true,\n serverId,\n code: code,\n state: state\n };\n }\n\n async handleCallbackRequest(req: Request): Promise<MCPOAuthCallbackResult> {\n const validation = this.validateCallbackRequest(req);\n\n if (!validation.valid) {\n if (validation.serverId && this.mcpConnections[validation.serverId]) {\n return this.failConnection(validation.serverId, validation.error);\n }\n\n return {\n serverId: validation.serverId,\n authSuccess: false,\n authError: validation.error\n };\n }\n\n const { serverId, code, state } = validation;\n const conn = this.mcpConnections[serverId]; // We have a valid connection - all errors from here should fail the connection\n\n try {\n if (!conn.options.transport.authProvider) {\n throw new Error(\n \"Trying to finalize authentication for a server connection without an authProvider\"\n );\n }\n\n const authProvider = conn.options.transport.authProvider;\n authProvider.serverId = serverId;\n\n // Two-phase state validation: check first (non-destructive), consume later\n // This prevents DoS attacks where attacker consumes valid state before legitimate callback\n const stateValidation = await authProvider.checkState(state);\n if (!stateValidation.valid) {\n throw new Error(stateValidation.error || \"Invalid state\");\n }\n\n // Already authenticated - just return success\n if (\n conn.connectionState === MCPConnectionState.READY ||\n conn.connectionState === MCPConnectionState.CONNECTED\n ) {\n this.clearServerAuthUrl(serverId);\n return { serverId, authSuccess: true };\n }\n\n if (conn.connectionState !== MCPConnectionState.AUTHENTICATING) {\n throw new Error(\n `Failed to authenticate: the client is in \"${conn.connectionState}\" state, expected \"authenticating\"`\n );\n }\n\n await authProvider.consumeState(state);\n await conn.completeAuthorization(code);\n this.updateStoredSessionId(serverId, conn.sessionId);\n await authProvider.deleteCodeVerifier();\n this.clearServerAuthUrl(serverId);\n conn.connectionError = null;\n this._onServerStateChanged.fire();\n\n return { serverId, authSuccess: true };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return this.failConnection(serverId, message);\n }\n }\n\n /**\n * Discover server capabilities if connection is in CONNECTED or READY state.\n * Transitions to DISCOVERING then READY (or CONNECTED on error).\n * Can be called to refresh server capabilities (e.g., from a UI refresh button).\n *\n * If called while a previous discovery is in-flight for the same server,\n * the previous discovery will be aborted.\n *\n * @param serverId The server ID to discover\n * @param options Optional configuration\n * @param options.timeoutMs Timeout in milliseconds (default: 30000)\n * @returns Result with current state and optional error, or undefined if connection not found\n */\n async discoverIfConnected(\n serverId: string,\n options: { timeoutMs?: number } = {}\n ): Promise<MCPDiscoverResult | undefined> {\n const conn = this.mcpConnections[serverId];\n if (!conn) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:discover\",\n payload: {},\n timestamp: Date.now()\n });\n return undefined;\n }\n\n // Delegate to connection's discover method which handles cancellation and timeout\n const result = await conn.discover(options);\n this._onServerStateChanged.fire();\n\n return {\n ...result,\n state: conn.connectionState\n };\n }\n\n /**\n * Establish connection in the background after OAuth completion.\n * This method connects to the server and discovers its capabilities.\n * The connection is automatically tracked so that `waitForConnections()`\n * will include it.\n * @param serverId The server ID to establish connection for\n */\n async establishConnection(serverId: string): Promise<void> {\n const promise = this._doEstablishConnection(serverId);\n this._trackConnection(serverId, promise);\n return promise;\n }\n\n private async _doEstablishConnection(serverId: string): Promise<void> {\n const conn = this.mcpConnections[serverId];\n if (!conn) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:preconnect\",\n payload: { serverId },\n timestamp: Date.now()\n });\n return;\n }\n\n // Skip if already discovering or ready - prevents duplicate work\n if (\n conn.connectionState === MCPConnectionState.DISCOVERING ||\n conn.connectionState === MCPConnectionState.READY\n ) {\n this._onObservabilityEvent.fire({\n type: \"mcp:client:connect\",\n payload: {\n url: conn.url.toString(),\n transport: conn.options.transport.type || \"unknown\",\n state: conn.connectionState\n },\n timestamp: Date.now()\n });\n return;\n }\n\n const retry = this.getServerRetryOptions(serverId);\n const maxAttempts = retry?.maxAttempts ?? 3;\n const baseDelayMs = retry?.baseDelayMs ?? 500;\n const maxDelayMs = retry?.maxDelayMs ?? 5000;\n\n const connectResult = await tryN(\n maxAttempts,\n async () => this.connectToServer(serverId),\n { baseDelayMs, maxDelayMs }\n );\n this._onServerStateChanged.fire();\n\n if (connectResult.state === MCPConnectionState.CONNECTED) {\n await this.discoverIfConnected(serverId);\n }\n\n this._onObservabilityEvent.fire({\n type: \"mcp:client:connect\",\n payload: {\n url: conn.url.toString(),\n transport: conn.options.transport.type || \"unknown\",\n state: conn.connectionState\n },\n timestamp: Date.now()\n });\n }\n\n /**\n * Configure OAuth callback handling\n * @param config OAuth callback configuration\n */\n configureOAuthCallback(config: MCPClientOAuthCallbackConfig): void {\n this._oauthCallbackConfig = config;\n }\n\n /**\n * Get the current OAuth callback configuration\n * @returns The current OAuth callback configuration\n */\n getOAuthCallbackConfig(): MCPClientOAuthCallbackConfig | undefined {\n return this._oauthCallbackConfig;\n }\n\n /**\n * @param filter - Optional filter to scope results to specific servers\n * @returns namespaced list of tools\n */\n listTools(filter?: MCPServerFilter): NamespacedData[\"tools\"] {\n return getNamespacedData(this.filterConnections(filter), \"tools\");\n }\n\n /**\n * @param filter - Optional filter to scope results to specific servers\n * @returns a set of tools that you can use with the AI SDK\n */\n getAITools(filter?: MCPServerFilter): ToolSet {\n const connections = this.filterConnections(filter);\n\n for (const [id, conn] of Object.entries(connections)) {\n if (\n conn.connectionState !== MCPConnectionState.READY &&\n conn.connectionState !== MCPConnectionState.AUTHENTICATING\n ) {\n console.warn(\n `[getAITools] WARNING: Reading tools from connection ${id} in state \"${conn.connectionState}\". Tools may not be loaded yet.`\n );\n }\n }\n\n const entries: [string, ToolSet[string]][] = [];\n for (const tool of getNamespacedData(connections, \"tools\")) {\n try {\n const toolKey = `tool_${tool.serverId.replace(/-/g, \"\")}_${tool.name}`;\n entries.push([\n toolKey,\n {\n description: tool.description,\n execute: async (args) => {\n const result = await this.callTool({\n arguments: args,\n name: tool.name,\n serverId: tool.serverId\n });\n if (result.isError) {\n const content = result.content as\n | Array<{ type: string; text?: string }>\n | undefined;\n const textContent = content?.[0];\n const message =\n textContent?.type === \"text\" && textContent.text\n ? textContent.text\n : \"Tool call failed\";\n throw new Error(message);\n }\n return result;\n },\n inputSchema: tool.inputSchema\n ? z.fromJSONSchema(\n tool.inputSchema as Parameters<typeof z.fromJSONSchema>[0]\n )\n : z.fromJSONSchema({ type: \"object\" }),\n outputSchema: tool.outputSchema\n ? z.fromJSONSchema(\n tool.outputSchema as Parameters<typeof z.fromJSONSchema>[0]\n )\n : undefined\n }\n ]);\n } catch (e) {\n console.warn(\n `[getAITools] Skipping tool \"${tool.name}\" from \"${tool.serverId}\": ${e}`\n );\n }\n }\n return Object.fromEntries(entries);\n }\n\n /**\n * @deprecated this has been renamed to getAITools(), and unstable_getAITools will be removed in the next major version\n * @param filter - Optional filter to scope results to specific servers\n * @returns a set of tools that you can use with the AI SDK\n */\n unstable_getAITools(filter?: MCPServerFilter): ToolSet {\n if (!this._didWarnAboutUnstableGetAITools) {\n this._didWarnAboutUnstableGetAITools = true;\n console.warn(\n \"unstable_getAITools is deprecated, use getAITools instead. unstable_getAITools will be removed in the next major version.\"\n );\n }\n return this.getAITools(filter);\n }\n\n /**\n * Closes all active in-memory connections to MCP servers.\n *\n * Note: This only closes the transport connections - it does NOT remove\n * servers from storage. Servers will still be listed and their callback\n * URLs will still match incoming OAuth requests.\n *\n * Use removeServer() instead if you want to fully clean up a server\n * (closes connection AND removes from storage).\n */\n private cleanupClosedConnection(id: string): void {\n this.updateStoredSessionId(id, undefined);\n\n const store = this._connectionDisposables.get(id);\n if (store) store.dispose();\n this._connectionDisposables.delete(id);\n\n delete this.mcpConnections[id];\n }\n\n async closeAllConnections() {\n const ids = Object.keys(this.mcpConnections);\n\n // Clear all pending connection tracking\n this._pendingConnections.clear();\n\n // Cancel all in-flight discoveries\n for (const id of ids) {\n this.mcpConnections[id].cancelDiscovery();\n }\n\n const results = await Promise.allSettled(\n ids.map(async (id) => {\n try {\n await this.mcpConnections[id].close();\n } finally {\n this.cleanupClosedConnection(id);\n }\n })\n );\n\n const errors = results.flatMap((result) =>\n result.status === \"rejected\" ? [result.reason] : []\n );\n\n if (errors.length === 1) {\n throw errors[0];\n }\n\n if (errors.length > 1) {\n throw new AggregateError(\n errors,\n \"Failed to close one or more MCP connections\"\n );\n }\n }\n\n /**\n * Closes a connection to an MCP server\n * @param id The id of the connection to close\n */\n async closeConnection(id: string) {\n const connection = this.mcpConnections[id];\n if (!connection) {\n throw new Error(`Connection with id \"${id}\" does not exist.`);\n }\n\n // Cancel any in-flight discovery\n connection.cancelDiscovery();\n\n // Remove from pending so waitForConnections() doesn't block on a closed server\n this._pendingConnections.delete(id);\n\n try {\n await connection.close();\n } finally {\n this.cleanupClosedConnection(id);\n }\n }\n\n /**\n * Remove an MCP server - closes connection if active and removes from storage.\n */\n async removeServer(serverId: string): Promise<void> {\n if (this.mcpConnections[serverId]) {\n try {\n await this.closeConnection(serverId);\n } catch (_e) {\n // Ignore errors when closing\n }\n }\n this.removeServerFromStorage(serverId);\n this._onServerStateChanged.fire();\n }\n\n /**\n * List all MCP servers from storage\n */\n listServers(): MCPServerRow[] {\n return this.getServersFromStorage();\n }\n\n /**\n * Dispose the manager and all resources.\n */\n async dispose(): Promise<void> {\n try {\n await this.closeAllConnections();\n } finally {\n // Dispose manager-level emitters\n this._onServerStateChanged.dispose();\n this._onObservabilityEvent.dispose();\n }\n }\n\n /**\n * @param filter - Optional filter to scope results to specific servers\n * @returns namespaced list of prompts\n */\n listPrompts(filter?: MCPServerFilter): NamespacedData[\"prompts\"] {\n return getNamespacedData(this.filterConnections(filter), \"prompts\");\n }\n\n /**\n * @param filter - Optional filter to scope results to specific servers\n * @returns namespaced list of resources\n */\n listResources(filter?: MCPServerFilter): NamespacedData[\"resources\"] {\n return getNamespacedData(this.filterConnections(filter), \"resources\");\n }\n\n /**\n * @param filter - Optional filter to scope results to specific servers\n * @returns namespaced list of resource templates\n */\n listResourceTemplates(\n filter?: MCPServerFilter\n ): NamespacedData[\"resourceTemplates\"] {\n return getNamespacedData(\n this.filterConnections(filter),\n \"resourceTemplates\"\n );\n }\n\n /**\n * Namespaced version of callTool\n */\n async callTool(\n params: CallToolRequest[\"params\"] & { serverId: string },\n resultSchema?:\n | typeof CallToolResultSchema\n | typeof CompatibilityCallToolResultSchema,\n options?: RequestOptions\n ) {\n const { serverId, ...mcpParams } = params;\n const unqualifiedName = mcpParams.name.replace(`${serverId}.`, \"\");\n return this.mcpConnections[serverId].client.callTool(\n {\n ...mcpParams,\n name: unqualifiedName\n },\n resultSchema,\n options\n );\n }\n\n /**\n * Namespaced version of readResource\n */\n readResource(\n params: ReadResourceRequest[\"params\"] & { serverId: string },\n options: RequestOptions\n ) {\n return this.mcpConnections[params.serverId].client.readResource(\n params,\n options\n );\n }\n\n /**\n * Namespaced version of getPrompt\n */\n getPrompt(\n params: GetPromptRequest[\"params\"] & { serverId: string },\n options: RequestOptions\n ) {\n return this.mcpConnections[params.serverId].client.getPrompt(\n params,\n options\n );\n }\n}\n\ntype NamespacedData = {\n tools: (Tool & { serverId: string })[];\n prompts: (Prompt & { serverId: string })[];\n resources: (Resource & { serverId: string })[];\n resourceTemplates: (ResourceTemplate & { serverId: string })[];\n};\n\nexport function getNamespacedData<T extends keyof NamespacedData>(\n mcpClients: Record<string, MCPClientConnection>,\n type: T\n): NamespacedData[T] {\n const sets = Object.entries(mcpClients).map(([name, conn]) => {\n return { data: conn[type], name };\n });\n\n const namespacedData = sets.flatMap(({ name: serverId, data }) => {\n return data.map((item) => {\n return {\n ...item,\n // we add a serverId so we can easily pull it out and send the tool call to the right server\n serverId\n };\n });\n });\n\n return namespacedData as NamespacedData[T]; // Type assertion needed due to TS limitations with conditional return types\n}\n"],"mappings":"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"}
|
package/dist/client.d.ts
CHANGED
|
@@ -729,7 +729,7 @@ declare class MCPClientConnection {
|
|
|
729
729
|
*/
|
|
730
730
|
getTransport(
|
|
731
731
|
transportType: BaseTransportType
|
|
732
|
-
):
|
|
732
|
+
): StreamableHTTPClientTransport | SSEClientTransport | RPCClientTransport;
|
|
733
733
|
private tryConnect;
|
|
734
734
|
private _capabilityErrorHandler;
|
|
735
735
|
}
|
|
@@ -1640,6 +1640,11 @@ type FiberRecoveryContext = {
|
|
|
1640
1640
|
/** Fiber ID. */ id: string /** Name passed to `runFiber`. */;
|
|
1641
1641
|
name: string /** Last checkpoint data from `stash()`, or null if never stashed. */;
|
|
1642
1642
|
snapshot: unknown | null;
|
|
1643
|
+
/**
|
|
1644
|
+
* Epoch milliseconds when the fiber row was inserted (when `runFiber`
|
|
1645
|
+
* started). Use `Date.now() - createdAt` to gate stale recoveries.
|
|
1646
|
+
*/
|
|
1647
|
+
createdAt: number;
|
|
1643
1648
|
[key: string]: unknown;
|
|
1644
1649
|
};
|
|
1645
1650
|
/**
|
|
@@ -1898,6 +1903,18 @@ declare class Agent<
|
|
|
1898
1903
|
* @param excludeIds Additional connection IDs to exclude (e.g. the source)
|
|
1899
1904
|
*/
|
|
1900
1905
|
private _broadcastProtocol;
|
|
1906
|
+
/**
|
|
1907
|
+
* When running as a facet, the parent DO owns the WebSocket registry
|
|
1908
|
+
* (`ctx.getWebSockets()`). Iterating from the child isolate throws
|
|
1909
|
+
* "Cannot perform I/O on behalf of a different Durable Object".
|
|
1910
|
+
* Downstream callers (e.g. chat-streaming paths) invoke
|
|
1911
|
+
* `this.broadcast()` directly, bypassing `_broadcastProtocol`'s
|
|
1912
|
+
* guard, so override at the base to catch every path.
|
|
1913
|
+
*/
|
|
1914
|
+
broadcast(
|
|
1915
|
+
msg: string | ArrayBuffer | ArrayBufferView,
|
|
1916
|
+
without?: string[]
|
|
1917
|
+
): void;
|
|
1901
1918
|
private _setStateInternal;
|
|
1902
1919
|
/**
|
|
1903
1920
|
* Update the Agent's state
|
|
@@ -2369,12 +2386,23 @@ declare class Agent<
|
|
|
2369
2386
|
*/
|
|
2370
2387
|
alarm(): Promise<void>;
|
|
2371
2388
|
/**
|
|
2372
|
-
*
|
|
2373
|
-
*
|
|
2374
|
-
*
|
|
2375
|
-
*
|
|
2389
|
+
* Initialize this agent as a facet in a single RPC.
|
|
2390
|
+
*
|
|
2391
|
+
* Runs entirely inside the child's isolate, so every storage write
|
|
2392
|
+
* and `onStart()` I/O is owned by the child DO. This replaces the
|
|
2393
|
+
* previous "construct a Request in the parent DO and `stub.fetch()`
|
|
2394
|
+
* it on the child" handshake, whose native I/O was tied to the
|
|
2395
|
+
* parent and triggered "Cannot perform I/O on behalf of a different
|
|
2396
|
+
* Durable Object" on the child.
|
|
2397
|
+
*
|
|
2398
|
+
* Order matters: set `_isFacet` BEFORE triggering initialization, so
|
|
2399
|
+
* the first `onStart()` run (which calls `broadcastMcpServers`) sees
|
|
2400
|
+
* the flag and skips broadcasts that would touch the parent DO's
|
|
2401
|
+
* WebSocket registry.
|
|
2402
|
+
*
|
|
2403
|
+
* @internal Called by {@link subAgent}.
|
|
2376
2404
|
*/
|
|
2377
|
-
|
|
2405
|
+
_cf_initAsFacet(name: string): Promise<void>;
|
|
2378
2406
|
/**
|
|
2379
2407
|
* Get or create a named sub-agent — a child Durable Object (facet)
|
|
2380
2408
|
* with its own isolated SQLite storage running on the same machine.
|
|
@@ -2383,7 +2411,7 @@ declare class Agent<
|
|
|
2383
2411
|
* entry point. The first call for a given name triggers the child's
|
|
2384
2412
|
* `onStart()`. Subsequent calls return the existing instance.
|
|
2385
2413
|
*
|
|
2386
|
-
* @experimental
|
|
2414
|
+
* @experimental The API surface may change before stabilizing.
|
|
2387
2415
|
*
|
|
2388
2416
|
* @param cls The Agent subclass (must be exported from the worker)
|
|
2389
2417
|
* @param name Unique name for this child instance
|
|
@@ -2405,7 +2433,7 @@ declare class Agent<
|
|
|
2405
2433
|
* Pending RPC calls receive the reason as an error.
|
|
2406
2434
|
* Transitively aborts the child's own children.
|
|
2407
2435
|
*
|
|
2408
|
-
* @experimental
|
|
2436
|
+
* @experimental The API surface may change before stabilizing.
|
|
2409
2437
|
*
|
|
2410
2438
|
* @param cls The Agent subclass used when creating the child
|
|
2411
2439
|
* @param name Name of the child to abort
|
|
@@ -2416,7 +2444,7 @@ declare class Agent<
|
|
|
2416
2444
|
* Delete a sub-agent: abort it if running, then permanently wipe its
|
|
2417
2445
|
* storage. Transitively deletes the child's own children.
|
|
2418
2446
|
*
|
|
2419
|
-
* @experimental
|
|
2447
|
+
* @experimental The API surface may change before stabilizing.
|
|
2420
2448
|
*
|
|
2421
2449
|
* @param cls The Agent subclass used when creating the child
|
|
2422
2450
|
* @param name Name of the child to delete
|
|
@@ -3080,4 +3108,4 @@ export {
|
|
|
3080
3108
|
QueueItem as y,
|
|
3081
3109
|
MCPClientOAuthResult as z
|
|
3082
3110
|
};
|
|
3083
|
-
//# sourceMappingURL=index-
|
|
3111
|
+
//# sourceMappingURL=index-D49HdAiY.d.ts.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createHeaderBasedEmailResolver, signAgentHeaders } from "./email.js";
|
|
|
4
4
|
import { __DO_NOT_USE_WILL_BREAK__agentContext } from "./internal_context.js";
|
|
5
5
|
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-BVdP0e3Z.js";
|
|
6
6
|
import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
|
|
7
|
-
import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-
|
|
7
|
+
import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-PEDsNnfY.js";
|
|
8
8
|
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider.js";
|
|
9
9
|
import { genericObservability } from "./observability/index.js";
|
|
10
10
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -718,10 +718,23 @@ var Agent = class Agent extends Server {
|
|
|
718
718
|
* @param excludeIds Additional connection IDs to exclude (e.g. the source)
|
|
719
719
|
*/
|
|
720
720
|
_broadcastProtocol(msg, excludeIds = []) {
|
|
721
|
+
if (this._isFacet) return;
|
|
721
722
|
const exclude = [...excludeIds];
|
|
722
723
|
for (const conn of this.getConnections()) if (!this.isConnectionProtocolEnabled(conn)) exclude.push(conn.id);
|
|
723
724
|
this.broadcast(msg, exclude);
|
|
724
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
* When running as a facet, the parent DO owns the WebSocket registry
|
|
728
|
+
* (`ctx.getWebSockets()`). Iterating from the child isolate throws
|
|
729
|
+
* "Cannot perform I/O on behalf of a different Durable Object".
|
|
730
|
+
* Downstream callers (e.g. chat-streaming paths) invoke
|
|
731
|
+
* `this.broadcast()` directly, bypassing `_broadcastProtocol`'s
|
|
732
|
+
* guard, so override at the base to catch every path.
|
|
733
|
+
*/
|
|
734
|
+
broadcast(msg, without) {
|
|
735
|
+
if (this._isFacet) return;
|
|
736
|
+
super.broadcast(msg, without);
|
|
737
|
+
}
|
|
725
738
|
_setStateInternal(nextState, source = "server") {
|
|
726
739
|
this.validateStateChange(nextState, source);
|
|
727
740
|
this._state = nextState;
|
|
@@ -1801,7 +1814,7 @@ var Agent = class Agent extends Server {
|
|
|
1801
1814
|
if (this._runFiberRecoveryInProgress) return;
|
|
1802
1815
|
this._runFiberRecoveryInProgress = true;
|
|
1803
1816
|
try {
|
|
1804
|
-
const rows = this.sql`SELECT id, name, snapshot FROM cf_agents_runs`;
|
|
1817
|
+
const rows = this.sql`SELECT id, name, snapshot, created_at FROM cf_agents_runs`;
|
|
1805
1818
|
for (const row of rows) {
|
|
1806
1819
|
if (this._runFiberActiveFibers.has(row.id)) continue;
|
|
1807
1820
|
let snapshot = null;
|
|
@@ -1813,7 +1826,8 @@ var Agent = class Agent extends Server {
|
|
|
1813
1826
|
const ctx = {
|
|
1814
1827
|
id: row.id,
|
|
1815
1828
|
name: row.name,
|
|
1816
|
-
snapshot
|
|
1829
|
+
snapshot,
|
|
1830
|
+
createdAt: row.created_at
|
|
1817
1831
|
};
|
|
1818
1832
|
try {
|
|
1819
1833
|
if (!await this._handleInternalFiberRecovery(ctx)) await this.onFiberRecovered(ctx);
|
|
@@ -1987,14 +2001,26 @@ var Agent = class Agent extends Server {
|
|
|
1987
2001
|
await this._scheduleNextAlarm();
|
|
1988
2002
|
}
|
|
1989
2003
|
/**
|
|
1990
|
-
*
|
|
1991
|
-
*
|
|
1992
|
-
*
|
|
1993
|
-
*
|
|
2004
|
+
* Initialize this agent as a facet in a single RPC.
|
|
2005
|
+
*
|
|
2006
|
+
* Runs entirely inside the child's isolate, so every storage write
|
|
2007
|
+
* and `onStart()` I/O is owned by the child DO. This replaces the
|
|
2008
|
+
* previous "construct a Request in the parent DO and `stub.fetch()`
|
|
2009
|
+
* it on the child" handshake, whose native I/O was tied to the
|
|
2010
|
+
* parent and triggered "Cannot perform I/O on behalf of a different
|
|
2011
|
+
* Durable Object" on the child.
|
|
2012
|
+
*
|
|
2013
|
+
* Order matters: set `_isFacet` BEFORE triggering initialization, so
|
|
2014
|
+
* the first `onStart()` run (which calls `broadcastMcpServers`) sees
|
|
2015
|
+
* the flag and skips broadcasts that would touch the parent DO's
|
|
2016
|
+
* WebSocket registry.
|
|
2017
|
+
*
|
|
2018
|
+
* @internal Called by {@link subAgent}.
|
|
1994
2019
|
*/
|
|
1995
|
-
async
|
|
2020
|
+
async _cf_initAsFacet(name) {
|
|
1996
2021
|
this._isFacet = true;
|
|
1997
|
-
await this.ctx.storage.put("cf_agents_is_facet", true);
|
|
2022
|
+
await Promise.all([this.ctx.storage.put("cf_agents_is_facet", true), this.ctx.storage.put("__ps_name", name)]);
|
|
2023
|
+
await this.__unsafe_ensureInitialized();
|
|
1998
2024
|
}
|
|
1999
2025
|
/**
|
|
2000
2026
|
* Get or create a named sub-agent — a child Durable Object (facet)
|
|
@@ -2004,7 +2030,7 @@ var Agent = class Agent extends Server {
|
|
|
2004
2030
|
* entry point. The first call for a given name triggers the child's
|
|
2005
2031
|
* `onStart()`. Subsequent calls return the existing instance.
|
|
2006
2032
|
*
|
|
2007
|
-
* @experimental
|
|
2033
|
+
* @experimental The API surface may change before stabilizing.
|
|
2008
2034
|
*
|
|
2009
2035
|
* @param cls The Agent subclass (must be exported from the worker)
|
|
2010
2036
|
* @param name Unique name for this child instance
|
|
@@ -2018,14 +2044,11 @@ var Agent = class Agent extends Server {
|
|
|
2018
2044
|
*/
|
|
2019
2045
|
async subAgent(cls, name) {
|
|
2020
2046
|
const ctx = this.ctx;
|
|
2021
|
-
if (!ctx.facets || !ctx.exports) throw new Error("subAgent()
|
|
2047
|
+
if (!ctx.facets || !ctx.exports) throw new Error("subAgent() is not supported in this runtime — `ctx.facets` / `ctx.exports` are unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
|
|
2022
2048
|
if (!ctx.exports[cls.name]) throw new Error(`Sub-agent class "${cls.name}" not found in worker exports. Make sure the class is exported from your worker entry point and that the export name matches the class name.`);
|
|
2023
2049
|
const facetKey = `${cls.name}\0${name}`;
|
|
2024
2050
|
const stub = ctx.facets.get(facetKey, () => ({ class: ctx.exports[cls.name] }));
|
|
2025
|
-
|
|
2026
|
-
req.headers.set("x-partykit-room", name);
|
|
2027
|
-
await stub.fetch(req).then((res) => res.text());
|
|
2028
|
-
await stub._cf_markAsFacet();
|
|
2051
|
+
await stub._cf_initAsFacet(name);
|
|
2029
2052
|
return stub;
|
|
2030
2053
|
}
|
|
2031
2054
|
/**
|
|
@@ -2034,7 +2057,7 @@ var Agent = class Agent extends Server {
|
|
|
2034
2057
|
* Pending RPC calls receive the reason as an error.
|
|
2035
2058
|
* Transitively aborts the child's own children.
|
|
2036
2059
|
*
|
|
2037
|
-
* @experimental
|
|
2060
|
+
* @experimental The API surface may change before stabilizing.
|
|
2038
2061
|
*
|
|
2039
2062
|
* @param cls The Agent subclass used when creating the child
|
|
2040
2063
|
* @param name Name of the child to abort
|
|
@@ -2042,7 +2065,7 @@ var Agent = class Agent extends Server {
|
|
|
2042
2065
|
*/
|
|
2043
2066
|
abortSubAgent(cls, name, reason) {
|
|
2044
2067
|
const ctx = this.ctx;
|
|
2045
|
-
if (!ctx.facets) throw new Error("abortSubAgent()
|
|
2068
|
+
if (!ctx.facets) throw new Error("abortSubAgent() is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
|
|
2046
2069
|
const facetKey = `${cls.name}\0${name}`;
|
|
2047
2070
|
ctx.facets.abort(facetKey, reason);
|
|
2048
2071
|
}
|
|
@@ -2050,14 +2073,14 @@ var Agent = class Agent extends Server {
|
|
|
2050
2073
|
* Delete a sub-agent: abort it if running, then permanently wipe its
|
|
2051
2074
|
* storage. Transitively deletes the child's own children.
|
|
2052
2075
|
*
|
|
2053
|
-
* @experimental
|
|
2076
|
+
* @experimental The API surface may change before stabilizing.
|
|
2054
2077
|
*
|
|
2055
2078
|
* @param cls The Agent subclass used when creating the child
|
|
2056
2079
|
* @param name Name of the child to delete
|
|
2057
2080
|
*/
|
|
2058
2081
|
deleteSubAgent(cls, name) {
|
|
2059
2082
|
const ctx = this.ctx;
|
|
2060
|
-
if (!ctx.facets) throw new Error("deleteSubAgent()
|
|
2083
|
+
if (!ctx.facets) throw new Error("deleteSubAgent() is not supported in this runtime — `ctx.facets` is unavailable. Update to the latest `compatibility_date` in your wrangler.jsonc.");
|
|
2061
2084
|
const facetKey = `${cls.name}\0${name}`;
|
|
2062
2085
|
ctx.facets.delete(facetKey);
|
|
2063
2086
|
}
|