/** * Probe an upstream MCP server: connect, `initialize`, page through `tools/list`, disconnect * (DES-MCP-TOOLS-001 §8 `POST /mcp/servers/preview` and `/test`). * * - Bounded: the whole probe has one budget ({@link MCP_PROBE_TIMEOUT_MS}, 45 s). A stdio server * gets all of it to answer `initialize` (a first start right after `npm ci` can take well over * 10 s on a loaded host, j6-cert 2026-10-27); an http server that is not listening yet is tried * again with a bounded backoff inside the same budget. A server that never answers is closed and * the probe fails with words that name the server's start, never a seat or a CLI (crew B6). * - Hardened env for stdio: the SDK's safe inherited set (HOME, LOGNAME, PATH, SHELL, TERM, USER on * POSIX) plus, when the server has auth, the ONE variable the secret is injected into. Nothing * else of the daemon's env (tokens, `WICKED_*`) reaches the upstream. * - Scrubbed: the resolved secret is removed from everything the probe returns — tool * descriptions and schemas, server info, and an error's message and stderr tail — so an * upstream that echoes its credential cannot carry it into a response (D-2). */ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import type { McpAuthConfig, McpRestMapping, McpToolAnnotations, McpUpstreamKind } from '../core/types.js'; import { type RestEgress } from './rest.js'; export declare const MCP_PROBE_TIMEOUT_MS = 45000; /** The waits between http connection attempts while the server is not listening yet. */ export declare const MCP_PROBE_RETRY_DELAYS_MS: readonly number[]; /** What crew needs to reach an upstream. Secret values are passed separately, never stored here. */ export interface McpUpstreamConfig { name: string; kind: McpUpstreamKind; command: string | null; args: string[]; url: string | null; auth: McpAuthConfig | null; /** `rest` only (slice S5a): where the OpenAPI document is fetched from. */ openapiUrl?: string | null; /** `rest` only: the OpenAPI document as pasted (instead of `openapiUrl`). */ openapi?: Record | null; /** `rest` only: the operations to wrap; `null`/absent = all. */ operations?: string[] | null; } export interface ProbedTool { name: string; description: string | null; inputSchema: Record | null; outputSchema: Record | null; annotations: McpToolAnnotations | null; /** A `rest` tool's request mapping; absent/`null` for an MCP server's tool. */ rest?: McpRestMapping | null; } export type ProbeResult = { ok: true; serverInfo: { name: string; version: string; } | null; tools: ProbedTool[]; skipped?: string[]; egress?: RestEgress; } | { ok: false; error: string; }; export type Prober = (config: McpUpstreamConfig, secret: string | null) => Promise; /** Keep only the annotation fields the spec defines, with the types it defines. */ export declare function normalizeAnnotations(raw: unknown): McpToolAnnotations | null; /** * The transport to one upstream, with the hardened env (stdio) or the auth header (http). Shared by * the probe and the broker's call path (`invoke.ts`), so both reach an upstream the same way. */ export declare function transportFor(config: McpUpstreamConfig, secret: string | null): { transport: Transport; stderrTail: () => string; }; /** Knobs of {@link probeMcpServerWithin}; tests shorten them. */ export interface ProbeBudget { budgetMs: number; retryDelaysMs: readonly number[]; sleep: (ms: number) => Promise; now: () => number; } /** The probe's words when the budget ran out: the server's start, not a seat or a CLI. */ export declare function stillStartingMessage(budgetMs: number, kind: McpUpstreamKind): string; /** The real prober. Every string it returns has `secret` scrubbed out. */ export declare const probeMcpServer: Prober; export declare function probeMcpServerWithin(config: McpUpstreamConfig, secret: string | null, budget: ProbeBudget): Promise; //# sourceMappingURL=probe.d.ts.map