npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aroha-sdk/mcp-guard

v0.2.1

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.

Readme

@aroha-sdk/mcp-guard

Beta npm Zero deps

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 — your client shows an approve/deny prompt with the tool name and arguments. MCP 2026-07-28 clients get a Multi Round-Trip Request (input_required with an embedded form, retried with inputResponses); older clients get a server-initiated elicitation, where no response within the timeout (default 120s) means deny. Clients without elicitation support fail closed: the call is denied at once, and the refusal says the client could not ask — it never claims you declined. | | (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. |

Check that your client can ask. A gate is only an "ask me" if your MCP client can show approval prompts (MCP elicitation, or the 2026-07-28 multi round-trip). If it cannot, every gated tool is refused — safe, but you are never asked. Each refusal says which it was: you declined, you dismissed the prompt, nobody answered within --gate-timeout, or the client could not show it. Support differs by client and version (public reports disagree for Claude Desktop), so gate one harmless tool first and confirm you see the prompt.

List the real tool names before writing rules. Globs match names, and servers name things their own way: the GitHub server pushes with push_files and comments with add_issue_comment, which a send_*,create_* rule misses. Rules are deny-lists, so a tool with a verb you did not list is allowed.

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.

Approvals across protocol revisions

The guard picks the gate mechanism per request. A tools/call whose _meta names protocol revision 2026-07-28 (or later) gets the stateless Multi Round-Trip Request pattern: the guard answers resultType: "input_required" with an aroha_guard_approval form and an HMAC-protected requestState holding the approval id, the tool name and a hash of the arguments. The retry is forwarded only when that state verifies (unexpired, signed by this guard) and names the same tool and arguments, and the form answer is an explicit approve. The guard strips its own fields before forwarding; when the wrapped server asks for input itself, the guard wraps that server's requestState so the next retry is still recognised as approved. Set AROHA_GUARD_STATE_KEY (64+ hex characters) when approvals must survive a proxy restart; the default key is random per process.

Requests without the 2026-07-28 _meta envelope (clients that opened with initialize) keep the elicitation behaviour.

Re-approval when a tool's definition changes

The guard fingerprints every tool in each tools/list result it sees (name, title, description, input/output schema, annotations). If a later list shows a different definition for a tool, the next call to that tool needs a human approval even when no rule gates it; the approved definition is then pinned. When a list carried a ttlMs freshness hint (2026-07-28) and it has expired, the guard fetches a fresh list from the wrapped server before deciding a call, so an expired client cache cannot hide a changed definition.

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.jsonl

Library 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

Apache-2.0 — see LICENSE.