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

agent-ambit

v0.3.0

Published

Lightweight permission layer for AI agents — declare what an agent may do in one file, enforce it anywhere

Readme

ambit

Lightweight permission layer for AI agents. Declare what an agent may do in one readable file; enforce it in any agentic system.

Without ambit, a hallucinating model can call delete_database, send_email, or any other tool in its registry — the only limit is what was registered. With ambit, a plain YAML file is the limit.

Architecture · Full usage guide · Runnable demos

Install

npm install agent-ambit

Usage

In-process enforcement (any tool registry)

import { wrap } from "agent-ambit";

const guard = wrap(myTools, { policy: "policy.yaml" });

// Blocked calls throw PermissionDeniedError with instructions for the fix.
// Allowed calls pass through to the original function unchanged.
const result = await guard.call("read_file", { path: "/tmp/data.csv" });

MCP server

import { wrapServer } from "agent-ambit/mcp";

const server = new McpServer({ name: "filesystem", version: "1.0.0" });
server.tool("read_file",  async () => { /* ... */ });
server.tool("write_file", async () => { /* ... */ });
server.tool("delete_file", async () => { /* ... */ });

wrapServer(server, { policy: "policy.yaml" }); // one line
await server.connect(transport);

One policy across differently-named agents

Different agents name equivalent tools differently — Claude Code's Bash vs. a generic run_shell, WebFetch vs. fetch_url. aliases maps a caller's literal tool name to your policy's canonical name before the decision is made, so one policy.yaml works across all of them:

const guard = wrap(myTools, {
  policy: "policy.yaml", // written in canonical names: run_shell, fetch_url, ...
  aliases: { Bash: "run_shell", WebFetch: "fetch_url" },
});

Everything the caller sees — error messages, audit callbacks, MCP responses — still reports the literal name they used; aliasing only feeds the policy decision. See USAGE.md and examples/claude-code-hook/ for a full worked example gating Claude Code itself.

Policy file

# policy.yaml
tier: operator        # readonly | operator | admin

# operator allows all tools by default.
# List only the tools you want to restrict.
deny:
  - delete_file
  - filesystem:write_file   # scoped to the "filesystem" MCP server only
  - run_shell               # applies to all servers (no prefix)

| tier | default | |------|---------| | readonly | deny all — list tools you want to permit in allow | | operator | allow all, except tools that read as destructive by name (delete_file, dropTable, purge_cache, ...) — those are denied unless listed in allow | | admin | allow all, including destructive-sounding tools — the tier with no built-in carve-out |

Deny always wins. A tool in both allow and deny is denied.

The destructive-tool default is a name heuristic, not a semantic guarantee — see THREAT_MODEL.md. For anything with real stakes, list destructive tools in deny explicitly rather than relying on it.

What a denial looks like

PermissionDeniedError: Blocked: delete_file is not permitted under tier "operator".
Add it to allow[] or use a higher tier.

The error names the fix. It carries a .decision object with the full allow/deny reasoning for programmatic handling.

Inspect a policy

npx ambit explain policy.yaml
operator tier — tools allowed by default

  ❌ delete_file            denied (all servers)
  ❌ filesystem:write_file  denied
  ❌ run_shell              denied (all servers)
npx ambit explain policy.yaml --json

Audit and override hooks

const guard = wrap(myTools, {
  policy: "policy.yaml",

  // fires on every call — allowed or denied — for audit logging
  onCall: (decision) => logger.log(decision),

  // return true to override a denial (e.g., after human approval)
  onDenied: (decision) => approvalQueue.ask(decision),
});

What this does and doesn't protect against

Defends against: a hallucinating or prompt-injected model calling tools it shouldn't. The check happens server-side, before the tool function runs, regardless of which client or model drives the agent.

Does not defend against:

  • A fully compromised process — ambit is not a sandbox. If Node itself is compromised, the check can be bypassed.
  • Parameter-level misuse of an authorized tool — ambit controls which tools are called, not what arguments they receive. bash in allow[] means bash can run; ambit does not inspect whether the argument is ls or rm -rf /.
  • A malicious server you chose to wrap — ambit enforces your policy against your registry. If the registry itself is malicious, wrapping it does not help.

See THREAT_MODEL.md for the full boundary statement.

Unprefixed deny is broad. deny: [write_file] denies write_file on every MCP server, not just one. To scope a denial to one server, use filesystem:write_file. This is intentional — unprefixed deny is the safe default.

Contributing

See CONTRIBUTING.md. Found a security issue? See SECURITY.md instead of opening a public issue.

Changelog

See CHANGELOG.md.

License

MIT