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

@cyanheads/toolkit-mcp-server

v2.3.1

Published

Generate random IDs, QR codes, and hashes, encode and decode values, and geolocate IPs, plus gated network and system diagnostics, via MCP. STDIO or Streamable HTTP.

Readme

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://toolkit.caseyjhand.com/mcp


Overview

A standalone developer-utilities server — the five always-on tools need no upstream API: generate identifiers, QR codes, and cryptographic digests, encode and decode values, and geolocate a public IP or hostname. Two more tools report diagnostics about the server's own host, gated off by default. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.

Tools

| Tool | Description | |:---|:---| | toolkit_hash_value | Generate a cryptographic digest (sha256/sha384/sha512/sha1/md5) as hex, base64, or SRI, or constant-time-compare a value against an expected digest. | | toolkit_generate_id | Mint cryptographically-random identifiers — UUIDv4, UUIDv7, or ULID — singly or in batches up to 1000. | | toolkit_generate_qr | Encode text or a URL into a QR code as SVG markup, base64 PNG, or a terminal-renderable string. | | toolkit_encode_value | Encode or decode a value across base64, base64url, hex, or URL percent-encoding, in either direction. | | toolkit_geolocate_ip | Resolve a public IP or hostname to geographic and network metadata — country, city, coordinates, ASN, timezone. | | toolkit_check_network | Gated, off by default. Read-only network diagnostics from the server host — ping, traceroute, TCP connectivity, or egress-IP detection. | | toolkit_check_system | Gated, off by default. Report a facet of the server host's system state — OS, CPU, memory, load average, or network interfaces. |

Capability reference

toolkit_hash_value tool

  • operation: generate (a digest) or compare (timing-safe check via timingSafeEqual); omitted, it compares when expected is sent and generates otherwise. generate sent together with expected is rejected with a typed expected_without_compare error rather than ignoring expected
  • Algorithms: sha256 (default), sha384, and sha512 for security; sha1 and md5 are exposed for checksum and file-integrity compatibility only — never for passwords or signatures
  • digestEncoding sets the generated digest's form: hex (lowercase, default), base64, or sri (sha512-<base64>, the npm lockfile integrity and Subresource Integrity form — sha256/sha384/sha512 only)
  • expected is accepted as hex, base64, or SRI, recognized by its shape at the algorithm's digest length, so a published checksum is pasted as-is. An SRI value may hold several space-separated entries, as an npm integrity field can: entries for other algorithms are skipped, and it matches when any entry for algorithm does
  • Typed errors separate an unrecognizable digest (expected_malformed), one of the wrong length (expected_length_mismatch, whose hint names the algorithm that length belongs to), and an SRI value with no entry for algorithm (expected_algorithm_mismatch)
  • inputEncoding reads value as utf8 (default), hex, or base64, so binary blobs skip a decode round-trip
  • Canonical use: match a download against a vendor-published checksum or a lockfile integrity entry

toolkit_generate_id tool

  • type: uuid_v4 (random, default), uuid_v7 (time-ordered, sortable by creation), or ulid (26-char Crockford base32, lexicographically sortable)
  • count mints a batch up to 1000 in one call; the returned ids array always holds exactly count values
  • uuid_v7 and ulid batches are monotonic — strictly increasing even within the same millisecond — so ids stays in sorted creation order. Ids minted in the same millisecond are separated by random gaps (a 32-bit draw plus one), so no id in a batch is derivable from another; for ulid this departs from the spec's reference +1 increment on purpose
  • Read-only — minting changes nothing — but never idempotent, so a client won't cache or deduplicate a batch

toolkit_generate_qr tool

  • format: svg (inline markup), png_base64 (raster bytes with mimeType and byteLength), or terminal (plain Unicode half-blocks with no escape codes, fenced in content[])
  • terminal is drawn for a dark background: light modules, quiet zone included, are blocks and dark modules are spaces
  • errorCorrection (L/M/Q/H) trades data capacity for damage tolerance; margin sets the quiet-zone width in modules for every format; scale sets pixels per module for svg (its width/height) and png_base64, so both are (modules + 2 × margin) × scale px per side
  • The returned version (1–40) reflects how dense the encoded data is
  • png_base64 also arrives as an MCP image content block, so a client reading content[] can render the code without decoding structuredContent
  • A rendered PNG is bounded at 2048 px per side — (modules + 2 × margin) × scale — so a dense symbol at a high scale is rejected with a typed raster_too_large error naming a scale that fits; svg and terminal are unbounded
  • data is encoded as UTF-8 and capped at 2953 bytes — the absolute ceiling (version 40, level L, byte mode); a non-ASCII character takes 2–4 bytes, and usable capacity is lower at higher errorCorrection levels, so over-capacity input is rejected with a typed data_too_large error that reports the payload's byte count

