Files
reverse-engineering-anything/tests/boundary/cli/mcpAddOptionValidation.test.ts
T
STDquantum 094ec5eb3f fix(cli): reject missing mcp add option values (#7996)
* fix(cli): reject mcp add options missing their values (#7609)

* fix(cli): reject mcp add values swallowed by the next flag (#7609)

* fix(cli): reject empty mcp add -c value (#7609)

* test(cli): verify rejected mcp add preserves settings

* chore: keep main formatting out of merge diff
2026-11-04 19:37:31 +08:00

92 lines
3.1 KiB
TypeScript

import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest("mcp add rejects valued options without a value", async ({ cli }) => {
const root = await createTestTempDirectory("rea-mcp-add-value-");
const home = join(root, "home");
const settingsPath = (selectedHome: string) =>
join(selectedHome, ".config", "amp", "settings.json");
const run = (arguments_: readonly string[], selectedHome = home) =>
cli.run({
arguments: [...arguments_, "--json"],
cwd: root,
environment: {
HOME: selectedHome,
USERPROFILE: selectedHome,
XDG_CONFIG_HOME: join(selectedHome, ".config"),
},
timeoutMs: 20_000,
});
for (const [flag, field] of [
["--agent", "agent"],
["--command", "command"],
["-c", "command"],
["-c=", "command"],
["--agent=", "agent"],
] as const) {
const result = await run(["mcp", "add", flag]);
expect(result.exitCode, result.stderr).toBe(1);
expect(result.json).toMatchObject({
code: "VALIDATION_ERROR",
fieldErrors: expect.arrayContaining([
expect.objectContaining({
code: "invalid_value",
missing: false,
path: field,
}),
]),
});
await expect(access(settingsPath(home))).rejects.toMatchObject({
code: "ENOENT",
});
}
// Incur would swallow the following flag as the option value; REA rejects it.
const swallowed = await run(["mcp", "add", "--command", "--agent", "amp"]);
expect(swallowed.exitCode, swallowed.stderr).toBe(1);
expect(swallowed.json).toMatchObject({
code: "VALIDATION_ERROR",
fieldErrors: expect.arrayContaining([
expect.objectContaining({
code: "invalid_value",
missing: false,
path: "command",
}),
]),
});
await expect(access(settingsPath(home))).rejects.toMatchObject({
code: "ENOENT",
});
const existingHome = join(root, "existing-home");
const existingSettingsPath = settingsPath(existingHome);
await mkdir(join(existingHome, ".config", "amp"), { recursive: true });
const existingSettings = '{"existing":true}\n';
await writeFile(existingSettingsPath, existingSettings);
const rejectedWithExistingSettings = await run(
["mcp", "add", "--agent"],
existingHome,
);
expect(
rejectedWithExistingSettings.exitCode,
rejectedWithExistingSettings.stderr,
).toBe(1);
expect(rejectedWithExistingSettings.json).toMatchObject({
code: "VALIDATION_ERROR",
});
expect(await readFile(existingSettingsPath, "utf8")).toBe(existingSettings);
// A registration with an explicit agent still writes the Amp registration.
const registered = await run(["mcp", "add", "--agent", "amp"]);
expect(registered.exitCode, registered.stderr).toBe(0);
const settings = JSON.parse(await readFile(settingsPath(home), "utf8"));
expect(settings["amp.mcpServers"].rea).toMatchObject({
command: expect.any(String),
});
});