MCP Guard · free · one file
Pin, police and prove every MCP tool call
A local proxy between your agent and any MCP server. It pins tool definitions so a quietly rewritten tool never reaches your agent, enforces your allow and deny rules, catches payment demands before anything is paid, and gives every call a tamper-evident receipt. Only hashes leave your machine.
Pins every tool definition
The first time the guard sees a server's tools it pins each one: name, description, input schema and annotations. If a definition later changes (the classic rug pull: a harmless tool quietly rewritten to exfiltrate), or a tool appears that was never announced, the guard withholds it from your agent and refuses its calls until you review it and start once with --approve.
Enforces your policy
A small JSON file: tools to allow and deny (with * wildcards), what to do when a definition changes, what to do when a server asks for payment, and a calls-per-minute ceiling. Refusals reach your agent as ordinary tool errors that say why.
Catches payment demands
When a tool answers with HTTP 402, a JSON-RPC 402 error or x402 payment requirements, the guard records the demand (amount, asset, network, recipient) and either passes it on to your agent or, under "payments": "block", refuses it. The guard never pays, holds no wallet and touches no money.
Gives every call a receipt
Each call, refusal, definition change and payment demand becomes a receipt in your Rightshold record: hash-chained, timestamped and committed to the public ledger, so nobody, including us, can quietly edit it later.
Install
curl -fsSL --create-dirs -o ~/.rightshold/mcp-guard.mjs https://www.rightshold.com/cli/mcp-guard.mjs
curl -fsSL https://www.rightshold.com/cli/mcp-guard.sha256
shasum -a 256 ~/.rightshold/mcp-guard.mjs
node ~/.rightshold/mcp-guard.mjs --help
Version 1.0.0 · SHA-256 7e16af57622c5b1313f557883b41810083376cd51a6ce3f007678b14f2b9fc0f. The two hashes must match before you run it. The file is plain, readable JavaScript with no dependencies: read it first.
Put it in front of a server
Wrap the command your client already runs. The guard starts the server itself and passes everything through.
Claude Desktop claude_desktop_config.json
{
"mcpServers": {
"files": {
"command": "node",
"args": ["/Users/you/.rightshold/mcp-guard.mjs", "--name", "files", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"],
"env": { "RIGHTSHOLD_API_KEY": "<your key>" }
}
}
}Claude Code terminal
claude mcp add files -e RIGHTSHOLD_API_KEY=<your key> -- node /Users/you/.rightshold/mcp-guard.mjs --name files -- npx -y @modelcontextprotocol/server-filesystem /Users/you/DocumentsCursor ~/.cursor/mcp.json
{
"mcpServers": {
"files": {
"command": "node",
"args": ["/Users/you/.rightshold/mcp-guard.mjs", "--name", "files", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"],
"env": { "RIGHTSHOLD_API_KEY": "<your key>" }
}
}
}VS Code .vscode/mcp.json
{
"servers": {
"files": {
"type": "stdio",
"command": "node",
"args": ["/Users/you/.rightshold/mcp-guard.mjs", "--name", "files", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"],
"env": { "RIGHTSHOLD_API_KEY": "<your key>" }
}
}
}A remote MCP server any client
"command": "node",
"args": ["/Users/you/.rightshold/mcp-guard.mjs", "--name", "remote", "--url", "<server-url>/mcp", "--header", "Authorization: Bearer <that server's token>"]
No Rightshold key yet? Add --accept-terms once: the first receipt opens a free account (terms) and the guard keeps its key in its state directory. Or add --no-record to pin and enforce locally and send nothing.
Policy
{
"allow": ["read_*", "search_*", "premium_search"],
"deny": ["delete_*", "send_*"],
"on_change": "block",
"payments": "block",
"max_calls_per_minute": 60
}Pass it with --policy policy.json. Without a policy everything is allowed except changed or unannounced tools. Deny wins over allow.
What leaves your machine
Per receipt, only:
- the action type (mcp.tool_call, mcp.call_refused, mcp.definitions_changed, mcp.payment_demanded or mcp.payment_refused) and its outcome
- a one-line summary: the label you gave the server and the tool's name
- the SHA-256 of the full local record, and an event id derived from it
What stays in the local log (~/.rightshold/mcp-guard/log/):
- the arguments your agent sent and the results it got back
- payment amounts, assets and recipients
- the server's tool descriptions and schemas
- your policy and your pins
Each log line holds the full record and its SHA-256. To prove what a call contained, hand someone that one record: they hash it (canonical JSON, keys sorted, no whitespace) and compare the hash with the receipt. Then verify the receipt without an account.
What it does not prove
A receipt proves what was recorded and when, and that it has not changed since. It does not prove that a tool did what it said. The guard sees only the traffic that passes through it, and pinning is trust on first use: review a server's tools the first time you run it.
Download mcp-guard.mjs · See your receipts in the console · Why proof matters now · Markdown
