agents 0.17.4 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-tool-types-OhWqAbCp.d.ts → agent-tool-types-BNUGGBzQ.d.ts} +30 -11
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-LdNKGZT9.d.ts → agent-tools-BFbzVLFc.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/chat/index.d.ts +29 -9
- package/dist/chat/index.js +31 -41
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +25 -1
- package/dist/chat/react.js +249 -93
- package/dist/chat/react.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/{client-C7F0MaVz.js → client-CcjiFpTf.js} +176 -42
- package/dist/client-CcjiFpTf.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/cloudflare-BldFV0Pa.js +117 -0
- package/dist/cloudflare-BldFV0Pa.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +118 -43
- 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/observability/ai/index.d.ts +155 -0
- package/dist/observability/ai/index.js +1845 -0
- package/dist/observability/ai/index.js.map +1 -0
- package/dist/react.d.ts +1 -1
- package/dist/{retries-CvHJwSuh.d.ts → retries-CAvxtG9d.d.ts} +16 -7
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +20 -2
- package/dist/retries.js.map +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/sub-routing.d.ts +1 -1
- package/dist/vite.d.ts +4 -4
- package/dist/vite.js +4 -2
- package/dist/vite.js.map +1 -1
- package/dist/{wire-types-nflOzNuU.js → wire-types-CU9rLoeS.js} +33 -2
- package/dist/wire-types-CU9rLoeS.js.map +1 -0
- package/dist/workflows.d.ts +1 -1
- package/docs/agent-class.md +9 -1
- package/docs/configuration.md +20 -0
- package/docs/mcp-client.md +13 -4
- package/docs/observability.md +282 -0
- package/package.json +7 -2
- package/dist/client-C7F0MaVz.js.map +0 -1
- package/dist/wire-types-nflOzNuU.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client-C7F0MaVz.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 {\n isJSONRPCErrorResponse,\n isJSONRPCResultResponse,\n JSONRPCMessageSchema\n} 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<Cloudflare.Env, McpAgent>(\n this._namespace,\n 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\ntype PendingRPCResponse = {\n messages: JSONRPCMessage[];\n resolve: (response: JSONRPCMessage | JSONRPCMessage[] | undefined) => void;\n reject: (error: Error) => void;\n timeoutId: ReturnType<typeof setTimeout>;\n};\n\nexport class RPCServerTransport implements Transport {\n private _started = false;\n private _protocolVersion?: string;\n private _timeout: number;\n private _pendingRequests = new Map<string, PendingRPCResponse>();\n private _pendingContinuations: PendingRPCResponse[] = [];\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\n const error = new Error(\"Transport closed\");\n for (const pending of this._pendingRequests.values()) {\n clearTimeout(pending.timeoutId);\n pending.reject(error);\n }\n this._pendingRequests.clear();\n\n for (const pending of this._pendingContinuations) {\n clearTimeout(pending.timeoutId);\n pending.reject(error);\n }\n this._pendingContinuations = [];\n }\n\n private _makeTimeout(onTimeout: () => void): ReturnType<typeof setTimeout> {\n return setTimeout(onTimeout, this._timeout);\n }\n\n private _appendPending(\n pending: PendingRPCResponse,\n message: JSONRPCMessage\n ): void {\n pending.messages.push(message);\n }\n\n private _completePending(\n pending: PendingRPCResponse,\n message: JSONRPCMessage\n ): void {\n pending.messages.push(message);\n clearTimeout(pending.timeoutId);\n\n const messages = pending.messages;\n queueMicrotask(() => {\n pending.resolve(messages.length === 1 ? messages[0] : messages);\n });\n }\n\n private _completeRequest(key: string, message: JSONRPCMessage): boolean {\n const pending = this._pendingRequests.get(key);\n if (!pending) return false;\n\n this._pendingRequests.delete(key);\n this._completePending(pending, message);\n return true;\n }\n\n private _appendRequest(key: string, message: JSONRPCMessage): boolean {\n const pending = this._pendingRequests.get(key);\n if (!pending) return false;\n\n this._appendPending(pending, message);\n return true;\n }\n\n private _completeContinuation(message: JSONRPCMessage): boolean {\n const pending = this._pendingContinuations.shift();\n if (!pending) return false;\n\n this._completePending(pending, message);\n return true;\n }\n\n private _appendContinuation(message: JSONRPCMessage): boolean {\n const pending = this._pendingContinuations[0];\n if (!pending) return false;\n\n this._appendPending(pending, message);\n return true;\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 (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {\n const id = message.id;\n if (id === undefined) {\n this.onerror?.(\n new Error(`RPC response missing id: ${JSON.stringify(message)}`)\n );\n return;\n }\n\n if (this._completeRequest(id.toString(), message)) {\n return;\n }\n\n if (this._completeContinuation(message)) {\n return;\n }\n\n this.onerror?.(\n new Error(\n `No pending RPC request found for response: ${JSON.stringify(message)}`\n )\n );\n return;\n }\n\n const relatedRequestId = options?.relatedRequestId?.toString();\n const expectsResponse = \"id\" in message;\n\n if (relatedRequestId) {\n if (expectsResponse) {\n if (this._completeRequest(relatedRequestId, message)) return;\n } else if (this._appendRequest(relatedRequestId, message)) {\n return;\n }\n }\n\n if (expectsResponse) {\n if (this._completeContinuation(message)) return;\n } else if (this._appendContinuation(message)) {\n return;\n }\n\n this.onerror?.(\n new Error(\n `No pending RPC request found for message: ${JSON.stringify(message)}`\n )\n );\n }\n\n /**\n * @internal Called by McpAgent.handleMcpMessage() — not for external use.\n *\n * Wait for the next unmatched send() call that expects a client response or\n * completes a resumed tool call.\n *\n * Used after resolving an elicitation response: the original tool call has\n * already returned the elicitation request to the RPC client, and the resumed\n * tool handler will eventually send the final tool result. That final response\n * has the original tool request id, so there is no active handle() waiter left\n * for id-based routing; this continuation waiter receives it instead.\n */\n async _awaitPendingResponse(): Promise<\n JSONRPCMessage | JSONRPCMessage[] | undefined\n > {\n if (!this._started) {\n throw new Error(\"Transport not started\");\n }\n\n return await new Promise<JSONRPCMessage | JSONRPCMessage[] | undefined>(\n (resolve, reject) => {\n const pending: PendingRPCResponse = {\n messages: [],\n resolve,\n reject,\n timeoutId: this._makeTimeout(() => {\n const index = this._pendingContinuations.indexOf(pending);\n if (index !== -1) {\n this._pendingContinuations.splice(index, 1);\n }\n reject(\n new Error(\n `Request timeout: No response received within ${this._timeout}ms`\n )\n );\n })\n };\n this._pendingContinuations.push(pending);\n }\n );\n }\n\n async handle(\n message: JSONRPCMessage | JSONRPCMessage[]\n ): Promise<JSONRPCMessage | JSONRPCMessage[] | undefined> {\n if (!this._started) {\n throw new Error(\"Transport not started\");\n }\n\n if (Array.isArray(message)) {\n validateBatch(message);\n\n const responses = await Promise.all(\n message.map((msg) => this.handle(msg))\n );\n const flattened = responses.flatMap((response) => {\n if (response === undefined) return [];\n return Array.isArray(response) ? response : [response];\n });\n\n return flattened.length === 0 ? undefined : flattened;\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 const isNotification = !(\"id\" in message);\n if (isNotification) {\n this.onmessage?.(message);\n return undefined;\n }\n\n const id = message.id?.toString();\n if (!id) {\n return makeInvalidRequestError(message.id);\n }\n\n if (this._pendingRequests.has(id)) {\n throw new Error(`Duplicate pending RPC request id: ${id}`);\n }\n\n const responsePromise = new Promise<\n JSONRPCMessage | JSONRPCMessage[] | undefined\n >((resolve, reject) => {\n const pending: PendingRPCResponse = {\n messages: [],\n resolve,\n reject,\n timeoutId: this._makeTimeout(() => {\n this._pendingRequests.delete(id);\n reject(\n new Error(\n `Request timeout: No response received within ${this._timeout}ms`\n )\n );\n })\n };\n\n this._pendingRequests.set(id, pending);\n });\n\n this.onmessage?.(message);\n\n return await responsePromise;\n }\n}\n","import { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { CfWorkerJsonSchemaValidator } from \"@modelcontextprotocol/sdk/validation/cfworker-provider.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// Workers disallows runtime code generation, so the MCP SDK's default AJV\n// validator (which compiles schemas with `new Function`) cannot run there.\n// Every connection defaults to the Worker-safe validator unless the caller\n// supplies their own.\nconst defaultClientOptions: McpClientOptions = {\n jsonSchemaValidator: new CfWorkerJsonSchemaValidator()\n};\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\n/**\n * Handler for server-initiated `elicitation/create` requests.\n * Held in memory only — never persisted — so it must be re-supplied when a\n * connection is recreated (e.g. after Durable Object hibernation).\n */\nexport type MCPElicitationHandler = (\n request: ElicitRequest\n) => Promise<ElicitResult>;\n\nexport type MCPElicitationHandlers = {\n form?: MCPElicitationHandler;\n url?: MCPElicitationHandler;\n};\n\n/** Derive the elicitation capability to advertise from the handler keys. */\nexport function elicitationCapabilitiesFromHandlers(handlers?: {\n form?: unknown;\n url?: unknown;\n}): ClientCapabilities[\"elicitation\"] | undefined {\n if (!handlers) return undefined;\n\n const elicitation: NonNullable<ClientCapabilities[\"elicitation\"]> = {};\n if (handlers.form) {\n elicitation.form = {};\n }\n if (handlers.url) {\n elicitation.url = {};\n }\n\n return elicitation.form || elicitation.url ? elicitation : undefined;\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\n /**\n * Transport that received the 401 during the initial connect attempt.\n * Kept so finishAuth() runs on the transport that captured the resource\n * metadata URL from the WWW-Authenticate header — a fresh transport would\n * rediscover from defaults and exchange the code at the wrong token\n * endpoint when the authorization server is not at the default location.\n */\n private _pendingAuthTransport?:\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 /**\n * Whether the connection advertised the elicitation capability. The SDK\n * client refuses to register an `elicitation/create` request handler when\n * the capability was not declared, so handler registration is gated on\n * this.\n */\n private _elicitationEnabled = false;\n\n constructor(\n public url: URL,\n private readonly _info: ConstructorParameters<typeof Client>[0],\n public options: {\n transport: MCPTransportOptions;\n client: McpClientOptions;\n elicitationHandlers?: MCPElicitationHandlers;\n /**\n * Client capabilities persisted from a previous session, advertised\n * until handlers are reconfigured after a hibernation restore. Cleared\n * by {@link configureElicitationHandlers} — reconfigured handlers are\n * the source of truth. Explicit `client.capabilities` win per key.\n */\n capabilitySeed?: ClientCapabilities;\n } = { client: {}, transport: {} }\n ) {\n this.options = {\n ...options,\n client: { ...defaultClientOptions, ...options.client }\n };\n\n this.client = this.createClient();\n }\n\n private createClient(): Client {\n // Advertise elicitation only when it can actually be handled. Handler\n // keys map directly to advertised modes, so a form-only handler advertises\n // form only, a url-only handler advertises url only, and no handlers means\n // no elicitation capability. An explicit caller-declared\n // `capabilities.elicitation` wins wholesale so callers can narrow (or\n // widen) the advertised modes. The restore seed applies last, covering\n // the window before handlers are reconfigured after hibernation.\n const seed = this.options.capabilitySeed;\n const elicitation =\n this.options.client?.capabilities?.elicitation ??\n elicitationCapabilitiesFromHandlers(this.options.elicitationHandlers) ??\n seed?.elicitation;\n this._elicitationEnabled = elicitation !== undefined;\n const clientOptions = {\n ...this.options.client,\n capabilities: {\n ...seed,\n ...this.options.client?.capabilities,\n ...(elicitation ? { elicitation } : {})\n } as ClientCapabilities\n };\n\n return new Client(this._info, clientOptions);\n }\n\n /**\n * Configure the handler used for server-initiated elicitation requests.\n *\n * If the connection has not been initialized yet, rebuild the SDK client so\n * handler-driven elicitation capabilities are reflected in the initial\n * handshake. A rebuild (rather than `Client.registerCapabilities`) is\n * required because SDK capability registration is merge-only — it cannot\n * un-advertise a mode when handlers are cleared before connecting. Active\n * connections keep their negotiated capabilities until they reconnect.\n */\n configureElicitationHandlers(handlers?: MCPElicitationHandlers): void {\n this.options.elicitationHandlers = handlers;\n // Handlers are now the source of truth — drop the restore seed so\n // clearing the handlers un-advertises the capability on rebuild.\n this.options.capabilitySeed = undefined;\n\n if (!this.client.transport) {\n this.client = this.createClient();\n }\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 // init() can be re-entered after a mid-session 401 → OAuth → reconnect\n // cycle (e.g. scope step-up, token revocation). The SDK client refuses\n // to connect while a previous transport is still attached, so detach it\n // first. Rebuild the client so the new handshake advertises the current\n // handler-derived capabilities — reconnects are documented as the point\n // where handler changes on a live connection take effect.\n if (this.client.transport) {\n this._transport = undefined;\n try {\n await this.client.close();\n } catch {\n // Closing a transport that just failed auth is best-effort.\n }\n this.client = this.createClient();\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 the elicitation request handler after a successful\n // connection. Only when the capability was advertised — the SDK\n // client throws on registering a handler for an undeclared capability.\n if (this._elicitationEnabled) {\n this.client.setRequestHandler(\n ElicitRequestSchema,\n async (request: ElicitRequest) => {\n return await this.handleElicitationRequest(request);\n }\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 // Prefer the transport that triggered authentication (initial-connect\n // 401, or the active transport for a mid-session 401 such as a scope\n // step-up): it holds the resource metadata URL from the WWW-Authenticate\n // header that finishAuth() needs to locate the authorization server.\n const authTransport = this._pendingAuthTransport ?? this._transport;\n this._pendingAuthTransport = undefined;\n if (\n authTransport &&\n \"finishAuth\" in authTransport &&\n typeof authTransport.finishAuth === \"function\"\n ) {\n await authTransport.finishAuth(code);\n return;\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(\n code: string,\n options: { alreadyAccepted?: boolean } = {}\n ): Promise<void> {\n const expectedState = options.alreadyAccepted\n ? MCPConnectionState.CONNECTING\n : MCPConnectionState.AUTHENTICATING;\n if (this.connectionState !== expectedState) {\n throw new Error(\n `Connection must be in ${expectedState} state to complete authorization`\n );\n }\n\n if (!options.alreadyAccepted) {\n this.connectionState = MCPConnectionState.CONNECTING;\n }\n\n try {\n // Finish OAuth by probing transports per configuration\n await this.finishAuthProbe(code);\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 *\n * Delegates to the `elicitationHandlers` connection option when provided.\n *\n * @deprecated Overriding or instance-patching this method directly is\n * deprecated — pass the `elicitationHandlers` connection option instead.\n */\n async handleElicitationRequest(\n request: ElicitRequest\n ): Promise<ElicitResult> {\n const mode = request.params.mode === \"url\" ? \"url\" : \"form\";\n const handler = this.options.elicitationHandlers?.[mode];\n if (handler) {\n return handler(request);\n }\n if (this.options.elicitationHandlers) {\n throw new Error(\n `No MCP ${mode}-mode elicitation handler configured for this connection.`\n );\n }\n throw new Error(\n \"Elicitation handler must be implemented for your platform. Provide the MCPClientConnection elicitationHandlers option, or register handlers through the MCP client manager before connecting.\"\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 this._pendingAuthTransport = undefined;\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 this._pendingAuthTransport = transport;\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 ClientCapabilities,\n CompatibilityCallToolResultSchema,\n ElicitRequest,\n ElicitResult,\n GetPromptRequest,\n Prompt,\n ReadResourceRequest,\n Resource,\n ResourceTemplate,\n Tool\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { type RetryOptions, tryN } from \"../retries\";\nimport { z } from \"zod\";\nimport { nanoid } from \"nanoid\";\nimport { Emitter, type Event, DisposableStore } from \"../core/events\";\nimport type { MCPObservabilityEvent } from \"../observability/mcp\";\nimport {\n elicitationCapabilitiesFromHandlers,\n MCPClientConnection,\n MCPConnectionState,\n type MCPElicitationHandlers,\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\nexport type MCPAITool = {\n description?: string;\n title?: string;\n execute: (\n args: Record<string, unknown>,\n options?: unknown\n ) => Promise<unknown>;\n inputSchema: z.ZodType;\n outputSchema?: z.ZodType;\n};\n\n/**\n * Structural tool set returned by {@link MCPClientManager.getAITools}.\n * Compatible with the AI SDK without importing its types into the core\n * `agents` declaration graph.\n */\nexport type MCPAIToolSet = Record<string, MCPAITool>;\n\n/** Maximum length of a normalized MCP server id. */\nexport const MCP_SERVER_ID_MAX_LENGTH = 64;\n\n/**\n * Normalize a caller-supplied MCP server id into a stable, storage- and\n * tool-name-safe form.\n *\n * The id is surfaced in several places where the character set matters:\n * - as the primary key in the `cf_agents_mcp_servers` SQLite table\n * - embedded in AI SDK tool names as `` `tool_${id.replace(/-/g, \"\")}_${tool}` ``\n * (tool names must match `/^[A-Za-z0-9_]+$/`)\n * - as a key on the `mcpConnections` map and OAuth provider storage\n *\n * Rules:\n * 1. Lowercase.\n * 2. Replace any run of disallowed characters with a single `-`.\n * 3. Collapse repeated `-` and trim leading/trailing `-`/`_`.\n * 4. Prefix with `id-` if the result is empty or doesn't start with a letter.\n * 5. Truncate to {@link MCP_SERVER_ID_MAX_LENGTH} characters.\n *\n * @example\n * normalizeServerId(\"my-supplied-id\"); // \"my-supplied-id\"\n * normalizeServerId(\"GitHub MCP!\"); // \"github-mcp\"\n * normalizeServerId(\"42-things\"); // \"id-42-things\"\n */\nexport function normalizeServerId(input: string): string {\n if (typeof input !== \"string\") {\n throw new TypeError(\n `normalizeServerId: expected string, got ${typeof input}`\n );\n }\n\n let id = input\n .toLowerCase()\n .replace(/[^a-z0-9_-]+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^[-_]+|[-_]+$/g, \"\");\n\n if (id.length === 0 || !/^[a-z]/.test(id)) {\n id = `id-${id}`.replace(/-+$/g, \"\");\n }\n\n if (id.length > MCP_SERVER_ID_MAX_LENGTH) {\n id = id.slice(0, MCP_SERVER_ID_MAX_LENGTH).replace(/-+$/g, \"\");\n }\n\n return id;\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 * Client capabilities advertised from configured handlers (currently the\n * elicitation modes). Handlers are functions and cannot be persisted, so\n * this records what they advertised for restores after hibernation.\n */\n capabilities?: ClientCapabilities;\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 MCPClientElicitationHandler = (\n request: ElicitRequest,\n serverId: string\n) => Promise<ElicitResult>;\n\nexport type MCPClientElicitationHandlers = {\n form?: MCPClientElicitationHandler;\n url?: MCPClientElicitationHandler;\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 private _elicitationHandlers?: MCPClientElicitationHandlers;\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 /**\n * Scope the manager-level elicitation handler to a single connection.\n * Returns undefined when no handler is configured so the connection keeps\n * its default throwing behavior.\n */\n private scopedElicitationHandlers(\n serverId: string\n ): MCPElicitationHandlers | undefined {\n const handlers = this._elicitationHandlers;\n if (!handlers) return undefined;\n\n const form = handlers.form;\n const url = handlers.url;\n return {\n form: form ? (request) => form(request, serverId) : undefined,\n url: url ? (request) => url(request, serverId) : undefined\n };\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 /**\n * Rename a server's id, in-place, across every place the id is used as a\n * key. Used to JIT-migrate servers that were originally registered under an\n * auto-generated nanoid to a caller-supplied stable id (see\n * `Agent.addMcpServer`'s `{ id }` option).\n *\n * Migrates:\n * - the `cf_agents_mcp_servers` row (primary key)\n * - the in-memory `mcpConnections` map key\n * - the connection disposables map key\n * - the attached `authProvider.serverId`, if any\n * - OAuth-related storage keys under `/{clientName}/{oldId}/...`\n *\n * Safe to call when no OAuth keys exist (RPC / bearer-token HTTP servers).\n * If `oldId === newId` this is a no-op. If a row already exists under\n * `newId`, throws — the caller is expected to have verified uniqueness.\n *\n * @internal Exposed for `Agent.addMcpServer` JIT-migration.\n */\n async migrateServerId(\n oldId: string,\n newId: string,\n clientName: string\n ): Promise<void> {\n if (oldId === newId) return;\n\n const existing = this.sql<MCPServerRow>(\n \"SELECT id FROM cf_agents_mcp_servers WHERE id = ?\",\n oldId\n );\n if (existing.length === 0) {\n // Nothing in storage to rename; just rename the in-memory connection if any.\n this._renameInMemoryConnection(oldId, newId);\n return;\n }\n\n const collision = this.sql<MCPServerRow>(\n \"SELECT id FROM cf_agents_mcp_servers WHERE id = ?\",\n newId\n );\n if (collision.length > 0) {\n throw new Error(\n `Cannot migrate MCP server id \"${oldId}\" → \"${newId}\": new id is already in use.`\n );\n }\n\n // 1. Storage: rename the SQL row.\n this.sql(\n \"UPDATE cf_agents_mcp_servers SET id = ? WHERE id = ?\",\n newId,\n oldId\n );\n\n // 2. OAuth-related storage keys. The DurableObjectOAuthClientProvider\n // keys everything under `/{clientName}/{serverId}/...`. Other servers\n // won't have any keys with this prefix, so the list will be empty and\n // this is a no-op for them.\n const oldPrefix = `/${clientName}/${oldId}/`;\n const newPrefix = `/${clientName}/${newId}/`;\n try {\n const keys = await this._storage.list({ prefix: oldPrefix });\n if (keys.size > 0) {\n const writes: Record<string, unknown> = {};\n const deletes: string[] = [];\n for (const [oldKey, value] of keys) {\n const newKey = newPrefix + oldKey.slice(oldPrefix.length);\n writes[newKey] = value;\n deletes.push(oldKey);\n }\n await this._storage.put(writes);\n await this._storage.delete(deletes);\n }\n } catch (error) {\n // Best-effort: storage rename failures shouldn't break the SQL-level\n // rename that already succeeded. Log and continue.\n console.warn(\n `[MCPClientManager] OAuth key migration ${oldPrefix} → ${newPrefix} failed:`,\n error\n );\n }\n\n // 3. In-memory connection + disposables + authProvider.\n this._renameInMemoryConnection(oldId, newId);\n\n this._onServerStateChanged.fire();\n }\n\n private _renameInMemoryConnection(oldId: string, newId: string): void {\n if (oldId === newId) return;\n\n const conn = this.mcpConnections[oldId];\n if (conn) {\n this.mcpConnections[newId] = conn;\n delete this.mcpConnections[oldId];\n const authProvider = conn.options.transport.authProvider;\n if (authProvider) {\n authProvider.serverId = newId;\n }\n // The elicitation handler closes over the server id it was created\n // with — rescope it so elicitation requests report the new id. With no\n // handlers there is nothing to rescope, and configuring would wipe the\n // connection's restored capability seed.\n const scoped = this.scopedElicitationHandlers(newId);\n if (scoped) {\n conn.configureElicitationHandlers(scoped);\n }\n }\n\n const disposables = this._connectionDisposables.get(oldId);\n if (disposables) {\n this._connectionDisposables.set(newId, disposables);\n this._connectionDisposables.delete(oldId);\n }\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 parsed server_options for a stored server, if any.\n */\n private getStoredServerOptions(\n serverId: string\n ): MCPServerOptions | 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 return JSON.parse(rows[0].server_options);\n }\n\n /**\n * Clear the capabilities persisted on a stored server row. Called once a\n * seeded connection's handshake completes (see `createConnection`): the\n * stamp is valid for one successful restore — sessions that configure\n * handlers re-stamp every row, so a deploy that stops configuring them\n * stops advertising stale modes after its first connected wake instead of\n * forever, while wakes that never handshake don't burn the stamp.\n */\n private clearStoredCapabilities(serverId: string): void {\n const rows = this.sql<MCPServerRow>(\n \"SELECT id, name, server_url, client_id, auth_url, callback_url, server_options FROM cf_agents_mcp_servers WHERE id = ?\",\n serverId\n );\n const row = rows[0];\n if (!row?.server_options) return;\n const options: MCPServerOptions = JSON.parse(row.server_options);\n if (!options.capabilities) return;\n options.capabilities = undefined;\n this.saveServerToStorage({\n ...row,\n server_options: JSON.stringify(options)\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 return this.getStoredServerOptions(serverId)?.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 private isAuthAcceptedConnection(conn: MCPClientConnection): boolean {\n return (\n conn.connectionState === MCPConnectionState.READY ||\n conn.connectionState === MCPConnectionState.CONNECTED ||\n conn.connectionState === MCPConnectionState.CONNECTING ||\n conn.connectionState === MCPConnectionState.DISCOVERING\n );\n }\n\n private oauthCallbackSuccess(\n serverId: string,\n conn: MCPClientConnection\n ): MCPOAuthCallbackResult {\n this.clearServerAuthUrl(serverId);\n conn.connectionError = null;\n return { serverId, authSuccess: true };\n }\n\n private async runWithCodeVerifierState<T>(\n authProvider: AgentMcpOAuthProvider,\n state: string,\n callback: () => Promise<T>\n ): Promise<T> {\n if (authProvider.runWithCodeVerifierState) {\n return authProvider.runWithCodeVerifierState(state, callback);\n }\n return callback();\n }\n\n private async consumeStaleOAuthState(\n serverId: string,\n authProvider: AgentMcpOAuthProvider,\n state: string\n ): Promise<void> {\n try {\n const stateValidation = await authProvider.checkState(state);\n if (!stateValidation.valid) {\n console.warn(\n `[MCPClientManager] Ignoring stale OAuth callback with invalid state for server \"${serverId}\": ${stateValidation.error ?? \"Invalid state\"}`\n );\n return;\n }\n await authProvider.consumeState(state);\n } catch (cleanupError) {\n console.warn(\n `[MCPClientManager] Failed to clean up stale OAuth callback state for server \"${serverId}\":`,\n cleanupError\n );\n }\n }\n\n private async completeAuthorizationAndCleanupVerifier(\n serverId: string,\n conn: MCPClientConnection,\n authProvider: AgentMcpOAuthProvider,\n state: string,\n code: string\n ): Promise<void> {\n await this.runWithCodeVerifierState(authProvider, state, async () => {\n let completeError: unknown;\n let cleanupError: unknown;\n\n try {\n await conn.completeAuthorization(code, { alreadyAccepted: true });\n } catch (error) {\n completeError = error;\n }\n\n try {\n await authProvider.deleteCodeVerifier();\n } catch (deleteError) {\n cleanupError = deleteError;\n }\n\n if (completeError) {\n if (cleanupError) {\n console.warn(\n `[MCPClientManager] Failed to clean up OAuth code verifier for server \"${serverId}\":`,\n cleanupError\n );\n }\n throw completeError;\n }\n\n if (cleanupError) {\n throw cleanupError;\n }\n });\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({\n bindingName,\n props,\n capabilities: this.advertisedHandlerCapabilities()\n })\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 // A caller-supplied jsonSchemaValidator is a live instance that can't\n // survive JSON serialization; drop whatever degraded value an older row\n // may hold so the connection falls back to the Worker-safe default.\n if (parsedOptions?.client) {\n delete parsedOptions.client.jsonSchemaValidator;\n }\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 // otherwise drop any existing connection and rebuild it.\n if (!options.reconnect?.oauthCode || !this.mcpConnections[id]) {\n delete this.mcpConnections[id];\n this.createConnection(id, url, {\n client: options.client,\n transport: options.transport ?? {}\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 const authProvider =\n this.mcpConnections[id].options.transport.authProvider;\n let completeError: unknown;\n try {\n await this.mcpConnections[id].completeAuthorization(\n options.reconnect.oauthCode\n );\n } catch (error) {\n completeError = error;\n }\n\n try {\n await authProvider?.deleteCodeVerifier();\n } catch (cleanupError) {\n console.warn(\n `[MCPClientManager] Failed to clean up OAuth code verifier for server \"${id}\":`,\n cleanupError\n );\n }\n\n if (completeError) {\n throw completeError;\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 // Stored servers re-advertise the capabilities persisted on their row\n // (see MCPServerOptions.capabilities); fresh registrations have no row\n // yet and rely on the handlers below. The stamp is read without\n // clearing so a wake that never handshakes (OAuth still pending, isolate\n // death before connect) doesn't burn it — it is consumed at first use,\n // once a seeded handshake completes.\n const capabilitySeed = this.getStoredServerOptions(id)?.capabilities;\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 elicitationHandlers: this.scopedElicitationHandlers(id),\n capabilitySeed\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 if (capabilitySeed) {\n const conn = this.mcpConnections[id];\n // Every handshake reports its success through this event, so it marks\n // the seed's first use. The burn only targets deploys that stopped\n // configuring handlers: a session with handlers configured owns the\n // row stamps (each configure call re-stamps them, and re-registering\n // a server writes a fresh one), and configureElicitationHandlers\n // drops the in-memory seed on live connections — either way a fresh\n // stamp meant for the next wake is never wiped here.\n const seedClear = conn.onObservabilityEvent((event) => {\n if (\n event.type !== \"mcp:client:connect\" ||\n event.payload.state !== MCPConnectionState.CONNECTED\n ) {\n return;\n }\n seedClear.dispose();\n if (this._elicitationHandlers || !conn.options.capabilitySeed) return;\n // migrateServerId may have renamed the row while the handshake was\n // in flight — clear under the connection's current id.\n const currentId = Object.keys(this.mcpConnections).find(\n (key) => this.mcpConnections[key] === conn\n );\n if (currentId) {\n this.clearStoredCapabilities(currentId);\n }\n });\n store.add(seedClear);\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, excluding live instances that can't survive JSON\n // serialization: authProvider is recreated during restore, and\n // jsonSchemaValidator falls back to the Worker-safe default.\n const { authProvider: _, ...transportWithoutAuth } =\n options.transport ?? {};\n const { jsonSchemaValidator: _validator, ...serializableClient } =\n options.client ?? {};\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: serializableClient,\n transport: transportWithoutAuth,\n retry: options.retry,\n capabilities: this.advertisedHandlerCapabilities()\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; state?: 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 state: state,\n valid: false,\n error: errorDescription || error\n };\n }\n\n if (!code) {\n return {\n serverId: serverId,\n state: state,\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 const conn = validation.serverId\n ? this.mcpConnections[validation.serverId]\n : undefined;\n if (validation.serverId && conn) {\n if (this.isAuthAcceptedConnection(conn)) {\n const authProvider = conn.options.transport.authProvider;\n if (validation.state && authProvider) {\n authProvider.serverId = validation.serverId;\n await this.consumeStaleOAuthState(\n validation.serverId,\n authProvider,\n validation.state\n );\n }\n return this.oauthCallbackSuccess(validation.serverId, conn);\n }\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 if (this.isAuthAcceptedConnection(conn)) {\n await this.consumeStaleOAuthState(serverId, authProvider, state);\n return this.oauthCallbackSuccess(serverId, conn);\n }\n throw new Error(stateValidation.error || \"Invalid state\");\n }\n\n // A stale popup can complete after another callback already exchanged tokens.\n // Treat it as success, but consume its state so it cannot be replayed.\n if (this.isAuthAcceptedConnection(conn)) {\n await this.consumeStaleOAuthState(serverId, authProvider, state);\n return this.oauthCallbackSuccess(serverId, conn);\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 conn.connectionState = MCPConnectionState.CONNECTING;\n await authProvider.consumeState(state);\n await this.completeAuthorizationAndCleanupVerifier(\n serverId,\n conn,\n authProvider,\n state,\n code\n );\n this.updateStoredSessionId(serverId, conn.sessionId);\n const result = this.oauthCallbackSuccess(serverId, conn);\n this._onServerStateChanged.fire();\n\n return result;\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 * Configure handling for server-initiated `elicitation/create` requests.\n *\n * The handler is held in memory only and applied to every MCP connection\n * created or restored by this manager. Call this before registering\n * connections when you want the initial MCP handshake to advertise\n * handler-driven form- and url-mode elicitation. Existing active connections\n * keep their negotiated capabilities until they reconnect.\n *\n * The advertised modes are persisted with each stored server, so\n * connections restored after hibernation re-advertise them at the handshake\n * even when this is called later in the wake-up (e.g. from onStart) — the\n * handlers attach to the live connections as soon as this runs.\n *\n * Pass undefined to clear the handler.\n *\n * @param handlers Elicitation handlers keyed by mode, each scoped with the server id that sent the request\n */\n configureElicitationHandlers(handlers?: MCPClientElicitationHandlers): void {\n this._elicitationHandlers =\n handlers && (handlers.form || handlers.url) ? handlers : undefined;\n this.persistAdvertisedCapabilities();\n for (const [id, connection] of Object.entries(this.mcpConnections)) {\n connection.configureElicitationHandlers(\n this.scopedElicitationHandlers(id)\n );\n }\n }\n\n /** Client capabilities advertised from the currently configured handlers. */\n private advertisedHandlerCapabilities(): ClientCapabilities | undefined {\n const elicitation = elicitationCapabilitiesFromHandlers(\n this._elicitationHandlers\n );\n return elicitation ? { elicitation } : undefined;\n }\n\n /**\n * Record the handler-derived capabilities on every stored server row so a\n * restore after hibernation re-advertises them before the handlers\n * themselves are reconfigured.\n */\n private persistAdvertisedCapabilities(): void {\n const capabilities = this.advertisedHandlerCapabilities();\n for (const server of this.getServersFromStorage()) {\n const options: MCPServerOptions = server.server_options\n ? JSON.parse(server.server_options)\n : {};\n if (\n JSON.stringify(options.capabilities) === JSON.stringify(capabilities)\n ) {\n continue;\n }\n options.capabilities = capabilities;\n this.saveServerToStorage({\n ...server,\n server_options: JSON.stringify(options)\n });\n }\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): MCPAIToolSet {\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, MCPAITool][] = [];\n for (const tool of getNamespacedData(connections, \"tools\")) {\n try {\n const toolKey = `tool_${tool.serverId.replace(/-/g, \"\")}_${tool.name}`;\n const title = tool.title ?? tool.annotations?.title;\n entries.push([\n toolKey,\n {\n description: tool.description,\n title,\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): MCPAIToolSet {\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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wire-types-nflOzNuU.js","names":[],"sources":["../src/chat/stream-accumulator.ts","../src/chat/broadcast-state.ts","../src/chat/wire-types.ts"],"sourcesContent":["/**\n * StreamAccumulator — unified chunk-to-message builder.\n *\n * Used by @cloudflare/ai-chat (server + client) and @cloudflare/think\n * to incrementally build a UIMessage from stream chunks. Wraps\n * applyChunkToParts and handles the metadata chunk types (start, finish,\n * message-metadata, error) that applyChunkToParts does not cover.\n *\n * The accumulator signals domain-specific concerns (early persistence,\n * cross-message tool updates) via ChunkAction returns — callers handle\n * these according to their context.\n */\n\nimport type { UIMessage } from \"ai\";\nimport { applyChunkToParts, type StreamChunkData } from \"./message-builder\";\n\nfunction asMetadata(value: unknown): Record<string, unknown> | undefined {\n if (value != null && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return undefined;\n}\n\nexport interface StreamAccumulatorOptions {\n messageId: string;\n continuation?: boolean;\n existingParts?: UIMessage[\"parts\"];\n existingMetadata?: Record<string, unknown>;\n}\n\nexport type ChunkAction =\n | {\n type: \"start\";\n messageId?: string;\n metadata?: Record<string, unknown>;\n }\n | {\n type: \"finish\";\n finishReason?: string;\n metadata?: Record<string, unknown>;\n }\n | { type: \"message-metadata\"; metadata: Record<string, unknown> }\n | { type: \"tool-approval-request\"; toolCallId: string }\n | {\n type: \"cross-message-tool-update\";\n updateType: \"output-available\" | \"output-error\";\n toolCallId: string;\n output?: unknown;\n errorText?: string;\n preliminary?: boolean;\n }\n | { type: \"error\"; error: string };\n\nexport interface ChunkResult {\n handled: boolean;\n action?: ChunkAction;\n}\n\nexport class StreamAccumulator {\n messageId: string;\n readonly parts: UIMessage[\"parts\"];\n metadata?: Record<string, unknown>;\n private _isContinuation: boolean;\n\n constructor(options: StreamAccumulatorOptions) {\n this.messageId = options.messageId;\n this._isContinuation = options.continuation ?? false;\n this.parts = options.existingParts ? [...options.existingParts] : [];\n this.metadata = options.existingMetadata\n ? { ...options.existingMetadata }\n : undefined;\n }\n\n applyChunk(chunk: StreamChunkData): ChunkResult {\n const handled = applyChunkToParts(this.parts, chunk);\n\n // Detect tool-approval-request for early persistence signaling\n if (chunk.type === \"tool-approval-request\" && chunk.toolCallId) {\n return {\n handled,\n action: { type: \"tool-approval-request\", toolCallId: chunk.toolCallId }\n };\n }\n\n // Detect cross-message tool output/error: applyChunkToParts returns true\n // for recognized types but silently does nothing when the toolCallId\n // doesn't exist in the current parts array.\n if (\n (chunk.type === \"tool-output-available\" ||\n chunk.type === \"tool-output-error\") &&\n chunk.toolCallId\n ) {\n const foundInParts = this.parts.some(\n (p) => \"toolCallId\" in p && p.toolCallId === chunk.toolCallId\n );\n if (!foundInParts) {\n return {\n handled,\n action: {\n type: \"cross-message-tool-update\",\n updateType:\n chunk.type === \"tool-output-available\"\n ? \"output-available\"\n : \"output-error\",\n toolCallId: chunk.toolCallId,\n output: chunk.output,\n errorText: chunk.errorText,\n preliminary: chunk.preliminary\n }\n };\n }\n }\n\n if (!handled) {\n switch (chunk.type) {\n case \"start\": {\n if (chunk.messageId != null && !this._isContinuation) {\n this.messageId = chunk.messageId;\n }\n const startMeta = asMetadata(chunk.messageMetadata);\n if (startMeta) {\n this.metadata = this.metadata\n ? { ...this.metadata, ...startMeta }\n : { ...startMeta };\n }\n return {\n handled: true,\n action: {\n type: \"start\",\n messageId: chunk.messageId,\n metadata: startMeta\n }\n };\n }\n case \"finish\": {\n const finishMeta = asMetadata(chunk.messageMetadata);\n if (finishMeta) {\n this.metadata = this.metadata\n ? { ...this.metadata, ...finishMeta }\n : { ...finishMeta };\n }\n const finishReason =\n \"finishReason\" in chunk\n ? (chunk.finishReason as string)\n : undefined;\n return {\n handled: true,\n action: {\n type: \"finish\",\n finishReason,\n metadata: finishMeta\n }\n };\n }\n case \"message-metadata\": {\n const msgMeta = asMetadata(chunk.messageMetadata);\n if (msgMeta) {\n this.metadata = this.metadata\n ? { ...this.metadata, ...msgMeta }\n : { ...msgMeta };\n }\n return {\n handled: true,\n action: {\n type: \"message-metadata\",\n metadata: msgMeta ?? {}\n }\n };\n }\n case \"finish-step\": {\n return { handled: true };\n }\n case \"error\": {\n return {\n handled: true,\n action: {\n type: \"error\",\n error: chunk.errorText ?? JSON.stringify(chunk)\n }\n };\n }\n }\n }\n\n return { handled };\n }\n\n /** Snapshot the current state as a UIMessage. */\n toMessage(): UIMessage {\n return {\n id: this.messageId,\n role: \"assistant\",\n parts: [...this.parts],\n ...(this.metadata != null && { metadata: this.metadata })\n } as UIMessage;\n }\n\n /**\n * Merge this accumulator's message into an existing message array.\n * Handles continuation (walk backward for last assistant), replacement\n * (update existing by messageId), or append (new message).\n */\n mergeInto(messages: UIMessage[]): UIMessage[] {\n let existingIdx = messages.findIndex((m) => m.id === this.messageId);\n\n if (existingIdx < 0 && this._isContinuation) {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === \"assistant\") {\n existingIdx = i;\n break;\n }\n }\n }\n\n const messageId =\n existingIdx >= 0 ? messages[existingIdx].id : this.messageId;\n\n const partialMessage: UIMessage = {\n id: messageId,\n role: \"assistant\",\n parts: [...this.parts],\n ...(this.metadata != null && { metadata: this.metadata })\n } as UIMessage;\n\n if (existingIdx >= 0) {\n const updated = [...messages];\n updated[existingIdx] = partialMessage;\n return updated;\n }\n return [...messages, partialMessage];\n }\n}\n","/**\n * Broadcast stream state machine.\n *\n * Manages the lifecycle of a StreamAccumulator for broadcast/resume\n * streams — the path where this client is *observing* a stream owned\n * by another tab or resumed after reconnect, rather than the transport-\n * owned path that feeds directly into useChat.\n *\n * The transition function is pure (no React, no WebSocket, no side\n * effects). Callers dispatch events and apply the returned state +\n * messagesUpdate. Side effects (sending ACKs, calling onData) stay\n * in the caller.\n */\n\nimport type { UIMessage } from \"ai\";\nimport { StreamAccumulator } from \"./stream-accumulator\";\nimport type { StreamChunkData } from \"./message-builder\";\n\n// ── State ──────────────────────────────────────────────────────────\n\nexport type BroadcastStreamState =\n | { status: \"idle\" }\n | {\n status: \"observing\";\n streamId: string;\n accumulator: StreamAccumulator;\n };\n\n// ── Events ─────────────────────────────────────────────────────────\n\nexport type BroadcastStreamEvent =\n | {\n type: \"response\";\n streamId: string;\n /** Fallback message ID for a new accumulator (ignored if one exists for this stream). */\n messageId: string;\n chunkData?: unknown;\n done?: boolean;\n error?: boolean;\n replay?: boolean;\n replayComplete?: boolean;\n continuation?: boolean;\n /** Required when continuation=true so the accumulator can pick up existing parts. */\n currentMessages?: UIMessage[];\n }\n | {\n type: \"resume-fallback\";\n streamId: string;\n messageId: string;\n }\n | { type: \"clear\" };\n\n// ── Result ─────────────────────────────────────────────────────────\n\nexport interface TransitionResult {\n state: BroadcastStreamState;\n messagesUpdate?: (prev: UIMessage[]) => UIMessage[];\n isStreaming: boolean;\n}\n\n// ── Transition ─────────────────────────────────────────────────────\n\nexport function transition(\n state: BroadcastStreamState,\n event: BroadcastStreamEvent\n): TransitionResult {\n switch (event.type) {\n case \"clear\":\n return { state: { status: \"idle\" }, isStreaming: false };\n\n case \"resume-fallback\": {\n const accumulator = new StreamAccumulator({\n messageId: event.messageId\n });\n return {\n state: {\n status: \"observing\",\n streamId: event.streamId,\n accumulator\n },\n isStreaming: true\n };\n }\n\n case \"response\": {\n let accumulator: StreamAccumulator;\n\n // A replayed `start` chunk means the server is re-sending the stream\n // buffer from chunk 0 (resume replay). Re-initialize the accumulator\n // instead of appending into an existing one: replaying into an\n // accumulator that already holds this stream's parts would duplicate\n // them (a second `text-start` unconditionally opens a second text\n // part — #1733). Re-initializing makes replay idempotent under any\n // number of replays, including a second replay triggered by a\n // duplicate STREAM_RESUMING → ACK cycle or a reconnect.\n const isReplayedStart =\n event.replay === true &&\n (event.chunkData as { type?: string } | null | undefined)?.type ===\n \"start\";\n\n if (\n state.status === \"idle\" ||\n state.streamId !== event.streamId ||\n isReplayedStart\n ) {\n let messageId = event.messageId;\n let existingParts: UIMessage[\"parts\"] | undefined;\n let existingMetadata: Record<string, unknown> | undefined;\n\n if (event.continuation && event.currentMessages) {\n for (let i = event.currentMessages.length - 1; i >= 0; i--) {\n if (event.currentMessages[i].role === \"assistant\") {\n messageId = event.currentMessages[i].id;\n existingParts = [...event.currentMessages[i].parts];\n if (event.currentMessages[i].metadata != null) {\n existingMetadata = {\n ...(event.currentMessages[i].metadata as Record<\n string,\n unknown\n >)\n };\n }\n break;\n }\n }\n }\n\n accumulator = new StreamAccumulator({\n messageId,\n continuation: event.continuation,\n existingParts,\n existingMetadata\n });\n } else {\n accumulator = state.accumulator;\n }\n\n if (event.chunkData) {\n accumulator.applyChunk(event.chunkData as StreamChunkData);\n }\n\n let messagesUpdate: ((prev: UIMessage[]) => UIMessage[]) | undefined;\n\n if (event.done) {\n messagesUpdate = (prev) => accumulator.mergeInto(prev);\n return {\n state: { status: \"idle\" },\n messagesUpdate,\n isStreaming: false\n };\n }\n\n if (event.chunkData && !event.replay) {\n messagesUpdate = (prev) => accumulator.mergeInto(prev);\n } else if (event.replayComplete) {\n messagesUpdate = (prev) => accumulator.mergeInto(prev);\n }\n\n return {\n state: {\n status: \"observing\",\n streamId: event.streamId,\n accumulator\n },\n messagesUpdate,\n isStreaming: true\n };\n }\n }\n}\n","import type { JSONSchema7, UIMessage } from \"ai\";\n\n/**\n * Enum for message types to improve type safety and maintainability\n */\nexport enum MessageType {\n CF_AGENT_CHAT_MESSAGES = \"cf_agent_chat_messages\",\n CF_AGENT_USE_CHAT_REQUEST = \"cf_agent_use_chat_request\",\n CF_AGENT_USE_CHAT_RESPONSE = \"cf_agent_use_chat_response\",\n CF_AGENT_CHAT_CLEAR = \"cf_agent_chat_clear\",\n CF_AGENT_CHAT_REQUEST_CANCEL = \"cf_agent_chat_request_cancel\",\n\n /** Sent by server when client connects and there's an active stream to resume */\n CF_AGENT_STREAM_RESUMING = \"cf_agent_stream_resuming\",\n /** Sent by client to acknowledge stream resuming notification and request chunks */\n CF_AGENT_STREAM_RESUME_ACK = \"cf_agent_stream_resume_ack\",\n /** Sent by client after message handler is ready, requesting stream resume check */\n CF_AGENT_STREAM_RESUME_REQUEST = \"cf_agent_stream_resume_request\",\n /** Sent by server when client requests resume but no active stream exists */\n CF_AGENT_STREAM_RESUME_NONE = \"cf_agent_stream_resume_none\",\n /**\n * Sent by server when a turn is accepted but its resumable stream has not\n * started yet (queued / debouncing / waiting on MCP / async setup). Tells a\n * reconnecting client to keep waiting rather than resolve its resume probe to\n * \"no stream\". Resolved by a later `CF_AGENT_STREAM_RESUMING` (stream started)\n * or `CF_AGENT_STREAM_RESUME_NONE` (settled without streaming). See #1784.\n */\n CF_AGENT_STREAM_PENDING = \"cf_agent_stream_pending\",\n\n /** Client sends tool result to server (for client-side tools) */\n CF_AGENT_TOOL_RESULT = \"cf_agent_tool_result\",\n /** Server notifies client that a message was updated (e.g., tool result applied) */\n CF_AGENT_MESSAGE_UPDATED = \"cf_agent_message_updated\",\n /** Client sends tool approval response to server (for tools with needsApproval) */\n CF_AGENT_TOOL_APPROVAL = \"cf_agent_tool_approval\",\n\n /**\n * Server→client progress hint: a durable chat turn is being recovered\n * (interrupted by a deploy/eviction or a stream-stall watchdog abort and now\n * resuming). Sent when a recovery continuation is scheduled and cleared on\n * every terminal outcome. (`@cloudflare/think` also replays it on connect;\n * `@cloudflare/ai-chat` broadcasts the live signal only — see #1645.)\n * Backward-compatible — clients that don't understand it ignore it. See #1620.\n */\n CF_AGENT_CHAT_RECOVERING = \"cf_agent_chat_recovering\"\n}\n\n/**\n * Types of messages sent from the Agent to clients\n */\nexport type OutgoingMessage<ChatMessage extends UIMessage = UIMessage> =\n | {\n /** Indicates this message is a command to clear chat history */\n type: MessageType.CF_AGENT_CHAT_CLEAR;\n }\n | {\n /** Indicates this message contains updated chat messages */\n type: MessageType.CF_AGENT_CHAT_MESSAGES;\n /** Array of chat messages */\n messages: readonly ChatMessage[];\n }\n | {\n /** Indicates this message is a response to a chat request */\n type: MessageType.CF_AGENT_USE_CHAT_RESPONSE;\n /** Unique ID of the request this response corresponds to */\n id: string;\n /** Content body of the response */\n body: string;\n /** Whether this is the final chunk of the response */\n done: boolean;\n /** Whether this response contains an error */\n error?: boolean;\n /** Whether this is a continuation (append to last assistant message) */\n continuation?: boolean;\n /** Whether this chunk is being replayed from storage (stream resumption) */\n replay?: boolean;\n /** Signals that replay of stored chunks is complete (stream is still active) */\n replayComplete?: boolean;\n }\n | {\n /** Indicates the server is resuming an active stream */\n type: MessageType.CF_AGENT_STREAM_RESUMING;\n /** The request ID of the stream being resumed */\n id: string;\n }\n | {\n /** Server notifies client that a message was updated (e.g., tool result applied) */\n type: MessageType.CF_AGENT_MESSAGE_UPDATED;\n /** The updated message */\n message: ChatMessage;\n }\n | {\n /** Server responds to resume request when no active stream exists */\n type: MessageType.CF_AGENT_STREAM_RESUME_NONE;\n }\n | {\n /**\n * Server signals an accepted turn whose resumable stream has not started\n * yet — the client should keep waiting for `STREAM_RESUMING` (or a later\n * `STREAM_RESUME_NONE`) rather than give up. See #1784.\n */\n type: MessageType.CF_AGENT_STREAM_PENDING;\n /** The accepted request id, when known. */\n id?: string;\n }\n | {\n /**\n * Progress hint: a durable chat turn is being recovered (`recovering:\n * true`) or recovery has resolved (`recovering: false`). Purely advisory;\n * a client renders a \"recovering…\" indicator while true.\n */\n type: MessageType.CF_AGENT_CHAT_RECOVERING;\n /** Whether recovery is in progress (true) or has resolved (false). */\n recovering: boolean;\n /** The recovery-root request id of the turn being recovered, if known. */\n id?: string;\n };\n\n/**\n * Types of messages sent from clients to the Agent\n */\nexport type IncomingMessage<ChatMessage extends UIMessage = UIMessage> =\n | {\n /** Indicates this message is a command to clear chat history */\n type: MessageType.CF_AGENT_CHAT_CLEAR;\n }\n | {\n /** Indicates this message is a request to the chat API */\n type: MessageType.CF_AGENT_USE_CHAT_REQUEST;\n /** Unique ID for this request */\n id: string;\n /** Request initialization options */\n init: Pick<\n RequestInit,\n | \"method\"\n | \"keepalive\"\n | \"headers\"\n | \"body\"\n | \"redirect\"\n | \"integrity\"\n | \"credentials\"\n | \"mode\"\n | \"referrer\"\n | \"referrerPolicy\"\n | \"window\"\n >;\n }\n | {\n /** Indicates this message contains updated chat messages */\n type: MessageType.CF_AGENT_CHAT_MESSAGES;\n /** Array of chat messages */\n messages: ChatMessage[];\n }\n | {\n /** Indicates the user wants to stop generation of this message */\n type: MessageType.CF_AGENT_CHAT_REQUEST_CANCEL;\n id: string;\n }\n | {\n /** Client acknowledges stream resuming notification and is ready to receive chunks */\n type: MessageType.CF_AGENT_STREAM_RESUME_ACK;\n /** The request ID of the stream being resumed */\n id: string;\n }\n | {\n /** Client requests stream resume check after message handler is registered */\n type: MessageType.CF_AGENT_STREAM_RESUME_REQUEST;\n }\n | {\n /** Client sends tool result to server (for client-side tools) */\n type: MessageType.CF_AGENT_TOOL_RESULT;\n /** The tool call ID this result is for */\n toolCallId: string;\n /** The name of the tool */\n toolName: string;\n /** The output from the tool execution */\n output: unknown;\n /** Override the tool part state (e.g. \"output-error\" for custom denial) */\n state?: \"output-available\" | \"output-error\";\n /** Error message when state is \"output-error\" */\n errorText?: string;\n /** Whether server should auto-continue the conversation after applying result */\n autoContinue?: boolean;\n /** Client tool schemas for continuation (client is source of truth) */\n clientTools?: Array<{\n name: string;\n description?: string;\n parameters?: JSONSchema7;\n }>;\n }\n | {\n /** Client sends tool approval response to server (for tools with needsApproval) */\n type: MessageType.CF_AGENT_TOOL_APPROVAL;\n /** The tool call ID this approval is for */\n toolCallId: string;\n /** Whether the tool execution was approved */\n approved: boolean;\n /** Whether server should auto-continue the conversation after applying approval */\n autoContinue?: boolean;\n };\n"],"mappings":"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"}
|