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

metrics-mcp

v0.1.0

Published

MCP server for metrics, the privacy-friendly web-analytics service: read page views, visitors, pages, referrers, countries and devices per project, inspect raw events and manage projects, with role-based tool access and audit logging.

Readme

metrics-mcp

Model Context Protocol server for metrics, the privacy-friendly web-analytics service. Lets an AI agent (Claude Code, Claude Desktop, Cursor, the MCP Inspector, your own app) read page views, visitors, pages, referrers, countries, devices and custom events per project, inspect raw hits, and manage projects — with role-based tool access, a human approval step for destructive actions, and an audit trail.

npx metrics-mcp                     # stdio (local hosts)
https://metrics.3m4.net/mcp         # Streamable HTTP (remote hosts)

Quick start

  1. Create an API key in the dashboard (/dashboard/settings) and pick a role.
  2. Register the server with your host.

Claude Code

claude mcp add metrics --scope user \
  --env METRICS_API_KEY=metrics_live_… \
  -- npx -y metrics-mcp

Cursor / Claude Desktop / any JSON config

{
  "mcpServers": {
    "metrics": {
      "command": "npx",
      "args": ["-y", "metrics-mcp"],
      "env": { "METRICS_API_KEY": "metrics_live_…" }
    }
  }
}

Remote (Streamable HTTP) — no install, the key travels as a Bearer token:

claude mcp add --transport http metrics https://metrics.3m4.net/mcp \
  --header "Authorization: Bearer metrics_live_…"

Then ask: "how is Kinegram doing this week?", "which countries visit the pricing page most?", "what did the newsletter campaign bring?", "list the signup events from mobile", "write the weekly report".

Configuration

| Variable | Default | Meaning | |---------------------|-------------------------------|---------| | METRICS_API_KEY | — | Required for stdio. The metrics management API key (metrics_live_…). | | METRICS_API_URL | https://metrics.3m4.net/api | API base URL. Self-hosters point this at their instance. | | METRICS_AUDIT_LOG | stderr | stderr, none, or a file path (JSON lines). | | METRICS_MCP_PORT | 3333 | HTTP entrypoint only. |

Project public keys (metrics_pub_…) are not accepted: they can only call the collect API and carry no role.

Roles and tools (RBAC)

Each API key has a role. The server asks the API for the key's scopes once (GET /apikeys/me) and only registers the tools those scopes allow, so a read-only key never even lists purge_events. Every call is re-checked at call time, and the API enforces the same scopes independently — three layers, and a bug in the server can hide access but never grant it.

| Tool | Scope | viewer | editor | admin | Notes | |----------------------|-------------------|:------:|:------:|:-----:|-------| | list_projects | projects:read | ✓ | ✓ | ✓ | search with q; 30-day counts, last hit | | get_project | projects:read | ✓ | ✓ | ✓ | public key, time zone, allowed hostnames, switches | | project_summary | stats:read | ✓ | ✓ | ✓ | today / yesterday / 7d / 30d, live, all-time | | get_stats | stats:read | ✓ | ✓ | ✓ | totals, previous period, series, every breakdown, live; filters | | get_breakdown | stats:read | ✓ | ✓ | ✓ | one dimension, paginated | | list_events | stats:read | ✓ | ✓ | ✓ | raw hits with props; kind, q | | get_live | stats:read | ✓ | ✓ | ✓ | last 5 minutes | | get_usage | usage:read | ✓ | ✓ | ✓ | events / dropped / counts vs. plan limits | | create_project | projects:write | | ✓ | ✓ | returns the public key + snippet | | update_project | projects:write | | ✓ | ✓ | pause tracking, bots, store raw IPs, time zone, allowed hostnames… | | rotate_project_key | projects:write | | ✓ | ✓ | old key stops working immediately | | delete_project | projects:delete | | | ✓ | confirmation required | | purge_events | events:delete | | | ✓ | confirmation required; optional before date | | get_audit_log | audit:read | | | ✓ | |

Roles are defined once in packages/shared/api_key_roles.json and shared by the API, the dashboard and this server.

Also exposed: resources metrics://projects and metrics://projects/{id}/summary, and the weekly_report prompt (last 7 days vs the week before: trend, daily shape, sources, pages, devices, things to look into — read-only).

Human approval for destructive actions

delete_project and purge_events are irreversible, so they never run on the model's say-so alone:

  • On hosts that support MCP elicitation (Claude Code, Inspector) the user gets a native confirmation dialog showing exactly what will be deleted.
  • On hosts that don't (and always over HTTP, which is stateless), the tool refuses with confirmation_required until the model calls it again with confirm: true — which it can only honestly do after asking the user.

rotate_project_key is not gated but is marked destructive: every snippet using the previous key stops working the moment it runs. Prefer update_project with collect_enabled: false over deleting a project.

Audit trail

Two complementary logs:

  • Server-side (audit_events table, GET /account/audit, tool get_audit_log): every mutation and every authorization denial, written in the same transaction as the change.
  • Agent-side (METRICS_AUDIT_LOG, JSON lines): every tool call the agent attempted, including ones refused locally that never reached the API, with duration, outcome (ok|denied|error|cancelled|pending) and compacted args.

Design notes

  • Structured outputs. Every tool declares an outputSchema and returns structuredContent; hosts can validate and chain results without parsing prose. Objects mirroring API responses allow extra fields, so an additive API change never breaks client-side validation.
  • Errors are results. API errors come back as isError with the stable code (limit_reached + which, key_taken, invalid_timezone, invalid_origins, insufficient_scope, not_found…) so the model can recover instead of crashing.
  • stdio hygiene. stdout is the protocol channel; all logging goes to stderr.
  • Stateless HTTP. Each request builds a server bound to the caller's key; tenants can never share state. The /apikeys/me lookup is cached 60 s per key.

Development

bun install
bun test            # in-memory MCP client ↔ server against a fake API
bun run build
METRICS_API_KEY=… METRICS_API_URL=http://localhost:8083/api bun run inspect   # MCP Inspector
METRICS_API_KEY=… METRICS_API_URL=http://localhost:8083/api bun run dev:http  # http://localhost:3333/mcp

The Docker image (packages/mcp-server/Dockerfile) runs the HTTP flavour and is wired into deploy/docker-compose.yml as the mcp service, exposed by Caddy at /mcp.

License

MIT © Enrique Magallon Alvarez