// Construction drafts keep each owner contract's fields while allowing conditional assignment. type Mutable = { -readonly [Field in keyof Value]: Value[Field] }; type LocalServerDefinition = Mutable>; type RemoteServerDefinition = Mutable>; type OAuthServerAuth = Mutable< Extract, { type: "oauth" }> >; type AuthCommand = Mutable>; type TestCommand = Mutable>; type LogsCommand = Mutable>; type LogoutOptions = Mutable>; type CommandExecution = Mutable; /** JSON data returned by a command adapter and rendered by the shared command runner. */ export type McpCommandJsonValue = | null | boolean | number | string | readonly McpCommandJsonValue[] | { readonly [key: string]: McpCommandJsonValue }; /** Commands available through Pi's `/mcp` surface, in help order. */ export const MCP_COMMAND_NAMES = [ "list", "add", "remove", "enable", "disable", "auth", "logout", "test", "status", "reconnect", "prompt", "subscribe", "unsubscribe", "logs", "help", ] as const; /** One command name accepted by the MCP command parser. */ export type McpCommandName = (typeof MCP_COMMAND_NAMES)[number]; /** Value options valid only for local stdio Server Definitions. */ export const MCP_ADD_LOCAL_VALUE_OPTIONS = ["cwd", "environment"] as const; /** OAuth credential options accepted by remote Server Definitions. */ export const MCP_ADD_OAUTH_VALUE_OPTIONS = [ "client-id", "client-secret", "redirect-uri", "scope", ] as const; /** Value options valid only for remote HTTP or SSE Server Definitions. */ export const MCP_ADD_REMOTE_VALUE_OPTIONS = [ "auth", ...MCP_ADD_OAUTH_VALUE_OPTIONS, "header", "token", ] as const; /** Option names accepted by the MCP command parser, without leading dashes. */ export const MCP_COMMAND_OPTIONS = { add: { flags: ["local"], values: [...MCP_ADD_LOCAL_VALUE_OPTIONS, ...MCP_ADD_REMOTE_VALUE_OPTIONS, "transport"], }, auth: { flags: ["no-open"], values: ["callback", "code", "state"] }, disable: { flags: ["local"], values: [] }, enable: { flags: ["local"], values: [] }, help: { flags: [], values: [] }, list: { flags: ["json"], values: [] }, logout: { flags: ["all", "force"], values: [] }, logs: { flags: [], values: [] }, prompt: { flags: [], values: ["arg"] }, reconnect: { flags: [], values: [] }, remove: { flags: ["local", "logout"], values: [] }, status: { flags: [], values: [] }, subscribe: { flags: [], values: [] }, test: { flags: ["all", "json"], values: [] }, unsubscribe: { flags: [], values: [] }, } as const satisfies Record< McpCommandName, { readonly flags: readonly string[]; readonly values: readonly string[] } >; /** Persistent and offline commands available through the standalone executable. */ export const MCP_STANDALONE_COMMAND_NAMES = MCP_COMMAND_NAMES.slice(0, 8); /** Command entrypoint that owns parsing and adapter execution. */ export type McpCommandSurface = "runtime" | "standalone"; /** Stable categories used for command results and process exit codes. */ export type McpCommandExitCategory = | "success" | "usage" | "settings" | "authentication" | "connection" | "runtime"; /** Settings layer targeted by a mutating command. */ export type McpCommandSettingsScope = "global" | "project"; /** Enabled Server Definition accepted by the `add` command. */ export type McpAddServerDefinition = | { readonly args: readonly string[]; readonly command: string; readonly cwd?: string; readonly enabled: true; readonly environment: Readonly>; readonly transport: "stdio"; } | { readonly auth?: | { readonly type: "none" } | { readonly token: string; readonly type: "bearer" } | { readonly clientId?: string; readonly clientSecret?: string; readonly redirectUri?: string; readonly scopes: readonly string[]; readonly type: "oauth"; }; readonly enabled: true; readonly headers: Readonly>; readonly transport: "http" | "sse"; readonly url: string; }; /** Parsed command and its normalized options. */ export type McpCommand = | { readonly json: boolean; readonly kind: "list" } | { readonly definition: McpAddServerDefinition; readonly kind: "add"; readonly name: string; readonly scope: McpCommandSettingsScope; } | { readonly kind: "remove"; readonly logout: boolean; readonly name: string; readonly scope: McpCommandSettingsScope; } | { readonly kind: "enable"; readonly name: string; readonly scope: McpCommandSettingsScope } | { readonly kind: "disable"; readonly name: string; readonly scope: McpCommandSettingsScope } | { readonly callback?: string; readonly code?: string; readonly kind: "auth"; readonly noOpen: boolean; readonly server: string; readonly state?: string; } | { readonly all: boolean; readonly force: boolean; readonly kind: "logout"; readonly server?: string; } | { readonly all: boolean; readonly json: boolean; readonly kind: "test"; readonly server?: string; } | { readonly includeHelp: boolean; readonly kind: "status" } | { readonly kind: "reconnect"; readonly server: string } | { readonly arguments: Readonly>; readonly kind: "prompt"; readonly prompt: string; readonly server: string; } | { readonly kind: "subscribe"; readonly server: string; readonly uri: string } | { readonly kind: "unsubscribe"; readonly server: string; readonly uri: string } | { readonly kind: "logs"; readonly server?: string } | { readonly kind: "help" }; /** Usage failure returned when command tokens cannot be parsed. */ export interface McpCommandParseFailure { readonly category: "usage"; readonly message: string; readonly ok: false; readonly usage: string; } /** Successful parsed command or a usage failure. */ export type McpCommandParseResult = | { readonly command: McpCommand; readonly ok: true } | McpCommandParseFailure; /** Successful adapter outcome and optional JSON payload. */ export interface McpCommandAdapterSuccess { readonly data?: McpCommandJsonValue; readonly message: string; readonly ok: true; } /** Adapter outcome for settings, authentication, connection, or runtime failure. */ export interface McpCommandAdapterFailure { readonly category: Exclude; readonly message: string; readonly ok: false; } /** Result returned by a command adapter. */ export type McpCommandAdapterResult = McpCommandAdapterSuccess | McpCommandAdapterFailure; /** Options for one command variant, excluding its discriminant. */ export type McpCommandOptions = Omit< Extract, "kind" >; /** Adapter implementations shared by runtime and standalone command surfaces. */ export interface McpCommandAdapters { auth: { authenticate(options: McpCommandOptions<"auth">): Promise; logout(options: McpCommandOptions<"logout">): Promise; }; live?: McpLiveCommandAdapter | undefined; settings: { add(options: McpCommandOptions<"add">): Promise; disable(options: McpCommandOptions<"disable">): Promise; enable(options: McpCommandOptions<"enable">): Promise; list(): Promise; remove(options: McpCommandOptions<"remove">): Promise; }; test: { test(options: McpCommandOptions<"test">): Promise; }; } /** Runtime-only adapter for live MCP Host operations. */ export interface McpLiveCommandAdapter { connectInBackground(server: string): void; disconnect(server: string): Promise; logs(options: McpCommandOptions<"logs">): Promise; prompt(options: McpCommandOptions<"prompt">): Promise; reconnect(server: string): Promise; status(): Promise; subscribe(options: McpCommandOptions<"subscribe">): Promise; unsubscribe(options: McpCommandOptions<"unsubscribe">): Promise; } /** Rendered command outcome with its process exit code. */ export interface McpCommandExecutionResult { readonly category: McpCommandExitCategory; readonly data?: McpCommandJsonValue; readonly exitCode: number; readonly ok: boolean; readonly output: string; } const EXIT_CODES = { authentication: 4, connection: 5, runtime: 6, settings: 3, success: 0, usage: 2, } as const satisfies Record; const GENERAL_USAGE = `Usage: pi-mcp [options] Commands: ${MCP_STANDALONE_COMMAND_NAMES.join(", ")}`; const RUNTIME_HELP = `Commands: ${MCP_COMMAND_NAMES.join(", ")}`; const COMMAND_USAGE = { add: "Usage: pi-mcp add [-l|--local] [options]\n pi-mcp add [-l|--local] [options] -- [args...]", auth: "Usage: pi-mcp auth [--no-open] [--callback URL | --code CODE --state STATE]", disable: "Usage: pi-mcp disable [-l|--local] ", enable: "Usage: pi-mcp enable [-l|--local] ", list: "Usage: pi-mcp list [--json]", logout: "Usage: pi-mcp logout | --all --force", logs: "Usage: /mcp logs [server]", help: "Usage: /mcp help", prompt: "Usage: /mcp prompt [--arg NAME=VALUE]...", reconnect: "Usage: /mcp reconnect ", remove: "Usage: pi-mcp remove [-l|--local] [--logout] ", status: "Usage: /mcp status", subscribe: "Usage: /mcp subscribe ", test: "Usage: pi-mcp test | --all [--json]", unsubscribe: "Usage: /mcp unsubscribe ", } as const satisfies Record<(typeof MCP_COMMAND_NAMES)[number], string>; function usageFailure( command: (typeof MCP_COMMAND_NAMES)[number] | undefined, message: string, ): McpCommandParseFailure { return { category: "usage", message, ok: false, usage: command === undefined ? GENERAL_USAGE : COMMAND_USAGE[command], }; } /** Tokenized command options, including an unfinished completion value when requested. */ export interface McpCommandScannedOptions { /** Normalized flag names present before the stdio delimiter. */ readonly flags: ReadonlySet; /** Value option awaiting the current completion token. */ readonly pendingValue?: string; /** Non-option tokens present before the stdio delimiter. */ readonly positionals: readonly string[]; /** Unparsed stdio command tokens after `--`, or `undefined` when no delimiter exists. */ readonly tail: readonly string[] | undefined; /** Normalized value-option names and their supplied values. */ readonly values: ReadonlyMap; } /** Failed command option scan before semantic validation. */ export interface McpCommandOptionScanFailure { /** Human-readable grammar failure. */ readonly message: string; /** Discriminant for a failed scan. */ readonly ok: false; } /** Successful command option scan before semantic validation. */ export interface McpCommandOptionScanSuccess { /** Discriminant for a successful scan. */ readonly ok: true; /** Parsed option structure. */ readonly options: McpCommandScannedOptions; } /** Result of scanning command options before command-specific semantic validation. */ export type McpCommandOptionScanResult = McpCommandOptionScanFailure | McpCommandOptionScanSuccess; function normalizeOptionName(rawName: string): string { switch (rawName) { case "-l": case "--local": return "local"; case "--env": return "environment"; default: return rawName.replace(/^--/, ""); } } /** Scan MCP command options in strict parser mode or tolerant completion mode. */ export function scanMcpCommandOptions( tokens: readonly string[], command: McpCommandName, mode: "completion" | "strict", ): McpCommandOptionScanResult { const flags = new Set(); const positionals: string[] = []; const values = new Map(); const accepted = MCP_COMMAND_OPTIONS[command]; const flagNames = new Set(accepted.flags); const valueNames = new Set(accepted.values); for (let index = 0; index < tokens.length; index += 1) { const token = tokens[index]; if (token === undefined) continue; if (token === "--") { if (command !== "add") return { message: "unexpected -- delimiter", ok: false }; return { ok: true, options: { flags, positionals, tail: tokens.slice(index + 1), values } }; } if (!token.startsWith("-")) { positionals.push(token); continue; } const equalsIndex = token.indexOf("="); const rawName = equalsIndex < 0 ? token : token.slice(0, equalsIndex); const name = normalizeOptionName(rawName); if (flagNames.has(name)) { if (equalsIndex >= 0) return { message: `option ${rawName} does not accept a value`, ok: false }; flags.add(name); continue; } if (!valueNames.has(name)) return { message: `unknown option ${rawName}`, ok: false }; const value = equalsIndex < 0 ? tokens[index + 1] : token.slice(equalsIndex + 1); if (value === undefined || (equalsIndex < 0 && value.startsWith("--"))) { return mode === "completion" && value === undefined ? { ok: true, options: { flags, pendingValue: name, positionals, tail: undefined, values }, } : { message: `option ${rawName} requires a value`, ok: false }; } if (equalsIndex < 0) index += 1; const existing = values.get(name) ?? []; values.set(name, [...existing, value]); } return { ok: true, options: { flags, positionals, tail: undefined, values } }; } function parseOptions( tokens: readonly string[], command: McpCommandName, ): McpCommandScannedOptions | string { const result = scanMcpCommandOptions(tokens, command, "strict"); return result.ok ? result.options : result.message; } function oneValue(options: McpCommandScannedOptions, name: string): string | undefined { return options.values.get(name)?.at(-1); } function parseAssignments( values: readonly string[], kind: "environment" | "header" | "argument", ): Record | string { const result: Record = {}; for (const value of values) { let split = value.indexOf("="); if (split < 0 && kind === "header") split = value.indexOf(":"); if (split <= 0) return `${kind} must use NAME=VALUE`; const key = value.slice(0, split).trim(); const rawItem = value.slice(split + 1); const item = kind === "header" ? rawItem.trim() : rawItem; if (key.length === 0) return `${kind} name must not be empty`; result[key] = item; } return result; } function parseScope(options: McpCommandScannedOptions): McpCommandSettingsScope { return options.flags.has("local") ? "project" : "global"; } function isHttpUrl(value: string): boolean { try { const url = new URL(value); return url.protocol === "http:" || url.protocol === "https:"; } catch { return false; } } /** Authentication mode inferred from `add` options. */ export type McpAddAuthenticationMode = "bearer" | "none" | "oauth" | undefined; /** Invalid combination of MCP `add` authentication options. */ export interface McpAddAuthenticationFailure { /** Parser-compatible explanation of the incompatible options. */ readonly message: string; /** Discriminant for incompatible authentication options. */ readonly ok: false; } /** Compatible MCP `add` authentication options. */ export interface McpAddAuthenticationSuccess { /** Discriminant for compatible authentication options. */ readonly ok: true; /** Explicit or inferred authentication mode. */ readonly type: McpAddAuthenticationMode; } /** Compatibility result for MCP `add` authentication options. */ export type McpAddAuthenticationResult = McpAddAuthenticationFailure | McpAddAuthenticationSuccess; /** Classify MCP `add` authentication options for strict parsing or partial completion. */ export function classifyMcpAddAuthentication( options: McpCommandScannedOptions, mode: "completion" | "strict", ): McpAddAuthenticationResult { const configuredType = oneValue(options, "auth"); const token = oneValue(options, "token"); const oauth = MCP_ADD_OAUTH_VALUE_OPTIONS.some((name) => options.values.has(name)); const type = configuredType ?? (token !== undefined ? "bearer" : oauth ? "oauth" : undefined); if (type === undefined) return { ok: true, type }; if (type === "none") { return token === undefined && !oauth ? { ok: true, type } : { message: "auth type none cannot include credential options", ok: false }; } if (type === "bearer") { if (mode === "strict" && (token === undefined || token.length === 0)) return { message: "bearer auth requires --token", ok: false }; if (oauth) return { message: "bearer auth cannot include OAuth options", ok: false }; return { ok: true, type }; } if (type === "oauth") { return token === undefined ? { ok: true, type } : { message: "OAuth auth cannot include --token", ok: false }; } return { message: "--auth must be none, bearer, or oauth", ok: false }; } /** Local, remote, undecided, or incompatible MCP `add` transport form. */ export type McpAddTransportMode = "both" | "invalid" | "local" | "remote"; /** Classify local and remote MCP `add` option signals without performing I/O. */ export function classifyMcpAddTransportMode( options: McpCommandScannedOptions, ): McpAddTransportMode { const transport = oneValue(options, "transport"); if ( transport !== undefined && transport !== "http" && transport !== "sse" && transport !== "stdio" ) return "invalid"; if (options.positionals.length > 2) return "invalid"; const local = options.tail !== undefined || transport === "stdio" || MCP_ADD_LOCAL_VALUE_OPTIONS.some((name) => options.values.has(name)); const remote = options.positionals.length > 1 || transport === "http" || transport === "sse" || MCP_ADD_REMOTE_VALUE_OPTIONS.some((name) => options.values.has(name)); return local && remote ? "invalid" : local ? "local" : remote ? "remote" : "both"; } function parseRemoteAuth( options: McpCommandScannedOptions, ): RemoteServerDefinition["auth"] | string { const token = oneValue(options, "token"); const clientId = oneValue(options, "client-id"); const clientSecret = oneValue(options, "client-secret"); const redirectUri = oneValue(options, "redirect-uri"); const scopes = options.values.get("scope") ?? []; const compatibility = classifyMcpAddAuthentication(options, "strict"); if (!compatibility.ok) return compatibility.message; const type = compatibility.type; if (type === undefined) return undefined; if (type === "none") return { type: "none" }; if (type === "bearer") { if (token === undefined) return "bearer auth requires --token"; return { token, type: "bearer" }; } const auth: OAuthServerAuth = { scopes: [...scopes], type: "oauth" }; if (clientId !== undefined) auth.clientId = clientId; if (clientSecret !== undefined) auth.clientSecret = clientSecret; if (redirectUri !== undefined) auth.redirectUri = redirectUri; return auth; } function parseAdd(args: readonly string[]): McpCommandParseResult { const options = parseOptions(args, "add"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The parser's closed result union uses strings for usage failures and objects for validated options. if (typeof options === "string") return usageFailure("add", options); const name = options.positionals[0]; if (name === undefined || name.length === 0) return usageFailure("add", "server name is required"); const transportMode = classifyMcpAddTransportMode(options); if (options.tail !== undefined) { if (options.positionals.length !== 1) return usageFailure("add", "a local add cannot also include a URL"); const command = options.tail[0]; if (command === undefined || command.length === 0) return usageFailure("add", "local command is required after --"); for (const remoteOption of MCP_ADD_REMOTE_VALUE_OPTIONS) { if (options.values.has(remoteOption)) return usageFailure("add", `local add cannot include --${remoteOption}`); } const transport = oneValue(options, "transport"); if (transport !== undefined && transportMode !== "local") { return usageFailure("add", "local transport must be stdio"); } const environment = parseAssignments(options.values.get("environment") ?? [], "environment"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Assignment parsing returns either a usage error string or the validated string map. if (typeof environment === "string") return usageFailure("add", environment); const cwd = oneValue(options, "cwd"); const definition: LocalServerDefinition = { args: options.tail.slice(1), command, enabled: true, environment, transport: "stdio", }; if (cwd !== undefined) definition.cwd = cwd; return { command: { definition, kind: "add", name, scope: parseScope(options), }, ok: true, }; } if (options.positionals.length !== 2) return usageFailure("add", "remote add requires a name and URL"); if (MCP_ADD_LOCAL_VALUE_OPTIONS.some((name) => options.values.has(name))) { return usageFailure("add", "remote add cannot include local process options"); } const url = options.positionals[1] ?? ""; if (!isHttpUrl(url)) return usageFailure("add", "remote URL must be absolute HTTP or HTTPS"); const transport = oneValue(options, "transport") ?? "http"; if (transportMode !== "remote" || (transport !== "http" && transport !== "sse")) return usageFailure("add", "remote transport must be http or sse"); const headers = parseAssignments(options.values.get("header") ?? [], "header"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Assignment parsing returns either a usage error string or the validated string map. if (typeof headers === "string") return usageFailure("add", headers); const auth = parseRemoteAuth(options); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Authentication parsing returns a usage error string, an owned auth definition, or absence. if (typeof auth === "string") return usageFailure("add", auth); const definition: RemoteServerDefinition = { enabled: true, headers, transport, url, }; if (auth !== undefined) definition.auth = auth; return { command: { definition, kind: "add", name, scope: parseScope(options), }, ok: true, }; } function parseScopedServer( kind: "remove" | "enable" | "disable", args: readonly string[], ): McpCommandParseResult { const options = parseOptions(args, kind); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The parser's closed result union uses strings for usage failures and objects for validated options. if (typeof options === "string") return usageFailure(kind, options); if (options.positionals.length !== 1) return usageFailure(kind, "exactly one server name is required"); const name = options.positionals[0] ?? ""; const scope = parseScope(options); if (kind === "remove") { return { command: { kind, logout: options.flags.has("logout"), name, scope }, ok: true }; } return kind === "enable" ? { command: { kind, name, scope }, ok: true } : { command: { kind: "disable", name, scope }, ok: true }; } /** Parse one tokenized command for the standalone executable or Pi runtime. */ export function parseMcpCommand( args: readonly string[], surface: McpCommandSurface, ): McpCommandParseResult { const name = args[0]; if (name === undefined) { return surface === "runtime" ? { command: { includeHelp: true, kind: "status" }, ok: true } : usageFailure(undefined, "command is required"); } if (!MCP_COMMAND_NAMES.some((candidate) => candidate === name)) return usageFailure(undefined, `unknown command ${name}`); // SAFETY: The membership check immediately above proves `name` is an approved command name. const commandName = name as (typeof MCP_COMMAND_NAMES)[number]; if ( surface === "standalone" && !MCP_STANDALONE_COMMAND_NAMES.some((candidate) => candidate === commandName) ) { return usageFailure(undefined, `${commandName} is available only through /mcp`); } const rest = args.slice(1); if (commandName === "add") return parseAdd(rest); if (commandName === "remove" || commandName === "enable" || commandName === "disable") return parseScopedServer(commandName, rest); if (commandName === "list") { const options = parseOptions(rest, "list"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Select the usage-error arm before inspecting the validated options arm. if (typeof options === "string" || options.positionals.length > 0) return usageFailure( "list", // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Preserve the parser's specific usage error when it returned the string arm. typeof options === "string" ? options : "list accepts no arguments", ); if (surface === "runtime" && options.flags.has("json")) return usageFailure("list", "--json is standalone-only"); return { command: { json: options.flags.has("json"), kind: "list" }, ok: true }; } if (commandName === "auth") { const options = parseOptions(rest, "auth"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The parser's closed result union uses strings for usage failures and objects for validated options. if (typeof options === "string") return usageFailure("auth", options); if (options.positionals.length !== 1) return usageFailure( "auth", "exactly one server name is required; bare authorization codes are not accepted", ); const callback = oneValue(options, "callback"); const code = oneValue(options, "code"); const state = oneValue(options, "state"); if ((code === undefined) !== (state === undefined)) return usageFailure("auth", "--code and --state must be supplied together"); if (callback !== undefined && code !== undefined) return usageFailure("auth", "use either --callback or --code with --state"); const command: AuthCommand = { kind: "auth", noOpen: options.flags.has("no-open"), server: options.positionals[0] ?? "", }; if (callback !== undefined) command.callback = callback; if (code !== undefined) command.code = code; if (state !== undefined) command.state = state; return { command, ok: true }; } if (commandName === "logout") { const options = parseOptions(rest, "logout"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The parser's closed result union uses strings for usage failures and objects for validated options. if (typeof options === "string") return usageFailure("logout", options); const all = options.flags.has("all"); const force = options.flags.has("force"); if (all || force) { return all && force && options.positionals.length === 0 ? { command: { all: true, force: true, kind: "logout" }, ok: true } : usageFailure("logout", "auth-store reset requires exactly --all --force"); } if (options.positionals.length !== 1) return usageFailure("logout", "exactly one server name is required"); return { command: { all: false, force: false, kind: "logout", server: options.positionals[0] ?? "" }, ok: true, }; } if (commandName === "test") { const options = parseOptions(rest, "test"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The parser's closed result union uses strings for usage failures and objects for validated options. if (typeof options === "string") return usageFailure("test", options); if (surface === "runtime" && options.flags.has("json")) return usageFailure("test", "--json is standalone-only"); const all = options.flags.has("all"); if ((all && options.positionals.length > 0) || (!all && options.positionals.length !== 1)) return usageFailure("test", "select one server or explicit --all"); const command: TestCommand = { all, json: options.flags.has("json"), kind: "test", }; if (!all) command.server = options.positionals[0] ?? ""; return { command, ok: true }; } if (commandName === "status") { if (rest.length > 0) return usageFailure("status", "status accepts no arguments"); return { command: { includeHelp: false, kind: "status" }, ok: true }; } if (commandName === "help") { if (rest.length > 0) return usageFailure("help", "help accepts no arguments"); return { command: { kind: "help" }, ok: true }; } if (commandName === "reconnect") { if (rest.length !== 1) return usageFailure("reconnect", "exactly one server name is required"); return { command: { kind: "reconnect", server: rest[0] ?? "" }, ok: true }; } if (commandName === "prompt") { const options = parseOptions(rest, "prompt"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- The parser's closed result union uses strings for usage failures and objects for validated options. if (typeof options === "string") return usageFailure("prompt", options); if (options.positionals.length !== 2) return usageFailure("prompt", "server and prompt names are required"); const arguments_ = parseAssignments(options.values.get("arg") ?? [], "argument"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Assignment parsing returns either a usage error string or the validated string map. if (typeof arguments_ === "string") return usageFailure("prompt", arguments_); return { command: { arguments: arguments_, kind: "prompt", prompt: options.positionals[1] ?? "", server: options.positionals[0] ?? "", }, ok: true, }; } if (commandName === "subscribe" || commandName === "unsubscribe") { if (rest.length !== 2) return usageFailure(commandName, "server and resource URI are required"); return { command: { kind: commandName, server: rest[0] ?? "", uri: rest[1] ?? "" }, ok: true }; } const options = parseOptions(rest, "logs"); // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Select the usage-error arm before inspecting the validated options arm. if (typeof options === "string" || options.positionals.length > 1) return usageFailure( "logs", // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Preserve the parser's specific usage error when it returned the string arm. typeof options === "string" ? options : "logs accepts at most one server name", ); const command: LogsCommand = { kind: "logs" }; if (options.positionals[0] !== undefined) command.server = options.positionals[0]; return { command, ok: true }; } function adapterFailure( category: Exclude, message: string, usage?: string, ): McpCommandExecutionResult { return { category, exitCode: EXIT_CODES[category], ok: false, output: category === "usage" ? `Pi MCP: ${message}\n${usage ?? GENERAL_USAGE}\n` : `Pi MCP: ${message}\n`, }; } function successResult( result: McpCommandAdapterSuccess, json: boolean, suffix = "", ): McpCommandExecutionResult { const output = json ? `${JSON.stringify(result.data ?? { message: result.message }, undefined, 2)}\n` : `${result.message}${suffix}\n`; const execution: CommandExecution = { category: "success", exitCode: 0, ok: true, output, }; if (result.data !== undefined) execution.data = result.data; return execution; } function liveAdapter( adapters: McpCommandAdapters, ): McpLiveCommandAdapter | McpCommandExecutionResult { return adapters.live ?? adapterFailure("runtime", "live MCP Host is unavailable"); } /** Execute one parsed command through injected persistence, auth, test, and live-Host adapters. */ export async function executeMcpCommand( command: McpCommand, adapters: McpCommandAdapters, surface: McpCommandSurface = "standalone", ): Promise { try { let result: McpCommandAdapterResult; switch (command.kind) { case "list": result = await adapters.settings.list(); break; case "add": result = await adapters.settings.add({ definition: command.definition, name: command.name, scope: command.scope, }); if (result.ok && surface === "runtime") adapters.live?.connectInBackground(command.name); break; case "remove": result = await adapters.settings.remove({ logout: command.logout, name: command.name, scope: command.scope, }); if (result.ok && surface === "runtime") await adapters.live?.disconnect(command.name); break; case "enable": result = await adapters.settings.enable({ name: command.name, scope: command.scope }); if (result.ok && surface === "runtime") adapters.live?.connectInBackground(command.name); break; case "disable": result = await adapters.settings.disable({ name: command.name, scope: command.scope }); if (result.ok && surface === "runtime") await adapters.live?.disconnect(command.name); break; case "auth": { const options: Omit = { noOpen: command.noOpen, server: command.server, }; if (command.callback !== undefined) options.callback = command.callback; if (command.code !== undefined) options.code = command.code; if (command.state !== undefined) options.state = command.state; result = await adapters.auth.authenticate(options); break; } case "logout": { const options: LogoutOptions = { all: command.all, force: command.force, }; if (command.server !== undefined) options.server = command.server; result = await adapters.auth.logout(options); break; } case "test": { const options: Omit = { all: command.all, json: command.json, }; if (command.server !== undefined) options.server = command.server; result = await adapters.test.test(options); break; } case "status": { const live = liveAdapter(adapters); if ("exitCode" in live) return live; result = await live.status(); break; } case "reconnect": { const live = liveAdapter(adapters); if ("exitCode" in live) return live; result = await live.reconnect(command.server); break; } case "prompt": { const live = liveAdapter(adapters); if ("exitCode" in live) return live; result = await live.prompt({ arguments: command.arguments, prompt: command.prompt, server: command.server, }); break; } case "subscribe": { const live = liveAdapter(adapters); if ("exitCode" in live) return live; result = await live.subscribe({ server: command.server, uri: command.uri }); break; } case "unsubscribe": { const live = liveAdapter(adapters); if ("exitCode" in live) return live; result = await live.unsubscribe({ server: command.server, uri: command.uri }); break; } case "logs": { const live = liveAdapter(adapters); if ("exitCode" in live) return live; result = await live.logs(command.server === undefined ? {} : { server: command.server }); break; } case "help": return successResult({ message: RUNTIME_HELP, ok: true }, false); } if (!result.ok) return adapterFailure(result.category, result.message); const json = (command.kind === "list" || command.kind === "test") && command.json; const suffix = command.kind === "status" && command.includeHelp ? `\n\n${RUNTIME_HELP}` : ""; return successResult(result, json, suffix); } catch { return adapterFailure("runtime", "command failed unexpectedly"); } } /** Current token and replacement boundary for an unfinished MCP command. */ export interface McpCommandCompletionToken { /** Opening quote style to preserve when replacing the token. */ readonly quote: "'" | '"' | undefined; /** Zero-based source offset where replacement begins. */ readonly start: number; /** Decoded token value before the cursor. */ readonly value: string; } /** Tolerant tokenization of a complete or unfinished MCP command prefix. */ export interface McpCommandCompletionPrefix { /** Source text preserved before replacing the current token. */ readonly beforeCurrent: string; /** Decoded tokens completed before the current token. */ readonly completed: readonly string[]; /** Token being completed at the cursor. */ readonly current: McpCommandCompletionToken; /** Whether the source ends inside a token rather than after whitespace. */ readonly currentStarted: boolean; /** Whether the source ends before its opening quote closes. */ readonly incompleteQuote: boolean; } /** Tokenize an unfinished MCP command while retaining its current replacement boundary. */ export function tokenizeMcpCommandCompletionPrefix(line: string): McpCommandCompletionPrefix { const tokens: McpCommandCompletionToken[] = []; let current = ""; let quote: "'" | '"' | undefined; let quoteAtStart: "'" | '"' | undefined; let escaping = false; let start = line.length; let tokenStarted = false; for (let index = 0; index < line.length; index += 1) { const character = line[index] ?? ""; if (escaping) { current += character; escaping = false; } else if (character === "\\" && quote !== "'") { if (!tokenStarted) start = index; escaping = true; tokenStarted = true; } else if (quote !== undefined) { if (character === quote) quote = undefined; else current += character; } else if (character === "'" || character === '"') { if (!tokenStarted) { quoteAtStart = character; start = index; } quote = character; tokenStarted = true; } else if (/\s/u.test(character)) { if (tokenStarted) { tokens.push({ quote: quoteAtStart, start, value: current }); current = ""; quoteAtStart = undefined; tokenStarted = false; } } else { if (!tokenStarted) start = index; current += character; tokenStarted = true; } } if (escaping) current += "\\"; if (tokenStarted) tokens.push({ quote: quoteAtStart, start, value: current }); else tokens.push({ quote: undefined, start: line.length, value: "" }); const active = tokens.at(-1) ?? { quote: undefined, start: line.length, value: "" }; return { beforeCurrent: line.slice(0, active.start), completed: tokens.slice(0, -1).map(({ value }) => value), current: active, currentStarted: tokenStarted, incompleteQuote: quote !== undefined, }; } /** Split a `/mcp` argument string without invoking a shell or expanding variables. */ export function tokenizeMcpCommandLine(line: string): string[] { const prefix = tokenizeMcpCommandCompletionPrefix(line); if (prefix.incompleteQuote) throw new Error("MCP command has an unterminated quote"); return prefix.currentStarted ? [...prefix.completed, prefix.current.value] : [...prefix.completed]; } /** Parse and execute one pre-tokenized MCP command without reinterpreting argument contents. */ export async function runMcpCommandTokens( tokens: readonly string[], surface: McpCommandSurface, adapters: McpCommandAdapters, ): Promise { const parsed = parseMcpCommand(tokens, surface); if (!parsed.ok) return adapterFailure("usage", parsed.message, parsed.usage); return executeMcpCommand(parsed.command, adapters, surface); } /** Tokenize, parse, and execute one shared command line without throwing through its caller. */ export async function runMcpCommandLine( line: string, surface: McpCommandSurface, adapters: McpCommandAdapters, ): Promise { try { return runMcpCommandTokens(tokenizeMcpCommandLine(line), surface, adapters); } catch { return adapterFailure("usage", "invalid quoting", GENERAL_USAGE); } }