fix(codex): support mcp add --url form, guard specs/URLs, cover --env
This commit is contained in:
@@ -62,6 +62,45 @@ describe("codex-cli argument validation", () => {
|
||||
assert.doesNotMatch("--force", cli.NAME_RE);
|
||||
assert.doesNotMatch("../../etc", cli.NAME_RE);
|
||||
});
|
||||
|
||||
it("accepts <name> and <name>@<marketplace> plugin specs only", () => {
|
||||
assert.equal(cli.assertPluginSpec("agenthub"), "agenthub");
|
||||
assert.equal(cli.assertPluginSpec("agenthub@sg"), "agenthub@sg");
|
||||
for (const bad of ["../agenthub", "agenthub agenthub", "@sg", "-agenthub", "a/b"]) {
|
||||
assert.throws(() => cli.assertPluginSpec(bad), undefined, `accepted ${bad}`);
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts only http(s) URLs", () => {
|
||||
assert.equal(cli.assertUrl("https://example.com/mp.git", "url"), "https://example.com/mp.git");
|
||||
for (const bad of ["ssh://example.com/mp.git", "not a url", "file:///tmp/x"]) {
|
||||
assert.throws(() => cli.assertUrl(bad, "url"), /http\(s\)/, `accepted ${bad}`);
|
||||
}
|
||||
// rejected earlier, by the shared leading-dash guard, not the URL check
|
||||
assert.throws(() => cli.assertUrl("--force", "url"), /may not start with/);
|
||||
});
|
||||
|
||||
it("rejects mcpAdd with neither or both of url/command", async () => {
|
||||
await assert.rejects(() => cli.mcpAdd({ name: "docs" }), /requires either/);
|
||||
await assert.rejects(
|
||||
() => cli.mcpAdd({ name: "docs", url: "https://x", command: "node" }),
|
||||
/either url or command, not both/
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects env together with url — --env is invalid on the url form", async () => {
|
||||
await assert.rejects(
|
||||
() => cli.mcpAdd({ name: "docs", url: "https://x", env: { A: "1" } }),
|
||||
/stdio-only/
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects an env key that is not a valid identifier", async () => {
|
||||
await assert.rejects(
|
||||
() => cli.mcpAdd({ name: "docs", command: "node", env: { "1bad": "1" } }),
|
||||
/identifier/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("codex-cli argv construction", () => {
|
||||
@@ -75,6 +114,54 @@ describe("codex-cli argv construction", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("puts --env flags between NAME and -- for a stdio server", async () => {
|
||||
const restore = stubCodex();
|
||||
try {
|
||||
const result = await cli.mcpAdd({
|
||||
name: "docs",
|
||||
command: "node",
|
||||
args: ["server.js"],
|
||||
env: { API_KEY: "secret", MODE: "prod" },
|
||||
});
|
||||
assert.deepEqual(argvOf(result), [
|
||||
"mcp",
|
||||
"add",
|
||||
"docs",
|
||||
"--env",
|
||||
"API_KEY=secret",
|
||||
"--env",
|
||||
"MODE=prod",
|
||||
"--",
|
||||
"node",
|
||||
"server.js",
|
||||
]);
|
||||
} finally {
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
it("builds the --url form with an optional --bearer-token-env-var", async () => {
|
||||
const restore = stubCodex();
|
||||
try {
|
||||
const result = await cli.mcpAdd({
|
||||
name: "docs",
|
||||
url: "https://example.com/mcp",
|
||||
bearerTokenEnvVar: "DOCS_TOKEN",
|
||||
});
|
||||
assert.deepEqual(argvOf(result), [
|
||||
"mcp",
|
||||
"add",
|
||||
"docs",
|
||||
"--bearer-token-env-var",
|
||||
"DOCS_TOKEN",
|
||||
"--url",
|
||||
"https://example.com/mcp",
|
||||
]);
|
||||
} finally {
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
it("builds mcp list / remove argvs", async () => {
|
||||
const restore = stubCodex();
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user