export type ToolDef = import("../types").ToolDef; /** * A server definition as found in an IDE/MCP config file. */ export type ServerDef = { command: string; args?: string[] | undefined; env?: Record | undefined; cwd?: string | undefined; }; /** * Raw tool descriptor as returned by an MCP server's tools/list. */ export type McpTool = { name: string; description?: string | undefined; inputSchema?: Record | undefined; /** * - Server-supplied behavioural HINTS (readOnlyHint, * destructiveHint, ...). Untrusted metadata from the server: never an authorization signal. */ annotations?: Record | undefined; }; /** * Per-server entry persisted in .mcp-bridge.json. */ export type BridgeServerEntry = { command: string; args: string[]; env?: Record | undefined; cwd?: string | undefined; /** * - tool name -> "allow" | "deny" */ tools: Record; }; /** * Persisted bridge config (.mcp-bridge.json). */ export type BridgeConfig = { /** * - ISO timestamp */ discovered: string; ttl: string; servers: Record; }; /** * A denied-tool descriptor surfaced to the LLM. */ export type DeniedTool = { server: string; tool: string; description: string; }; /** * JSON-RPC stdio client over a spawned MCP server. */ export type RpcClient = { rpc: (method: string, params?: object, timeoutMs?: number, label?: { server: string; tool: string; }) => Promise; notify: (method: string, params?: object) => void; child: import("node:child_process").ChildProcessWithoutNullStreams; stderr: string; }; /** * Create an MCP bridge. On first run, discovers MCP servers from IDE configs, * connects, lists tools, and writes .mcp-bridge.json with all tools set to "allow". * On subsequent runs, reads .mcp-bridge.json and respects allow/deny per tool. * Re-discovers when TTL expires (default: 24h). * * Returns BOTH surfaces (v0.9+): * - `tools` — bulk-loaded array of name-prefixed tools (small catalogs; * LLM sees them upfront). * - `metaTools` — [mcp_discover, mcp_invoke] LLM-callable pair (large catalogs; * LLM picks tools dynamically). Shares the same RPC connections. * * Wire one or the other into Loop's tool array; never both (the LLM would see * the same MCP tool twice). Pick by catalog size and token budget. * * @param {object} [opts] * @param {string} [opts.bridgePath] - Path to .mcp-bridge.json. Default: .mcp-bridge.json in cwd. * @param {string[]} [opts.configPaths] - Explicit config paths for discovery. When given, honored * verbatim. When omitted, only the trusted $HOME/IDE defaults are scanned (NOT `./.mcp.json`). * @param {boolean} [opts.includeProjectConfig=false] - Also scan the project-cwd `./.mcp.json` * during default discovery. Off by default: a project config in an untrusted repo can auto-spawn * arbitrary commands. Implied true when a `confirmServer` hook is present (it vets each command). * @param {Record} [opts.allow] - Signed allow-list `{ [serverName]: rawMcpToolNames[] }` * (raw MCP names, NOT the `server_tool` prefixed form; each list must be non-empty). Switches to an IN-MEMORY mode: never reads or * writes `.mcp-bridge.json` (no cache, no TTL; `bridgePath`/`refresh`/`servers` are ignored); server * definitions come only from `discoverServers(configPaths)`; only servers named here are spawned (still * vetted by `confirmServer`); each connects ONCE and exposes signed ∩ listed — every other tool is ABSENT * (not in `tools`, `metaTools` catalog or `systemContext`). Signed tools the server does not list (or whose * server was not found / failed / was rejected) come back in `missing`. `denied` is always `[]`, nothing is * written to stdout, and `mcp_invoke` on a listed-but-unsigned name throws `context.denied === true`. * Invalid shape throws `ValidationError` (type only, never values). * @param {string[]} [opts.servers] - Limit to these server names (ignored when `allow` is given). * @param {number} [opts.timeout=15000] - Per-server handshake timeout in ms (initialize + tools/list). * @param {number} [opts.callTimeout=120000] - Per-invocation timeout in ms for tools/call. Bounds a * server that accepts a tool call but never responds. Set 0 to disable (unbounded). * @param {boolean} [opts.refresh=false] - Force re-discovery regardless of TTL. * @param {(name: string, def: ServerDef) => boolean | Promise} [opts.confirmServer] * Vet each discovered server BEFORE its `command` is spawned. Connecting to an * MCP server runs its command. Return false to skip a server (its command is * never executed). A throw is treated as a deny (fail-closed). Default: every * discovered server is trusted — pass this to gate command execution. Presence * of this hook also opts default discovery into the project-cwd `./.mcp.json`, * since each command is then vetted regardless of source. * @returns {Promise<{tools: ToolDef[], metaTools?: ToolDef[], servers: string[], systemContext: string, denied: DeniedTool[], errors: Array<{server: string, error: string}>, missing?: Array<{server: string, tool: string}>, close: Function}>} * @when you want to auto-discover MCP servers and expose them as bareagent tools in one call — with a trust hook gating command execution, or a per-run signed `allow` list that exposes only the named tools * @fails a trust hook returning false skips a server (its command never runs) and a throw is fail-closed (deny); with `allow` it is in-memory only (no cache file read or written) and signed tools the server does not list land in `missing`, never a throw; returns tools plus a close() to shut servers down. * @example * const { tools, close } = await createMCPBridge(); * const loop = new Loop({ provider, tools }); */ export function createMCPBridge(opts?: { bridgePath?: string | undefined; configPaths?: string[] | undefined; includeProjectConfig?: boolean | undefined; allow?: Record | undefined; servers?: string[] | undefined; timeout?: number | undefined; callTimeout?: number | undefined; refresh?: boolean | undefined; confirmServer?: ((name: string, def: ServerDef) => boolean | Promise) | undefined; }): Promise<{ tools: ToolDef[]; metaTools?: ToolDef[]; servers: string[]; systemContext: string; denied: DeniedTool[]; errors: Array<{ server: string; error: string; }>; missing?: Array<{ server: string; tool: string; }>; close: Function; }>; /** * @param {string[]} [configPaths] - Explicit config paths. When given, honored verbatim (the * caller owns the choice). When omitted, the trusted $HOME/IDE defaults are scanned. * @param {{ includeProjectConfig?: boolean }} [opts] - When no explicit `configPaths` are given, * set `includeProjectConfig: true` to also scan `./.mcp.json`. Default false — see PROJECT_CONFIG_PATH. * @returns {Map} * @when you want to find configured MCP servers from the trusted $HOME/IDE config paths (or explicit paths) — discovery without invoking them * @fails honors explicit configPaths verbatim and scans trusted defaults otherwise; returns a Map and never executes a server command. * @example * const servers = discoverServers(); */ export function discoverServers(configPaths?: string[], { includeProjectConfig }?: { includeProjectConfig?: boolean; }): Map; /** * Build the LLM-callable meta-tool surface from a fully-connected bridge. * Shares the underlying tool array and RPC clients with the bulk surface — * one set of connections, one factory, two output forms. The user picks * `bridge.tools` (bulk) for small catalogs the LLM should see upfront, or * `bridge.metaTools` for large catalogs the LLM should discover on demand. * * Gov shape: when the LLM calls mcp_invoke, the action sent to gate.check * is `{ type: 'mcp_invoke', args: { name, args }, _ctx }` — bareguard sees * `mcp_invoke` as the type. To deny specific MCP tools, use bareguard's * `tools.denyArgPatterns: { mcp_invoke: [/"name":"linear_admin_.*"/] }` * or `content.denyPatterns` over the JSON-serialized form. The inner MCP * tool name doesn't travel as `action.type` — that's a deliberate v0.9 * trade for one consistent gate-check call per LLM tool invocation. * * @param {ToolDef[]} tools - The bulk-loaded, name-prefixed tools array. * @param {string} [discoveredAt] - ISO timestamp from .mcp-bridge.json. * @param {Map} [excluded] - Allow mode only: names the server * LISTS but the signed allow-list excludes. mcp_invoke refuses them with `context.denied === true`; * they never appear in the catalog. Omitted ⇒ unchanged behaviour. * @returns {ToolDef[]} [mcp_discover, mcp_invoke] * @when you want the two bulk MCP meta-tools (mcp_discover, mcp_invoke) instead of exposing every discovered tool individually — one gate-check per invocation * @fails returns [mcp_discover, mcp_invoke]; a tool name does not travel as action.type (a deliberate v0.9 trade for one gate-check per call). * @example * const metaTools = buildMetaTools(tools); */ export function buildMetaTools(tools: ToolDef[], discoveredAt?: string, excluded?: Map): ToolDef[]; /** * List one configured MCP server's tools WITHOUT building a bridge: discover, vet, connect, list * (following pagination), then always kill the child. Never reads or writes `.mcp-bridge.json` * and prints nothing to stdout. `annotations` are the server's own HINTS, passed through unmodified * (key absent when the server sent none) — never an authorization signal. * * Total wall-clock ceiling is DERIVED: 4 × `timeout` (initialize + three maximally slow pages) over * the connect + list, excluding `confirmServer` (caller code, possibly interactive). On trip the * child is killed and a TimeoutError naming the total deadline rejects. A SMALL `timeout` shrinks the * total proportionally (timeout 500 ⇒ 2s total), so a server with many pages near that speed can trip it. * A non-object `annotations` value from the server is treated as absent (dropped). * * @param {object} opts * @param {string} opts.server - Server name as it appears in the discovered configs. * @param {string[]} [opts.configPaths] - Explicit config paths; omitted ⇒ trusted $HOME/IDE defaults. * @param {(name: string, def: ServerDef) => boolean | Promise} [opts.confirmServer] - Same semantics as createMCPBridge: `=== true` proceeds, a throw denies. Implies includeProjectConfig. * @param {boolean} [opts.includeProjectConfig=false] - Also scan `./.mcp.json` during default discovery. * @param {number} [opts.timeout=15000] - Per-round-trip bound in ms (initialize and each tools/list page). * @returns {Promise}>>} * @when you want to list one MCP server's tools and their annotation hints before deciding what to allow — no bridge, no cache file, no tools kept alive * @fails throws (never returns []) when the server is not found, confirmServer declines or throws, the connect/handshake fails or times out, the total deadline trips, or the reply is malformed; the child is always reaped. * @example * const tools = await listServerTools({ server: 'files', configPaths: [cfg] }); */ export function listServerTools(opts: { server: string; configPaths?: string[] | undefined; confirmServer?: ((name: string, def: ServerDef) => boolean | Promise) | undefined; includeProjectConfig?: boolean | undefined; timeout?: number | undefined; }): Promise; }>>;