import type { IdeAdapter, IdeId } from '../ide/ide-types.js'; import { type IdeMcpInstallProfile } from '../ide/ide-mcp-install-types.js'; /** One process the plan wants run, as command + args. Never a joined string. */ export interface McpInstallCommand { readonly command: string; readonly args: readonly string[]; } /** What one operation is for, so a report can say which step did what. */ export type McpInstallOperation = 'remove' | 'add'; export interface McpInstallStep { readonly operation: McpInstallOperation; /** The scope this step acts on; the target scope for an `add`. */ readonly scope: string; readonly command: McpInstallCommand; } /** What a runner says happened. `detail` is surfaced verbatim when it did not. */ export interface McpInstallOutcome { readonly ok: boolean; readonly detail: string; /** * `false` only when the harness NEVER RENDERED A VERDICT — the deadline killed * it, or it never launched. A non-zero exit answered ("no such registration"), * so an absent value reads as `true`, as does every outcome that predates this * field. */ readonly answered?: boolean; } /** The seam: a test supplies one, the CLI supplies the spawning one below. */ export type McpInstallRunner = (command: McpInstallCommand) => McpInstallOutcome; export interface McpInstallReport { readonly serverName: string; /** Scopes where the removal reported success — i.e. there was something to remove. */ readonly removed: readonly string[]; /** Scopes where the removal reported failure — normally "nothing was there". */ readonly absent: readonly string[]; /** * Scopes whose removal never reached a verdict — the harness timed out or never * launched — so it is neither `removed` nor honestly `absent`. Optional because * the install sweep tolerates a failed removal (it is about to write) and never * populates it; the uninstall sweep always does. */ readonly unknown?: readonly string[]; readonly added: boolean; readonly ok: boolean; readonly detail: string; } /** * The argv that launches THIS installation's read-only MCP server. * * An interpreter and an entry path, in array form: the same shape `mcp serve` * spawns and the same one the read-only executor uses. A bare CLI name would be * a `.cmd` shim on some platforms, and picking a spelling per platform is the * branch this tree forbids — resolving the pair needs none. */ export declare function resolveMcpServerArgv(): readonly string[]; /** The plan: one removal per declared scope, then the addition. Order is the contract. */ export declare function buildMcpInstallPlan(profile: IdeMcpInstallProfile, serverArgv: readonly string[]): readonly McpInstallStep[]; /** Every removal, then the addition. A failed removal is a fact, not a failure. */ export declare function runMcpInstallPlan(profile: IdeMcpInstallProfile, serverArgv: readonly string[], runner: McpInstallRunner): McpInstallReport; /** * The removal sweep on its own — what `uninstall` is, and all that it is. * * `exit 0` removed it, `exit != 0` answered "no" (absent, unchanged) — but a run the * deadline killed, or one that never launched, ANSWERED NOTHING, and calling that * "nothing was there" is the lie this sweep used to tell. Those scopes are `unknown`, * and `ok` is false whenever any scope is unknown: with a scope the harness never * spoke for, the removal is unconfirmed and the state is not known. */ export declare function runMcpUninstallPlan(profile: IdeMcpInstallProfile, serverArgv: readonly string[], runner: McpInstallRunner): McpInstallReport; /** * One adapter's outcome. `skipped` is a FIRST-CLASS result and not an error: the * install's contract with an adapter that declares no registration entry is * silence, and a caller that has to distinguish "did nothing" from * "failed" needs the distinction to be in the value, not in a log line. */ export type AdapterMcpInstallResult = { readonly ideId: IdeId; readonly skipped: true; readonly reason: string; } | { readonly ideId: IdeId; readonly skipped: false; readonly report: McpInstallReport; }; export declare const NO_MCP_INSTALL_REASON = "adapter declares no mcpInstall profile"; /** Register this adapter's harness, or report that there is nothing to register. */ export declare function installMcpForAdapter(adapter: IdeAdapter, serverArgv: readonly string[], runner: McpInstallRunner): AdapterMcpInstallResult; /** The same, for the removal side. */ export declare function uninstallMcpForAdapter(adapter: IdeAdapter, serverArgv: readonly string[], runner: McpInstallRunner): AdapterMcpInstallResult; /** * The production runner and its deadline live in `mcp-install-runner.ts` — split * out only to keep this engine within the file-size cap. They are re-exported, so * the boundary is invisible: every caller still imports them from here. */ export { MCP_INSTALL_TIMEOUT_MS, spawnMcpInstallCommand, spawnMcpInstallRunner } from './mcp-install-runner.js';