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

@keepsnappy/mcp

v0.1.0-alpha.18

Published

KeepSnappy MCP server (stdio, read-only): performance findings, compare, window history and commits for coding agents.

Downloads

780

Readme

@keepsnappy/mcp

KeepSnappy's MCP server for coding agents (Claude Code and others): it answers "what got slower, where, how sure, why, and what do I do" from the performance findings KeepSnappy measured on real phones. It never starts a measurement. It reads, with two exceptions: submit_hypotheses stores open predictions about a finding (no verdict reads them), and recompute_with_new_rules starts the server-side recompute with a run key (plan 48 §4, plan 82, plan 86).

It gives the same answers as the keepsnappy CLI (keepsnappy findings | finding | compare | history | commits | layers): both use this package's API client and answer layout, so a person and an agent read the same words.

Which question needs which tool, and how to read the answers: docs/agents.md ("KeepSnappy for coding agents").

Set it up in an app repo (TapIn)

  1. Ask the KeepSnappy owner for a project key with read scope (bun run create-key -- <org> <app> read in apps/api; it refuses an org or app that does not exist). Setup → Keys on the dashboard lists read keys and revokes them. A CI key (run scope) works too; an SDK key (ingest) does not.
  2. Put the key in your shell environment, not in the repo: export KEEPSNAPPY_KEY=pl_rk_… (for example in ~/.zshenv).
  3. Add .mcp.json at the repo root:
{
  "mcpServers": {
    "keepsnappy": {
      "command": "npx",
      "args": ["-y", "@keepsnappy/[email protected]"],
      "env": {
        "KEEPSNAPPY_KEY": "${KEEPSNAPPY_KEY}",
        "KEEPSNAPPY_API": "https://keepsnappy-api-922918702810.europe-west1.run.app"
      }
    }
  }
}

Claude Code expands ${KEEPSNAPPY_KEY} from your environment. KEEPSNAPPY_API is optional (the hosted API is the default). Use the first release after 0.1.0-alpha.18 (all seventeen tools; alpha.18 has fifteen, without get_investigation and submit_hypotheses; alpha.17 has thirteen, without rules_status and recompute_with_new_rules; alpha.16 has eleven, without list_problems and main_changes; alpha.15 ten, alpha.13 six). From a KeepSnappy checkout instead of npm: "command": "bun", "args": ["<keepsnappy>/packages/mcp/src/bin.ts"].

Tools

