Skip to content

Bug: --header is documented as repeatable but only the last one is sent #3

Description

@royalpinto007

What is wrong

--header is documented as repeatable, but only the last one survives.

src/cli.ts line 63, in usage():

  --header <k:v>         Extra header for http transport (repeatable)

parseArgs (src/cli.ts lines 23-43) stores flags in a flat Record<string, string | boolean> and assigns with flags[key] = next, so a second --header overwrites the first. collectHeaders (lines 103-115) then does:

const raw = flags["header"];
const values = Array.isArray(raw) ? raw : raw ? [raw] : [];

raw is typed string | boolean and can never be an array, so the Array.isArray branch is unreachable. The author clearly intended repeats to accumulate; the parser never produced the array shape the collector was written for.

So this fails silently:

mcp-audit http https://example.com/mcp --header "X-Tenant: acme" --header "X-Env: prod"

Only X-Env is sent. No warning, and the audit result differs from what the user asked for.

Why it matters

Real MCP deployments behind a gateway routinely need more than one header (a tenant id plus a trace header, or an API key plus a version pin). Getting a partial header set means you audit a different server surface than you think you are auditing, which is the worst kind of wrong answer for a security scanner. It also affects MCP040/MCP041 in src/rules/transport.ts, which reason about connection.authProvided.

Suggested fix

  1. In src/cli.ts, let parseArgs accumulate repeats. The least invasive version is to change the flag map value type to string | boolean | string[] and, on a second occurrence of the same key, promote the existing value to an array and push. Keep CliArgs.flags exported shape in sync.
  2. collectHeaders already handles the array case, so it should need no change once the parser produces arrays. Delete the now-correct-but-previously-dead comment if there is one.
  3. overlayFlags and the other flags[...] readers use typeof x === "string" guards, so confirm they still behave sensibly if a user repeats --fail-on or --config. Last-wins is a fine answer there, just make it deliberate.
  4. Add a test. There is no test/cli.test.ts today, so a small new file that exercises parseArgs and collectHeaders directly is the cheapest path. Cover: one header, two headers, a header value containing a colon (for example Authorization: Bearer a:b, which collectHeaders splits on the first colon at line 111 and should preserve).

Comment below if you want to take it. I usually reply within a day.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingclaimedSomeone is already working on this issuehelp wantedExtra attention is needed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions