Files
reverse-engineering-anything/src/cli/mcpBuiltinCommands.ts
T
morluto 69630c0362 fix(cli)!: own MCP client registration boundaries (#10102)
* fix(cli)!: own MCP client registration boundaries

* fix(cli): bind registration to native connection facts

* test(cli): isolate registration failure and fixture controls

* test(cli): distinguish native syntax and launcher alignment

* fix(cli): preserve registration selection and cancellation

* fix(cli): honor Goose and Zed native settings paths

* fix(cli): honor registration output controls

* fix(cli): reject disabled Junie global locations
2026-11-11 09:05:21 -05:00

129 lines
4.4 KiB
TypeScript

import { mcpRegistrationOptionsSchema } from "../contracts/mcpRegistration.js";
import {
adaptCliOutputArgumentsForIncur,
declaredSensitiveValues,
splitIncurBuiltinArguments,
} from "../cliBuiltinArguments.js";
import {
parseCliOutputArguments,
renderCliDocument,
type CliOutputFormat,
} from "../cliOutput.js";
import { redactExplicitText } from "../domain/explicitSensitiveValues.js";
import type { CliInstance } from "./types.js";
type ServeOptions = NonNullable<Parameters<CliInstance["serve"]>[1]>;
/** Named MCP commands shared by completion and unknown-command guidance. */
export const MCP_SUBCOMMANDS = new Map([
[
"add",
{
description: "Register as MCP server",
alias: { command: "c" },
options: mcpRegistrationOptionsSchema,
},
],
["doctor", { description: "Validate MCP server startup and tool listing" }],
]);
/** Incur 0.6.1 builtins that it serves before its `mcp` subcommand branch. */
const PRECEDING_BUILTIN_FLAGS: ReadonlySet<string> = new Set([
"--incur-update-check",
"--update",
"--llms",
"--llms-full",
"--mcp",
]);
// zsh expands a word that starts with `=` to an executable path.
const PLAIN_SHELL_WORD = /^(?!=)[\w./+=:,-]+$/u;
/**
* Incur 0.6.1 rejects an unknown `mcp` subcommand in TOON regardless of the
* selected format. Report the same envelope in the explicitly selected format;
* without one, Incur's TOON or human rendering is already the selected output.
* The arguments are the caller's normalized ones, before REA adapts output
* flags for Incur, so the suggested command keeps the caller's selection.
*/
export const reportUnknownMcpSubcommand = (
name: string,
arguments_: readonly string[],
options: ServeOptions,
): boolean => {
const { command, commandIndices } = splitIncurBuiltinArguments(arguments_);
const [entry, subcommand] = command;
// Incur receives JSON for REA's delimited formats and no `--columns`.
const incurArguments = adaptCliOutputArgumentsForIncur(arguments_);
const incurOutputValues = new Set(
splitIncurBuiltinArguments(incurArguments).outputValues.map(
({ index }) => index,
),
);
if (
entry !== "mcp" ||
// Incur shows group help for an empty token and owns flag-shaped globals.
!subcommand ||
subcommand.startsWith("-") ||
MCP_SUBCOMMANDS.has(subcommand) ||
// Incur serves these builtins before its `mcp` subcommand branch. An
// output flag's value is data, even when it is spelled like a builtin.
incurArguments.some(
(token, index) =>
!incurOutputValues.has(index) && PRECEDING_BUILTIN_FLAGS.has(token),
)
)
return false;
const output = parseCliOutputArguments(incurArguments);
// Invalid output controls keep Incur's own parse diagnostic.
if (!output.formatExplicit || !output.validation.ok) return false;
const sensitive = declaredSensitiveValues(command);
const text = (value: string): string => redactExplicitText(value, sensitive);
const suggestion = [...MCP_SUBCOMMANDS.keys()].find((candidate) =>
candidate.startsWith(subcommand.toLowerCase()),
);
const corrected = arguments_.map((token, index) =>
index === commandIndices[1] ? suggestion : token,
);
// Joined tokens replay only when each one is a plain word in every shell;
// quoting is shell-specific, and a redacted value cannot be replayed.
const replayable =
suggestion !== undefined &&
corrected.every(
(token) =>
token !== undefined &&
PLAIN_SHELL_WORD.test(token) &&
text(token) === token,
);
const commands = [
...(replayable ? [{ command: `${name} ${corrected.join(" ")}` }] : []),
{
command: text(`${name} mcp --help`),
description: text("see all available commands"),
},
];
const document = {
code: "COMMAND_NOT_FOUND",
message: text(
`'${subcommand}' is not a command for '${name} mcp'.${suggestion === undefined ? "" : ` Did you mean '${suggestion}'?`}`,
),
cta: {
description: text(
commands.length === 1 ? "Suggested command:" : "Suggested commands:",
),
commands,
},
};
(options.stdout ?? ((value: string) => process.stdout.write(value)))(
// REA's document renderer owns JSON layout and Markdown escaping.
renderCliDocument(
document,
output.format as CliOutputFormat,
process.stdout.isTTY === true,
),
);
(options.exit ?? ((code: number) => process.exit(code)))(1);
return true;
};