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

@kiliaosi/toll

v0.1.0

Published

Framework-agnostic Tool Runtime — governance (discovery, execution, restriction, optimization, audit) for AI agent tools.

Readme

@kiliaosi/toll

Framework-agnostic Tool Runtime — a governance layer for AI agent tools.

toll = tool + toll (a checkpoint). Every tool call passes through the checkpoint.

It is not another agent framework and does not take over the model↔tool loop. It sits one layer below that loop and turns a raw tool call into a governed one — discovery, restriction, optimization, and audit — then hands the governed tools to whatever loop you already use (Claude Agent SDK, Vercel AI SDK, a raw MCP client, or your own loop).

┌─────────────────────────────────────┐
│  Agent Loop (Claude SDK, LangGraph…) │  someone else's job: the model↔tool loop
├─────────────────────────────────────┤
│  Tool Runtime  ← toll lives here     │  discovery / restriction / optimization / audit
├─────────────────────────────────────┤
│  Raw Tools (fs, child_process, rg)   │  thin wrappers
└─────────────────────────────────────┘

Why

Every SDK bakes tool governance into its own loop and couples it there — switch loops and you rewrite the governance. toll makes one policy (a danger model + allow/deny + human confirmation + an audit trail) that you carry across frameworks, and that can govern a host's own native tools via a hook.


Install

npm install @kiliaosi/toll

Track 1 — Govern your Claude Code

This is the capability nothing else gives you: Claude Code's own Bash/Write/Edit never flow through an MCP server, so an MCP server cannot restrict them. A PreToolUse hook can. toll ships one as a CLI.

Add to .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "*",
        "hooks": [
          { "type": "command", "command": "npx -y @kiliaosi/toll hook" }
        ]
      }
    ]
  }
}

Now every native tool call is judged by toll's danger policy before it runs. Bash is graded by its actual command (not just the name): git status passes, rm -rf build is blocked. Every decision is written to an append-only JSONL audit log.

Configure via env (no code):

| Env | Values | Default | Meaning | |-----|--------|---------|---------| | TOLL_MAX_DANGER | safe | mutating | destructive | mutating | Danger ceiling. Calls above it are blocked. | | TOLL_ASK_ON_DENY | 1 | off | Turn a hard block into an interactive prompt instead. | | TOLL_AUDIT_LOG | path | ./toll-hook-audit.log | Where the audit trail goes. |

⚠️ Bash command-grading is a heuristic to add friction on the obviously-dangerous case, not a sandbox. A determined command can evade pattern matching. Use it as defense-in-depth.

Need a custom policy? Import the pieces and build your own hook entry:

import {
  createHookRuntime, decidePreToolUse, parsePreToolUseEvent, readStdin,
  bashCommandDanger, audit, restriction, fileSink,
} from '@kiliaosi/toll';

const rt = createHookRuntime({
  middleware: [
    audit({ sink: fileSink('./audit.log') }),
    restriction({ maxDanger: 'mutating', deny: ['WebFetch'] }),
  ],
});

const event = parsePreToolUseEvent(await readStdin());
const decision = await decidePreToolUse(rt, event, { classify: bashCommandDanger });
process.stdout.write(JSON.stringify(decision));

Track 2 — Provide governed tools to your agent

Build a runtime from tools + middleware, then adapt it to your framework. The same GovernedTool works everywhere.

import {
  createToolRuntime, builtinTools, restriction, audit, fileSink,
} from '@kiliaosi/toll';

const rt = createToolRuntime({
  tools: builtinTools(), // read, glob, grep (safe) + write, bash (destructive)
  middleware: [
    audit({ sink: fileSink('./audit.log') }),
    restriction({
      maxDanger: 'mutating',
      confirm: async (call) => askTheUser(call), // gate destructive calls
    }),
  ],
});

await rt.invoke('read', { path: 'package.json' }); // runs the full governance chain

Adapt to a framework — the runtime is unchanged, only the adapter differs:

// Claude Agent SDK
import { toClaudeSdkServer } from '@kiliaosi/toll';
const server = toClaudeSdkServer(rt);
// query({ prompt, options: { mcpServers: { toll: server } } })

// Vercel AI SDK
import { toVercelTools } from '@kiliaosi/toll';
const tools = toVercelTools(rt);
// generateText({ model, prompt, tools })

// A generic MCP server (any MCP client can connect)
import { serveMcp } from '@kiliaosi/toll';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
await serveMcp(rt, new StdioServerTransport());

Track 3 — Adopt & govern a third-party MCP server

Wrap tools you don't control with your own checkpoint. Adopted tools are fail-closed by default: without a confirm, only read-only tools run.

import { fromMcpServer, restrictAdopted, createToolRuntime, audit, fileSink } from '@kiliaosi/toll';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

const { client, tools } = await fromMcpServer(
  new StdioClientTransport({ command: 'node', args: [someServerEntry] }),
  { prefix: 'fs_' },
);

const rt = createToolRuntime({
  tools,
  middleware: [audit({ sink: fileSink('./audit.log') }), restrictAdopted()],
});

The governance pipeline

An invoke flows through composable middleware. Each is optional and independent.

| Phase | Role | |-------|------| | discovery | Which tools are visible in this context. | | restriction | allow/deny lists, a safe < mutating < destructive danger ceiling, human confirmation. | | execute | Timeout / retry around the tool's own handler. | | optimization | Output shrinking — transparent by default (byte-identical); opt in via a compress hook that delegates to a dedicated compressor. toll ships no compression algorithm of its own. | | audit | Full lifecycle trace of every call — args, each phase's decision, timing, result — to an append-only sink. Must be the outermost middleware. |

type Middleware = (call, next) => Promise<ToolResult>;
// composed outer→inner: [audit, restriction, execute, optimization]

ToolResult is MCP-shaped ({ content, isError? }), so it crosses the MCP / Claude SDK boundary with zero conversion.


Governance boundary (host vs toll)

When both toll and a host (Claude Code) can gate a call, they are domain-separated, never stacked:

  • Host-native tools (Claude Code's own Bash/Write/…) → governed by toll via the PreToolUse hook.
  • Self-registered / adopted tools → governed by the toll runtime directly.

The two paths are mutually exclusive: a call is judged by toll once. This avoids the "host allowed it but toll blocked it" double-judgment. See ARCHITECTURE.md §7.5.


License

MIT © kiliaosi