toolkit_encode_value tool

  • encoding: base64, base64url (URL-safe alphabet), hex, or url (percent-encoding)
  • operation: encode (raw UTF-8 → encoding) or decode (encoded value → bytes)
  • outputEncoding (decode only) returns the recovered bytes as utf8 text (when omitted), hex, or base64 — lossless for binary data, and a direct transcode between encodings (a base64 digest to hex, for example). Sent with encode, it is rejected with a typed output_encoding_not_applicable error
  • Decode never substitutes replacement characters: bytes that aren't valid UTF-8 return a typed decode_not_utf8 error pointing at outputEncoding, and a leading byte-order mark is kept
  • Whitespace in hex, base64, and base64url input is ignored, so line-wrapped MIME and PEM bodies decode as-is (without PEM's -----BEGIN/END----- lines, which aren't base64); a url value is taken literally
  • Malformed decode input returns a typed decode_failed error with a recovery hint, not a silent best-effort

toolkit_geolocate_ip tool

  • Returns country, region, city, latitude/longitude, ASN, owning organization, and timezone
  • proxy, hosting, and mobile flag when the address is a proxy/VPN/Tor exit, a datacenter network, or a mobile carrier — a true on any of them means the coordinates describe infrastructure, not a person. Absent when the provider doesn't report them
  • A hostname is DNS-resolved first; resolvedIp echoes the IP actually located, and source names the answering provider
  • SSRF-free — the server calls the provider, never the target; the resolved IP is re-checked against private ranges, and private/reserved addresses are rejected (they have no public geolocation)
  • Best-effort and provider-bounded: VPNs, proxies, mobile NAT, and anycast all defeat IP-to-location, accuracy is city-level at best, and absent fields are reported as unknown rather than invented
  • Provider-supplied strings are truncated and stripped of control characters before they reach the response, so registry-controlled text (org, isp, as) cannot flood or format a model's context
  • Keyless by default (ip-api free tier, which is plaintext HTTP — see TOOLKIT_GEO_BASE_URL); results are cached in memory by resolved IP under a fixed entry cap

toolkit_check_network tool

  • Gated — registered only when TOOLKIT_ENABLE_NET_DIAGNOSTICS=true; absent from tools/list otherwise
  • mode: ping (ICMP round-trip), traceroute (hop path to the target), connectivity (raw TCP connect to target on port), or public_ip (the host's own egress IP)
  • A host that does not respond is reported as reachable: false — a valid result, not an error. A ping or traceroute binary that is missing, or that exits without a result, is an unreachable error naming the binary instead
  • ping reports sent, received, and packetLossPercent alongside the average rttMs; on macOS/BSD an IPv6 target runs ping6/traceroute6
  • connectivity reports an outcome — open, refused (nothing listening), timeout (traffic dropped), or unreachable (no route) — and the connect time as rttMs when open
  • Diagnoses the server's own network, so it is useful on a local or self-hosted deployment; reaching a private/reserved/internal target additionally requires TOOLKIT_ALLOW_PRIVATE_NETWORK=true, which keeps the cloud-metadata endpoint blocked by default

toolkit_check_system tool

  • Gated — registered only when TOOLKIT_ENABLE_SYSTEM_INFO=true; absent from tools/list otherwise
  • what: os, cpu, memory, load, or interfaces
  • Exactly one facet object is populated per call, matching what
  • memory reports availableBytes (headroom for new allocations) and, when the server runs under a container memory limit, limitBytes; totalBytes, freeBytes, and usedBytes are the raw OS figures, which count reclaimable cache as used and read the host's RAM inside a container
  • Describes the host this server runs on, not the calling client — meaningful on a local or self-hosted deployment; gated off by default because os and interfaces disclose host topology and version details

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

Toolkit-specific:

  • Local, pure-compute core — hashing, ID minting, QR encoding, and value encode/decode run entirely in-process via node:crypto and the qrcode library; no upstream calls
  • toolkit_geolocate_ip is the one keyless-by-default network call (ip-api free tier, optional TOOLKIT_GEO_API_KEY); the server calls the provider directly and re-checks the DNS-resolved IP against private ranges, so a hostname can't smuggle a request to an internal address
  • Fail-closed gating — the two host-probing tools (toolkit_check_network, toolkit_check_system) are absent from tools/list unless explicitly enabled, so a hosted instance exposes no SSRF or info-disclosure surface by default
  • Two-tier network gate — even with diagnostics enabled, private/reserved/loopback/link-local targets (including the cloud-metadata endpoint) stay blocked until a second flag permits them
  • Bounded inputs — QR data capped at 2953 bytes, rendered PNGs capped at 2048 px per side, ID batches capped at 1000; CSPRNG-backed primitives with constant-time hash comparison via timingSafeEqual

Agent-friendly output:

  • Provenance — geolocation echoes resolvedIp (the IP actually located) and source (the answering provider); absent upstream fields are reported as unknown, never invented
  • Response shaping — provider-supplied strings (org, isp, as) are length-bounded and stripped of control characters before they reach the response, so untrusted registry text can't flood or format a model's context
  • Discriminated output contracts — operation, format, mode, and what fields echo back exactly what ran, with only the branch-relevant fields populated per call; an unreachable host in toolkit_check_network reports reachable: false as valid data, not an error
  • Typed failure reasons — decode, hashing, QR, geolocation, and network failures each carry a structured reason plus a next-step recovery hint (e.g. decode_not_utf8, expected_malformed, raster_too_large, private_target_blocked); expected sent with generate, and outputEncoding sent with encode, are rejected by name rather than silently ignored

Getting started

Public Hosted Instance

A public instance is available at https://toolkit.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:

{
  "mcpServers": {
    "toolkit-mcp-server": {
      "type": "streamable-http",
      "url": "https://toolkit.caseyjhand.com/mcp"
    }
  }
}

Self-Hosted / Local

Add the following to your MCP client configuration file. No API key is required — the five always-on tools and the default keyless geolocation tier work out of the box.

{
  "mcpServers": {
    "toolkit-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/toolkit-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "toolkit-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/toolkit-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "toolkit-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/toolkit-mcp-server:latest"]
    }
  }
}

