A security-hardened Model Context Protocol server that gives AI agents read-only access to CLI tools. Designed for auto-allow environments where the allowlist is the sole security boundary against prompt injection.
Most agent frameworks restrict tool access through prompt instructions or by curating which tools the model sees. These are soft boundaries — a prompt injection that convinces the agent to "use bash instead" or "call this unlisted function" bypasses them entirely. The restriction lives in the model's interpretation, not in code the model cannot influence.
This server moves the boundary to the execution layer. The allowlist is enforced at runtime by the MCP server process: if a command isn't explicitly permitted, the server rejects it before any process is spawned. No amount of creative prompting changes what execFile will execute. The agent can request anything — the server decides what actually runs.
The goal is to make auto-allow safe for read-only tools. Without enforcement at the server, auto-allowing an MCP tool is equivalent to giving the agent unrestricted shell access, because the tool's "read-only" contract is only as strong as the model's compliance.
- Allowlists, not denylists — every command and subcommand must be explicitly permitted
- No
cwdparameter — commands run in the server's working directory only, preventing agents from reading files in arbitrary directories execFile, no shell — prevents shell metacharacter injection (;,&&,|,$(),`) even when args contain untrusted input- Prefix flag matching —
rejectBlockedFlagsuses prefix matching to defeat CLI flag abbreviation (e.g., npm expands--regto--registry). Exact matching would be bypassable. --registryblocked — prevents SSRF / data exfiltration to attacker-controlled registries--ignore-scriptsinjected — defense-in-depth for npm to block lifecycle scripts.--no-ignore-scriptsand--ignore-scriptsare also blocked from user-provided args to prevent last-wins override.az devops invokeflag allowlist — unknown flags are rejected (allowlist, not blocklist), blocking abbreviation bypass- Resource limits — 10 s timeout, 2 MB maxBuffer to prevent runaway commands
- Windows support —
bash -c 'exec "$@"'wrapper preservesexecFileguarantees for shell utilities
| Command / flag | Reason |
|---|---|
chezmoi cat-config |
May leak encryption keys, GPG recipient IDs, or other sensitive configuration |
chezmoi data |
Leaks template variables that may contain secrets |
printenv / env |
Leaks environment variables (API keys, tokens) |
rg |
Redundant with host Grep tool; allows searching arbitrary paths |
cat, head, tail, bat, diff, delta |
Redundant with host Read tool; path args duplicate file-read surface area |
git diff --no-index |
Reads arbitrary files outside the repo |
git --output / diff -o |
Writes command output to a file |
gh variable get |
Returns plaintext values that may contain internal URLs, hostnames, or quasi-sensitive configuration |
Read-only shell utilities: basename, date, dirname, eza, file, jq, ls, pwd, readlink, realpath, stat, wc, which, whoami
jq blocks file-path arguments, file-reading flags (--slurpfile, --rawfile, -f, -L), combined short flags containing file-read characters (e.g., -rf, -nf), concatenated -L/path, and --flag=val forms — use stdin piping instead.
Schema: { command: string, args?: string[] }
Read-only git commands: blame, branch, describe, diff, log, ls-files, ls-tree, merge-base, reflog, remote, rev-parse, shortlog, show, stash, status, worktree
Additional restrictions:
--outputblocked globally (prefix matching defeats abbreviation)-oshort flag blocked ondiffonly (other commands use-ofor unrelated flags, e.g.,ls-files -o=--others)--no-indexblocked globally (prefix matching)branch: destructive flags blocked (-D,-d,-m,--delete,--force, etc.)stash: onlylistandshowsub-subcommands (--treated as terminator)worktree: onlylist(mutating:add,remove,move,prune, etc.)reflog: onlyshow,exists, and bare invocation (destructive:delete,expire)remote: only bare listing,get-url, andshow -n(no network I/O)
Schema: { args: string[] }
Read-only GitHub CLI commands: attestation verify, cache list, gist list/view, issue list/status/view, label list, pr checks/diff/list/status/view, project field-list/item-list/list/view, release list/view, repo list/view, ruleset check/list/view, run list/view, search code/commits/issues/prs/repos, secret list, status, variable list, workflow list/view
Also supports --version, --help, -h as standalone flags.
Schema: { args: string[] }
Read-only Azure DevOps CLI commands: devops project/team/extension/service-endpoint/wiki, pipelines list/show/build/release/runs/pool/agent
devops invoke is restricted to GET-only requests against build-debugging endpoints (build/builds, build/timeline, build/logs, build/changes, build/artifacts, build/leases, test/runs). Unknown flags are rejected (allowlist, not blocklist).
Schema: { args: string[] }
Read-only npm commands: audit, bin, explain, fund, ls, outdated, root, search, view
Blocked flags: --fix, --registry, --no-ignore-scripts, --ignore-scripts. --ignore-scripts is always re-injected by the server.
Schema: { args: string[] }
Read-only pnpm commands: audit, bin, licenses list, list, outdated, root, search, store status, why
Also supports --version, --help, -h as standalone flags.
Blocked flags: --fix, --registry
Schema: { args: string[] }
Read-only chezmoi commands: diff, doctor, managed, source-path, status, target-path, verify
Schema: { args: string[] }
Read-only Atlassian CLI commands: jira board list, jira filter list, jira project list, jira sprint list, jira workitem comment list, jira workitem list/search/view
Schema: { args: string[] }
pnpm add -g github:readonly-mcp/coreclaude mcp add -s user readonly -- node "$(pnpm root -g)/@readonly-mcp/core/index.mjs"To auto-approve all readonly tools, add to ~/.claude/settings.json:
{
"permissions": {
"allow": [
"mcp__readonly__*"
]
}
}Add to your settings.json:
{
"mcp": {
"servers": {
"readonly": {
"command": "node",
"args": ["/path/to/global/node_modules/@readonly-mcp/core/index.mjs"]
}
}
}
}Replace /path/to/global/node_modules with the output of pnpm root -g.
pnpm install # first time only
pnpm testindex.mjs Server entry point
lib/
allowlist.mjs Allowlist matching and validation
exec.mjs Sandboxed command execution
tools/
index.mjs Tool registry
acli.mjs Atlassian CLI tool
az.mjs Azure DevOps CLI tool
chezmoi.mjs Chezmoi tool
gh.mjs GitHub CLI tool
git.mjs Git tool
npm.mjs npm tool
pnpm.mjs pnpm tool
shell.mjs Shell utilities tool
test/ Unit and integration tests (vitest)