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

@mateusz-klatt/snapper-mcp

v0.11.0

Published

Lightweight stdio-to-HTTP MCP bridge from Claude Desktop / Claude Code to Snapper /api/mcp, with a `watch` subcommand that streams live signal + order events as JSONL via the /api/auth/ws_token endpoint.

Readme

@mateusz-klatt/snapper-mcp

npm version node license CI Quality Gate Status Claude Code Plugin

Lightweight stdio-to-HTTP Model Context Protocol bridge. Spawns as a subprocess, speaks MCP over stdio to Claude Desktop / Claude Code, and proxies every request to a Snapper backend's /api/mcp endpoint with Bearer-token auth.

What is this

Snapper is a multi-tenant trading platform. Its backend exposes MCP at /api/mcp, guarded by Bearer-JWT auth, feature flag, and per-principal rate limiting. MCP hosts (Claude Desktop, Claude Code) speak MCP over stdio — they spawn a subprocess and exchange JSON-RPC frames over stdin/stdout. @mateusz-klatt/snapper-mcp is the stdio ⇄ HTTP bridge that makes that conversation work.

It is a thin bridge: ~1500 lines of TypeScript, using Node's built-in fetch, plus @modelcontextprotocol/sdk for MCP framing. No OAuth, no telemetry. Standalone hosts (Claude Desktop, systemd, plain CLI) read credentials from env vars only — nothing on disk. Claude Code plugin installs write a 0600-mode env.json into the per-plugin ${CLAUDE_PLUGIN_DATA} directory at proxy startup so the auto-spawned monitor process can read its credentials via --config=PATH; the file is owned by the plugin's data dir and is overwritten atomically on every proxy startup.

Install

Three install paths, in recommended order. All three end at the same runtime — pick whichever matches your MCP host.

Option 1 — Claude Code plugin (recommended)

In any Claude Code session:

/plugin marketplace add mateusz-klatt/snapper-mcp
/plugin install snapper-mcp@mateusz-klatt-snapper-mcp

Claude Code prompts for two values:

  • Snapper API URL — your backend's /api/mcp endpoint.
  • Access token — paste from Snapper's Settings -> AI Delegates page (the config-snippet generator). The same token authenticates both the proxy MCP server and the watch monitor.

Plugin changes installed mid-session need /reload-plugins (or a Claude Code restart) before the MCP server starts. After reloading, /mcp list should show the snapper server connected.

The plugin manifest threads both credentials (SNAPPER_BASE_URL, SNAPPER_ACCESS_TOKEN) into the proxy MCP subprocess as env vars via ${user_config.KEY} interpolation. The proxy then writes a 0600-mode env.json snapshot into ${CLAUDE_PLUGIN_DATA} at startup so the auto-spawned watch monitor can read the same values via --config="${CLAUDE_PLUGIN_DATA}/env.json". Claude Code stores sensitive: true user_config values in the OS keychain when available, falling back to ~/.claude/.credentials.json — they never land in settings.json or the manifest.

Option 2 — Claude Desktop manual config

Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):

{
  "mcpServers": {
    "snapper": {
      "command": "npx",
      "args": ["-y", "@mateusz-klatt/snapper-mcp"],
      "env": {
        "SNAPPER_BASE_URL": "https://your-snapper-instance.example.com/api/mcp",
        "SNAPPER_ACCESS_TOKEN": "<generated via Snapper UI: Settings -> AI Delegates>"
      }
    }
  }
}

Restart Claude Desktop; the Snapper server shows up in the MCP Servers settings panel.

Option 3 — Direct CLI / custom hosts

SNAPPER_BASE_URL="..." SNAPPER_ACCESS_TOKEN="..." \
  npx -y @mateusz-klatt/snapper-mcp

npm install -g @mateusz-klatt/snapper-mcp
SNAPPER_BASE_URL="..." SNAPPER_ACCESS_TOKEN="..." snapper-mcp