To enable the gated host-probing tools, add their flags to env (or -e for Docker):

"env": {
  "MCP_TRANSPORT_TYPE": "stdio",
  "TOOLKIT_ENABLE_NET_DIAGNOSTICS": "true",
  "TOOLKIT_ENABLE_SYSTEM_INFO": "true"
}

For Streamable HTTP, set the transport and start the server:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key needed — geolocation uses the keyless ip-api free tier by default.

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/toolkit-mcp-server.git
  1. Navigate into the directory:
cd toolkit-mcp-server
  1. Install dependencies:
bun install

Configuration

Every variable is optional. Server-specific options are validated at startup via the Zod schema in src/config/server-config.ts.

| Variable | Description | Default | |:---|:---|:---| | TOOLKIT_ENABLE_NET_DIAGNOSTICS | Register the gated toolkit_check_network tool. Leave off for hosted or shared deployments. | false | | TOOLKIT_ENABLE_SYSTEM_INFO | Register the gated toolkit_check_system tool. Meaningful only on a local or self-hosted deployment. | false | | TOOLKIT_ALLOW_PRIVATE_NETWORK | With network diagnostics on, permit private/reserved/loopback targets. The second explicit gate. | false | | TOOLKIT_GEO_API_KEY | API key for the geolocation endpoint, if it requires one. | none | | TOOLKIT_GEO_BASE_URL | Base URL for an ip-api-compatible geolocation endpoint. The default is plaintext HTTP — ip-api's HTTPS endpoint is not part of the keyless free tier and answers 403 SSL unavailable for this endpoint without a paid key. Point this at an HTTPS endpoint (with TOOLKIT_GEO_API_KEY) to encrypt the provider request. | http://ip-api.com | | TOOLKIT_GEO_CACHE_TTL_SECONDS | In-memory geolocation cache TTL in seconds. | 3600 | | TOOLKIT_GEO_RATE_LIMIT_PER_MIN | Max geolocation requests per minute. | 45 | | MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio | | MCP_HTTP_PORT | Port for the HTTP server. | 3010 | | MCP_SESSION_MODE | auto, stateful, or stateless. No tool requests multi-round input. auto is the framework schema default and resolves to stateful, but with MCP_SESSION_MODE unset the server resolves stateless from createApp({ sessionMode }); an explicit MCP_SESSION_MODE value still overrides it. | stateless | | MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none | | MCP_LOG_LEVEL | Log level (RFC 5424). | info | | OTEL_ENABLED | Enable OpenTelemetry instrumentation (spans, metrics, completion logs). | false |

See .env.example for the full list of optional overrides.

Running the server

Local development

  • Build and run:

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
  • Run checks and tests:

    bun run devcheck   # Lint, format, typecheck, security, changelog sync
    bun run test       # Vitest test suite
    bun run lint:mcp   # Validate MCP definitions against spec

Docker

docker build -t toolkit-mcp-server .
docker run --rm -e MCP_TRANSPORT_TYPE=http -p 3010:3010 toolkit-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/toolkit-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

| Directory | Purpose | |:---|:---| | src/index.ts | createApp() entry point — registers tools and inits services, with fail-closed gating for the two host-probing tools. | | src/config | Server-specific environment variable parsing and validation with Zod. | | src/mcp-server/tools | Tool definitions (*.tool.ts). Seven tools — five always-on, two gated. | | src/services/geo | Geolocation service — DNS resolution, provider call with retry/backoff, normalization, in-memory cache. | | src/services/network | Network-diagnostic service plus the shared target validator and private-range classifier. | | tests/ | Unit and integration tests mirroring the src/ structure. |

Development guide

See CLAUDE.md / AGENTS.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools in the createApp() arrays in src/index.ts
  • The two host-probing tools register behind their enable-flags; the network target gate validates after DNS resolution — never fabricate a result for an unlocatable or unreachable target

Contributing

Issues are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.