| Tool | Answers | API | |---|---|---| | pr_status | Is this branch ok, in one call: blocked, nothing blocks with results to look at, or ok; per journey × phone the deciding word, whether it blocks, the worst present findings (ids for get_finding) and the dashboard link. A branch never measured, whose newest run set was not judged, or that could not be compared yet is not ok: the answer says which, with its reason. | GET /v1/branches/status | | list_problems | The single problems of a branch's own runs: "Over budget" and "Far over budget" (from twice the budget), each with the value and the budget, the runs it was seen in, the heaviest components and the tag ("New in this PR", "Also on main", or "Not compared with main" when the comparison could not tell). Not a regression and never blocking; pr_status counts them. Filters: branch or main, journey, phone, limit (≤ 200), offset. | GET /v1/problems | | list_findings | Whether and when the branch was measured (measured: run sets, the newest set of each journey × phone with its commit, word and runs), then what got worse (Worse / Warning) or better, worst first. Filters: branch or main, journey, phone, state (present default, not-judged, resolved, all), limit (≤ 200), offset (the answer gives next_offset). | GET /v1/findings | | get_finding | One finding by id: what changed, where (journey, phone, branch, commit title and author, PR), how sure (runs per side, session spread, measured differently / not blocking, setup notes), why (components that rendered longer with their cause, counts that moved), what to do (a prompt for a coding agent), lifecycle (first and last seen; present, resolved, or not judged when the newest run set did not judge the measure; seen in N PRs). The prompt ends with the finding's id and page. | GET /v1/findings/:id | | compare_runs | Two runs side by side, window by window, and what differs in how they were measured. No statistics. | GET /v1/compare?a=&b= | | window_history | One window (screen, tap, scroll) of a journey on main over time, run by run, with commit titles, plus the findings on it. | GET /v1/history | | session_places | Every place (screen, tap, scroll) of one session (a run set of one journey on one phone) from its stored statistics: per measure the median, MAD, p10/p90 and range, and the components with the most own time. Not a verdict, not a comparison. Pick with run_set, or journey (+ phone, branch; default: the app's baseline branch (main)) for the newest closed run set with a valid run; rank by measure (default render_ms) and sort (median, p90, mad), narrow with window, page with limit / offset (the answer gives next_offset), top_components (default 5). A session with no stored statistics says not_computed, one with another version's stale: never zero. | GET /v1/sessions/places | | commits_between | The commits KeepSnappy measured between two points (run set ids or commit hashes), one entry per commit with its sessions. | GET /v1/commits | | main_changes | Main's confirmed level shifts in one lane (journey, phone), newest first: place, measure, before → after, the commit range from the last main session before the change to its first session, and the PRs merged in it, oldest first. The PRs are what the agent stored with the main run set at ingest (git log --first-parent); null ("PRs not known") when no stored list reaches the session before. Main's own history, never a PR verdict. limit ≤ 200. CLI: keepsnappy main --journey KEY --phone MODEL --changes. | GET /v1/main/changes | | get_investigation | The investigation package of a finding, from stored results only: the lane's verdict as stored, the worse places with main and PR values, the components to check, the problems, the measured commits between main and the PR, a main finding's change with its PRs. Lists say their total when cut; digest names what was read. form: "short" keeps the counts, the verdict and the digest. The structured result is the whole package. The skill docs/skills/keepsnappy-investigate/SKILL.md uses it. | GET /v1/findings/:id/investigation | | submit_hypotheses | Stores up to ten hypotheses about a finding: predictions with a measurable expected change, never a verdict. The submission (with the package's package_digest) is checked against the one hypothesis schema (@keepsnappy/dashboard-contract/hypotheses) before anything is sent. A read or run key; the same submission_id stores nothing twice, and the same id with other content is a conflict. The prose (claim, evidence, risk notes, moved cost, prior conflict, magnitude hint) may not contain a word of the verdict (Worse, Slightly worse, Better, No change); Can’t tell is allowed. | POST /v1/findings/:id/hypotheses | | explain_layers | Which of KeepSnappy's layers answers which question, and each layer's certificate (none issued yet). | GET /v1/layers |

Besides measures, a run in which the phone heated up (iOS thermal state nominal or fair at the start, serious or worse at its peak, or critical from a lower state) is a finding of its own (kind: "heat", layer 3), invalid runs included (REQ-MET-20). The comparison warning "Phone heats up" is a measure finding whose values are shares of cool-started runs.

An empty list is a clean result only when measured.newest exists and was judged: the answer's first line says "No KeepSnappy measurement on branch X" or "the newest run set was not judged" otherwise. Query parameters the API does not know are dropped, not refused (there is no ?commit= filter).

Every tool returns the text the CLI prints and the API's JSON as structured content. The answer tools (pr_status, list_findings, list_problems, get_finding, session_places, main_sessions, main_vs_previous, place_trend, window_history) take form: full (the default, the whole text) or short (the CLI's --short). Short is the full text with lines left out, in the same order, and ends with one line of its own that says what it left out (with counts); nothing left out, no line. Each tool's description says what its short form leaves out. The JSON is the same in both forms. Every finding names its layer (4 = PR vs main, 5 = main over time), its certificate (null until plan 51 certifies the layer) and its limits.

Words

The one word table of packages/present/src/states.ts (REQ-VER-24): Worse / Slightly worse / Better / No change / Can’t tell, and "not blocking" as a badge on a Worse measured differently from main ("Worse (not blocking)"). The evidence ranks likely causes; it does not prove them.

Develop

bun install
bunx vitest run        # the server against a fake API serving findings built by the API's own finding module
bunx tsc --noEmit
KEEPSNAPPY_KEY=… bun src/bin.ts   # stdio