/** * Opt-in MCP tool bridge (agent-loop-capability-port sprint 10). * * Exposes a single configured MCP server's tools as provider-agnostic * `ToolDef`s the agentic loop can call directly, alongside its own tool set. * Reuses the repo's existing SDK-based MCP client path (`src/mcp/external-client.ts`'s * `ExternalMcpServer` — spawn `{command,args}` -> `listTools` -> `callTool` -> * `stop`) rather than hand-rolling JSON-RPC: the SDK `Client` + * `StdioClientTransport` perform the initialize/tools-list/tools-call * handshake internally. * * Disabled by default: nothing in this module runs unless a caller * explicitly invokes `createMcpToolBridge` — and callers must only do so * after confirming `config.tools?.mcpBridge?.enabled === true` at the CALL * SITE, never at config parse time (see `config/schema.ts` * `ToolsSectionSchema`). This keeps `runAgenticLoop` itself hermetic (the * loop never owns MCP lifecycle) and preserves the hermetic claude-code / * fleet child paths (nonGoal #2 — this module is never wired there). * * Bridged tools are NEVER marked `readOnly` (unknown upstream side effects * => serial execution per ADR-2, nonGoal #3) and are namespaced with an * `mcp__` prefix so they never collide with the loop's own filesystem/bash * tools (Pattern E). */ import type { ToolDef } from "../../providers/types.js"; import type { ToolHandler } from "./handlers.js"; /** * Minimal MCP-client interface satisfied by the real SDK client AND test * stubs. Mirrors `McpServerLike` (src/vault/mcp-adapter.ts:31-37) so tests * never spawn a real process or import the SDK. */ export interface McpBridgeClientLike { start(): Promise; listTools(): Promise>; callTool(name: string, args: unknown): Promise; close(): Promise; } export interface McpToolBridge { tools: ToolDef[]; handlers: Map; close(): Promise; } export interface CreateMcpToolBridgeOpts { /** Injected for tests so no real process is ever spawned. Defaults to the real SDK client. */ clientFactory?: (server: { command: string; args: string[]; }) => McpBridgeClientLike; } /** * Start a configured MCP server, list its tools, and expose them as * `mcp__`-prefixed `ToolDef`s + `ToolHandler`s a caller can merge into a * loop's own tool set. Callers own the bridge's lifetime — construct ONLY * when `config.tools?.mcpBridge?.enabled === true` at the call site, and * close it (directly, or via `runWithMcpBridge`) when done. */ export declare function createMcpToolBridge(server: { command: string; args?: string[]; }, opts?: CreateMcpToolBridgeOpts): Promise; /** * Run `fn` with the bridge's tools available, closing the underlying MCP * connection in a `finally` regardless of how `fn` resolves/rejects. Keeps * `runAgenticLoop` itself hermetic — the loop never owns MCP lifecycle; a * consumer builds the bridge, merges its tools into the loop's params, runs * the loop inside `fn`, and this helper guarantees `close()` at loop end * (design decision (d) in the sprint 10 briefing). `runAgenticLoop` resolves * exactly once (its `finish()` single exit), so this `finally` always fires * after the loop is truly done. */ export declare function runWithMcpBridge(bridge: McpToolBridge, fn: () => Promise): Promise; //# sourceMappingURL=mcp-bridge.d.ts.map