import { McpClient, type CallToolResult, type McpRequestOptions, type McpTransport } from "@earendil-works/pi-mcp"; import { type ElicitResponse, type ElicitUi } from "../client/elicitation.js"; import type { CombinerConnection } from "../client/types.js"; import type { ServerFilter } from "../client/config-ladder.js"; export type LogFn = (level: "info" | "warn" | "error", message: string) => void; export type ResolvedConnection = { /** Base URL including /mcp, WITHOUT any token path (e.g. http://127.0.0.1:9741/mcp). */ baseUrl: string; /** Env var holding the inbound bearer token (read at connect time), if any. */ bearerTokenEnv?: string; /** Explicit token already present in the configured URL path (user override). */ urlToken?: string; }; export type ConnectionState = "disconnected" | "connecting" | "connected" | "failed"; export type ToolSummary = { name: string; description?: string; inputSchema?: unknown; /** MCP tool annotations, carried straight through to pi.registerTool so pi's * permission extensions can classify calls. */ annotations?: ToolAnnotationsSummary; }; export type ToolAnnotationsSummary = { readOnlyHint?: boolean; destructiveHint?: boolean; idempotentHint?: boolean; openWorldHint?: boolean; }; export type PromptArgument = { name: string; description?: string; required?: boolean; }; export type PromptSummary = { name: string; title?: string; description?: string; arguments?: PromptArgument[]; }; export type ResourceSummary = { uri: string; name?: string; description?: string; mimeType?: string; _meta?: Record; }; export type ConnectionHooks = { onToolsChanged?: () => void; onStateChange?: (state: ConnectionState) => void; onWidgetUrl?: (url: string) => void; }; export type ElicitResult = ElicitResponse; /** Build the tokened endpoint URL: /mcp/ (identical to client/connection.ts). */ export declare function tokenedUrl(baseUrl: string, token: string): string; export declare class NativeCombinerConnection implements CombinerConnection { private conn; private readonly log; private elicitUi; private token; private client; /** Bumped by every reset: in-flight connects check it before publishing a client * (a connect that started before the reset must never win the publish race). */ private generation; private connecting; private tools; private toolsFetchedAt; private prompts; private promptsFetchedAt; private resources; private resourcesFetchedAt; private hooks; state: ConnectionState; lastError: string | undefined; /** Test seam: build the transport for one connect (defaults to * StreamableHttpTransport). Injection lets tests drive the connection over an * in-memory pair instead of HTTP. */ private readonly transportFactory; constructor(conn: ResolvedConnection, log: LogFn, opts?: { transportFactory?: (url: URL) => McpTransport; }); setHooks(hooks: ConnectionHooks): void; /** Effective grouping token for this session (explicit URL token wins). */ get sessionToken(): string | undefined; /** Bind the UI slice used by elicitation (called from session_start). */ bindUi(ui: ElicitUi): void; /** Swap connection inputs (base URL / bearer env); drops the client when changed. */ rebind(conn: ResolvedConnection): boolean; /** Set the grouping token for this session; resets any open connection. */ setToken(token: string): void; /** Drop the connection + caches (session reset / shutdown). Never throws. */ reset(reason: string): Promise; private effectiveToken; /** Connect (idempotent, single-flight). THE elicitation-relevant site: the * capability + request handler live HERE, on a client we own. */ ensureConnected(): Promise; /** One-shot stale-session retry shared by EVERY request method — a combiner * bounce invalidates the transport session, and the first operation to notice * can be any of them (the router's listTools lookup runs BEFORE callTool; * without this it surfaced raw McpSessionExpiredError and never reset the * client, wedging the connection until reload). * Classification is TYPE-based over pi-mcp's exported taxonomy; the message * regex remains only as a belt for raw transport failures pi-mcp does not wrap * (e.g. fetch failed). callTool passes retryStaleOnly: only * McpSessionExpiredError — which the server raises BEFORE executing anything — * may trigger an automatic re-run there. */ private withStaleRetry; /** tools/list, cached until invalidated (list_changed / reset / reconnect). * pi-mcp returns the tools array directly (pagination already resolved). */ listTools(force?: boolean): Promise; /** prompts/list, cached alongside tools. pi-mcp has no prompts convenience (yet); * the generic request() is the sanctioned path for server-initiated methods the * client doesn't model. */ listPrompts(force?: boolean): Promise; /** prompts/get through the combiner. */ getPrompt(name: string, args: Record): Promise; /** resources/list, cached alongside tools/prompts. * SAFETY: pi-mcp's Resource adds typed `_meta` variance the combiner surfaces as * Record — the shapes agree on the fields we consume * (uri/name/description/mimeType), so the cast narrows only metadata typing. */ listResources(force?: boolean): Promise; /** resources/read through the combiner. */ readResource(uri: string): Promise; /** callTool. Transport-level stale retry is STALE-ONLY (typed * McpSessionExpiredError): that error is raised by the server BEFORE it * executes the request, so one re-run is provably safe. Any other failure — * closed/timeout/"fetch failed" AFTER delivery — may correspond to a request * that already EXECUTED upstream; an automatic re-run could repeat a * destructive tool, so those surface as errors (with the recovery hints) and * the model/user decides whether to call again. * A server-marked error (CallToolResult.isError) is checked AFTER the transport * retry layer and THROWS, so pi records the call as a tool error — and crucially * the tool's own error TEXT can never reach the retry classification (a * "…not found" tool error must not look like transport staleness). * `options.signal` rides into the transport: cancellation cancels upstream work * and widget holds (notifications/cancelled). */ callTool(name: string, args: Record | undefined, options?: McpRequestOptions): Promise; controlOrigin(): string; uiUrlFor(resourceUri: string): string; private controlHeaders; applyFilter(filter: ServerFilter): Promise; health(): Promise; } //# sourceMappingURL=combiner-connection.d.ts.map