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

claude-call

v0.3.0

Published

Invoke Claude tools directly.

Readme

claude-call

npm version npm downloads

Invoke Claude tools directly.

Why

Your project already has Claude Code working — MCP servers authenticated, env vars set, permissions dialed in. claude-call reuses all of that to invoke a single tool directly, so you don't have to replicate the MCP auth dance or rebuild the environment just to call one tool from a script. No LLM in the loop: no reasoning, no tokens.

Usage

npx claude-call mcp__linear__get_issue '{"id":"ENG-123"}'

Tool name format is mcp__<server>__<tool>. The input JSON must match the tool's schema exactly. The tool's result is printed to stdout; exit is 0 on success, 1 if something went wrong (stderr has the message).

Hooks in your Claude Code config are disabled by default — claude-call is for scripted dispatch, and interactive hooks can silently deny or hang calls. Re-enable them for a specific invocation with:

npx claude-call --settings='{"disableAllHooks":false}' Bash '{"command":"ls"}'

--settings <json> is deep-merged on top of the defaults; anything you can put in Claude Code's settings.json goes there.

Discovering tools

npx claude-call listToolSchemas

Spawns claude briefly, captures its tools array (built-ins + every registered MCP), and prints the schemas as JSON. Handy for seeing what tool names exist, what inputs they expect, or feeding into another system that needs the schema. Accepts --settings to scope the lookup to specific MCPs.

Each one-shot call spawns a fresh claude subprocess, which pays the full MCP connect cost (~5–20s) and starts with fresh MCP state. Two reasons to run a server instead:

  1. Speed — scripts making many calls skip the reconnect on every call after the first.
  2. Stateful MCPs — MCPs that hold in-process state reset between one-shot calls, since each call spawns fresh MCP subprocesses. Under serve, the MCP subprocesses live as long as the claude child does, so state persists across --server calls. If your script depends on state surviving between calls, you need serve — one-shot won't work.

Run claude-call serve to boot one claude and keep it alive, then hit it with --server=<port>.

claude-call serve runs in the foreground. It prints a single JSON line to stdout: {"port":<num>,"pid":<num>}. All other output (logs, claude's stderr) goes to stderr so the stdout line is trivially parseable.

Put it in the background with & (capturing the first line via a log file), use process substitution, or just run it in a second terminal.

# Start serve in the background and capture port/pid.
npx claude-call serve > /tmp/cc.out &
until [ -s /tmp/cc.out ]; do sleep 0.1; done
eval "$(jq -r '"PORT=\(.port); PID=\(.pid)"' /tmp/cc.out)"

# Wait until MCPs are connected so the first real call is warm too.
until curl -sf http://127.0.0.1:$PORT/ready > /dev/null; do sleep 0.2; done

# All calls are now fast.
for id in ENG-123 ENG-124 ENG-125; do
  npx claude-call --server=$PORT mcp__linear__get_issue "{\"id\":\"$id\"}"
done

# Stop serve when you're done (SIGINT/SIGTERM also work cleanly).
curl -s http://127.0.0.1:$PORT/kill   # or: kill $PID

Endpoints: POST /call (body: {tool, input}) drives claude; GET /ready returns 200 once MCPs are connected (claude is warmed with a no-op Bash true on startup); GET /kill exits.

Concurrency is 1 — calls against the same server serialize (claude handles one turn at a time). If you need parallelism, start multiple servers.

Timeouts: pass -t/--timeout <ms> to claude-call --server=... to cap a single call; default 120s.

Per-server --settings: claude-call serve --settings=<json> applies to the long-running claude, so every --server call inherits it. Useful for registering ad-hoc MCP servers for a script without touching the user's config:

claude-call serve --settings='{"mcpServers":{"my-thing":{"command":"/abs/path/to/node","args":["my-mcp.mjs"]}}}'

Use an absolute binary path for mcpServers.*.command. Shell aliases and functions aren't resolved by the child claude's process spawner — it execs the binary directly.

The server listens on 127.0.0.1 only. It runs claude with --permission-mode=bypassPermissions, which is fine for a locally-owned process but would be a bad idea on a shared host.

--skip-claude: bring your own claude

If you want to compose the claude invocation yourself, pass --skip-claude. serve then exposes only the fake /v1/messages endpoint (and /kill), and you drive claude manually:

npx claude-call serve --skip-claude > /tmp/cc.out &
until [ -s /tmp/cc.out ]; do sleep 0.1; done
eval "$(jq -r '"PORT=\(.port); PID=\(.pid)"' /tmp/cc.out)"

claude -p \
  --settings "{\"env\":{\"ANTHROPIC_API_KEY\":\"sk-fake\",\"ANTHROPIC_BASE_URL\":\"http://127.0.0.1:$PORT\"}}" \
  --permission-mode bypassPermissions \
  --output-format json \
  '{"tool":"mcp__linear__get_issue","input":{"id":"ENG-123"}}' \
  | jq -r .result

curl -s http://127.0.0.1:$PORT/kill

In --skip-claude mode there's no /call endpoint, so --server won't work — you're on your own for talking to claude.

License

MIT