← back to Agent Permission Config Check

This checker vs. reading the JSON by eye

Opening settings.json or an mcpServers block and skimming it before committing is a normal way to catch an obvious problem once, and it works fine for a two-server config checked right after you write it. It is a different job from confirming the config is still tight today, after every onboarding session or MCP server someone else has added since -- most of which nobody thinks to re-read, because they didn't add it themselves.

Where reading it by eye usually goes wrong

StepCommon gap by eyeWhat this checker does
Spotting a Bash allow-all ruleObvious in a two-line example; buried and easy to skim past among dozens of narrowly-scoped rule strings in a config that grew over months of onboarding.Flags a bare "Bash" or "Bash(*)" entry by rule, every time, regardless of how many other rules surround it.
Spotting an unscoped destructive commandRequires recognizing that Bash(rm) with no colon-scoped path is functionally unlimited -- easy to read as "the same shape as the other rules" without checking each one's scope.Checks every allow rule's scope and cross-references it against the deny list in the same pass.
Spotting a plain-http MCP urlA url that looks superficially fine (a real domain, a real path) is easy to approve without noticing it starts with http:// rather than https://.Checks the literal scheme of every server's url, every time.
Spotting a hardcoded token in env/headersObvious in a two-line example; much less obvious in a real server block where most values legitimately are literals (a model name, a log level) and the one live token blends in.Applies the same known-token-format and reference-pattern check to every env/headers value, every server, every time.
Re-checking after later, unrelated changesNothing prompts a re-read of a server block you didn't just add -- drift accumulates silently between the checks that do happen.Costs nothing to re-run; wiring the CLI into CI on every PR makes the re-check automatic rather than something to remember.

Common questions

What's wrong with just reading settings.json myself before committing it?

Nothing, for a short config checked once. It stops scaling the way these files actually grow: an mcpServers block that accumulates entries from several onboarding sessions means re-reading all of them, correctly, every time anyone adds or edits one — and nobody re-checks a server block they didn't just touch. This tool checks every allow/deny rule and every server entry the same way, every time you run it, in the time it takes to paste the file in.

Isn't a hardcoded API key obvious when you're looking at the file?

It is obvious in a two-line example. It is much less obvious in a real mcpServers block with several servers, each carrying a handful of env or header entries, most of which are legitimately literal (a model name, a port number, a log level) — the one entry that is a live token blends into the rest unless something specifically checks each value's shape. This tool applies the same known-token-format and reference-pattern checks to every value, every time, rather than relying on a reviewer's eye catching the one that matters.

Wouldn't code review catch a Bash allow-all rule?

Only if the reviewer already treats permission config with the same scrutiny as application code — and settings.json / .mcp.json are usually written once during setup, outside the normal review flow that catches a risky application-code change. Even when it is reviewed, "Bash" and "Bash(*)" read as ordinary configuration next to dozens of other rule strings unless the reviewer is specifically looking for the allow-all shape. This tool flags it by rule, every time, regardless of whether the reviewer already knows to look.

Is anything I check here uploaded anywhere?

No. The browser page parses the JSON you paste entirely in your own browser and never sends any of it anywhere — exactly like the rest of this tool. The CLI reads only local files, with no network call, ever.

Try it

node cli.mjs check --root .
node cli.mjs check --file .mcp.json --json

Exits 1 if any high/medium finding is present, so it works as a CI gate on the same PR that adds a server or widens a permission rule. See why these configs need a linter, and this checker's own named limits.