Requires Node 22+ (uses Node's built-in fetch, AbortController, and ESM top-level await). CI validates the declared minimum (Node 22) across Ubuntu / macOS / Windows; higher Node versions work because the bridge only relies on APIs stable since Node 18.

Configuration

Two environment variables. The MCP host must set them before spawning:

| Variable | Required? | Purpose | | --- | --- | --- | | SNAPPER_BASE_URL | yes | URL of Snapper's /api/mcp endpoint. | | SNAPPER_ACCESS_TOKEN | yes | Long-lived AI delegate JWT for Bearer auth (generated in Snapper UI). The same token authenticates both the proxy MCP server and the watch monitor. |

See .env.example for placeholder values.

Multi-profile support

Run multiple Snapper instances (prod + staging, prod + local dev) from one bridge install. Select a profile at spawn time:

SNAPPER_PROFILE=prod snapper-mcp
# or
snapper-mcp --profile=prod

When a profile is selected, the bridge reads SNAPPER_PROFILE_<UPPER>_BASE_URL and SNAPPER_PROFILE_<UPPER>_ACCESS_TOKEN instead of the bare top-level vars (hard isolation — no accidental cross-profile fallback). Profile names match ^[a-z0-9]{1,32}$. CLI flag wins over env var.

The --config JSON file may carry a profiles block:

{
  "profiles": {
    "prod":    { "SNAPPER_BASE_URL": "https://snapper.example.com/api/mcp", "SNAPPER_ACCESS_TOKEN": "..." },
    "staging": { "SNAPPER_BASE_URL": "https://staging.example.com/api/mcp", "SNAPPER_ACCESS_TOKEN": "..." }
  }
}

Verify a profile offline before spawning the bridge:

snapper-mcp check --profile=prod

Generating tokens

Snapper ships a Settings → AI Delegates UI that issues credentials. Each delegate emits a single long-lived (~10-year) access JWT. Paste the value into SNAPPER_ACCESS_TOKEN; the same token powers both the proxy MCP server and the optional push-wakeup monitor.

Each delegate has configurable caps (per-order max USD value, per-day / per-month ceilings, allowed order types). Tokens are bound to the delegate's permissions + wallet scope; revoke the delegate in-place via POST /api/ai-delegates/{id}/deactivate to kill the bridge live.

Logging

All bridge logs go to stderr exclusively. Stdout is reserved for MCP JSON-RPC frames — any stray write there corrupts the protocol stream and Claude Desktop disconnects.

Verbosity knob: set SNAPPER_MCP_LOG_LEVEL=debug for transport-level diagnostics (URL, HTTP status, error_code). Valid levels: debug / info (default) / warn / error. Timestamps in the log prefix: SNAPPER_MCP_LOG_TIMESTAMPS=1.

For structured-log pipelines, set SNAPPER_MCP_LOG_FORMAT=json to switch every stderr line to a single-line JSON object:

{"t":"2026-05-02T16:00:00.000Z","lvl":"info","prefix":"bridge","msg":"connected","rest":[{"req":42}]}

The t field is always present in JSON mode regardless of SNAPPER_MCP_LOG_TIMESTAMPS. Pipe through jq for filtering:

npx -y @mateusz-klatt/snapper-mcp 2>&1 | jq 'select(.lvl=="error")'

Error instances in the rest arguments serialise as {name, message, stack}; circular objects fall back to String(value) rather than throwing.

Authentication

On every outbound HTTP request, the bridge injects Authorization: Bearer ${SNAPPER_ACCESS_TOKEN} while preserving any Accept / Content-Type / other headers the SDK set.

On 401 the bridge writes a single-line stderr message — once per session — and propagates the 401 response to the MCP host. The host surfaces the auth error as a protocol error; the operator recreates the AI delegate in Snapper to recover.

Watch subcommand

snapper-mcp watch opens a long-lived WebSocket session against Snapper's /api/ws endpoint and writes one JSONL frame per line to stdout. A Claude Code plugin monitor (or any host that can read a subprocess's stdout) consumes the JSONL stream as push-style wakeup.

snapper-mcp watch --topic signals. --topic orders.events.

Default subscription: signals. and orders.events. if no --topic is given. Each prefix MUST end with . to address a topic family root.

The watch session mints a one-shot WebSocket token via POST /api/auth/ws_token using the same SNAPPER_ACCESS_TOKEN configured for the proxy.

Check subcommand

snapper-mcp check runs an offline diagnostic on the configured access token + base URL — no network. Useful for validating the contents of SNAPPER_ACCESS_TOKEN (sub, role, scopes, expiry) without burning the token on a failed bridge-up.

snapper-mcp check
# base URL: https://snapper.example.com/api/mcp/
# access token:
#   alg: HS256
#   sub: 01891e92-...
#   role: AI_DELEGATE
#   scopes: read.orders, write.orders
#   exp: 2026-08-01T00:00:00.000Z (in 90.5d)
#   status: valid

Exit codes:

  • 0 — token decoded, base URL parsed, expiry OK.
  • 1 — env validation failed (missing or malformed inputs).
  • 2 — token decoded but is already expired or has no exp claim.

The same --config=PATH, --access-token, and --base-url flags the proxy + watch subcommands accept also work for check.

Plugin monitor entry

The plugin manifest's monitors[] block auto-spawns snapper-mcp watch on plugin install. The monitor command reads credentials from the same --config=PATH JSON file the proxy MCP server seeded into ${CLAUDE_PLUGIN_DATA}/env.json at startup. Both processes share the same SNAPPER_ACCESS_TOKEN.

Development

See CONTRIBUTING.md for the dev workflow (npm install, npm test, npm run build).

License

MIT — see LICENSE.