@aroha-sdk/mcp-guard
v0.1.0
Published
A firewall for your AI agent's tools — wrap any MCP server with spending limits, action limits, human approval gates, and a full audit log. Zero code, one config change.
Maintainers
Readme
@aroha-sdk/mcp-guard
A firewall for your AI agent's tools. Wrap any MCP server with action limits, always-deny rules, human approval gates, and a full audit log — zero code, one config change.
Your assistant's GitHub MCP server can delete repos. Your database server can drop tables. The model is polite, but politeness is not a security boundary. mcp-guard puts a hard boundary between the model and every tool it can reach.
Quick start
Take any MCP server in your client config and wrap it:
Before:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
}
}
}After:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@aroha-sdk/mcp-guard",
"--block", "delete_*",
"--gate", "merge_*,create_repository",
"--limit", "create_*:10",
"--",
"npx", "-y", "@modelcontextprotocol/server-github"]
}
}
}That's it. Works with Claude Desktop, Cursor, Windsurf, Cline, Zed — anything that speaks MCP over stdio. The wrapped server needs no changes and never knows the guard is there.
What each rule does
| Rule | Behaviour |
|------|-----------|
| --block <globs> | Matching tools are always denied. The model gets a clear refusal, the server never sees the call. |
| --limit <glob>:<n> | At most n calls per session across all tools matching the glob. The n+1th call is denied with a "do not retry" message. |
| --gate <globs> | Each call pauses for human approval via MCP elicitation — your client shows an approve/deny prompt with the tool name and arguments. No response within the timeout (default 120s) means deny. Clients without elicitation support fail closed: the call is denied. |
| (always on) | Every decision is appended to ~/.aroha/mcp-guard.jsonl — tool, timestamp, allow/deny — plus a session receipt on exit. Disable with --no-log. |
Globs are simple: * matches anything, ? one character, matching is case-insensitive. Rules also accept env vars (AROHA_GUARD_BLOCK, AROHA_GUARD_LIMIT, AROHA_GUARD_GATE) so config args stay short.
Why fail closed?
Every ambiguous situation resolves to deny: unknown elicitation support, approval timeout, malformed rules at startup. A guard that fails open is decoration.
Recipes
Read-only database access:
--block "insert_*,update_*,delete_*,drop_*,execute_*"Filesystem with a write budget:
--limit "write_file:20" --block "delete_*" --gate "move_*"Anything irreversible needs a human:
--gate "send_*,delete_*,merge_*,deploy_*,publish_*"Audit only (observe before you restrict):
--name "github" --log ./github-audit.jsonlLibrary use
The rule engine and proxy are exported for embedding:
import { GuardEngine, parseRules } from "@aroha-sdk/mcp-guard";
const engine = new GuardEngine(parseRules({
block: ["delete_*"],
limit: ["create_*:5"],
gate: ["merge_*"],
}));
engine.decide("delete_repo"); // { action: "block", reason: "..." }Part of the Aroha Protocol
mcp-guard is the zero-config entry point to Aroha — cryptographically bounded authority for AI agents. When you outgrow session-scoped globs and need signed, verifiable, delegable authority (spending mandates, task mandates, receipts), the same rules become Ed25519-signed mandates that survive across agents and organisations.
MCP calls the tools. mcp-guard bounds what they're allowed to do.
License
MIT © Aroha Labs
