@keepsnappy/mcp 0.1.0-alpha.11 → 0.1.0-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/keepsnappy-mcp.js +14 -14
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ KeepSnappy's MCP server for coding agents (Claude Code and others): it answers "
|
|
|
4
4
|
what do I do" from the performance findings KeepSnappy measured on real phones. Read-only: it never starts a measurement,
|
|
5
5
|
writes or recomputes anything (plan 48 §4).
|
|
6
6
|
|
|
7
|
-
It gives the same answers as the `
|
|
7
|
+
It gives the same answers as the `keepsnappy` CLI (`keepsnappy findings | finding | compare | history | commits | layers`):
|
|
8
8
|
both use this package's API client and answer layout, so a person and an agent read the same words.
|
|
9
9
|
|
|
10
10
|
## Set it up in an app repo (TapIn)
|
|
@@ -12,26 +12,26 @@ both use this package's API client and answer layout, so a person and an agent r
|
|
|
12
12
|
1. Ask the KeepSnappy owner for a project key with **read** scope (`bun run create-key -- <org> <app> read` in
|
|
13
13
|
`apps/api`; it refuses an org or app that does not exist). Setup → Keys on the dashboard lists read keys and revokes
|
|
14
14
|
them. A CI key (`run` scope) works too; an SDK key (`ingest`) does not.
|
|
15
|
-
2. Put the key in your shell environment, not in the repo: `export
|
|
15
|
+
2. Put the key in your shell environment, not in the repo: `export KEEPSNAPPY_KEY=pl_rk_…` (for example in `~/.zshenv`).
|
|
16
16
|
3. Add `.mcp.json` at the repo root:
|
|
17
17
|
|
|
18
18
|
```json
|
|
19
19
|
{
|
|
20
20
|
"mcpServers": {
|
|
21
|
-
"
|
|
21
|
+
"keepsnappy": {
|
|
22
22
|
"command": "npx",
|
|
23
23
|
"args": ["-y", "@keepsnappy/mcp"],
|
|
24
24
|
"env": {
|
|
25
|
-
"
|
|
26
|
-
"
|
|
25
|
+
"KEEPSNAPPY_KEY": "${KEEPSNAPPY_KEY}",
|
|
26
|
+
"KEEPSNAPPY_API": "https://keepsnappy-api-922918702810.europe-west1.run.app"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
Claude Code expands `${
|
|
34
|
-
From a KeepSnappy checkout instead of npm: `"command": "bun", "args": ["<
|
|
33
|
+
Claude Code expands `${KEEPSNAPPY_KEY}` from your environment. `KEEPSNAPPY_API` is optional (the hosted API is the default).
|
|
34
|
+
From a KeepSnappy checkout instead of npm: `"command": "bun", "args": ["<keepsnappy>/packages/mcp/src/bin.ts"]`.
|
|
35
35
|
|
|
36
36
|
## Tools
|
|
37
37
|
|
|
@@ -67,5 +67,5 @@ Can't compare / Not enough data yet**, and "not blocking" as a badge on a Worse
|
|
|
67
67
|
bun install
|
|
68
68
|
bunx vitest run # the server against a fake API serving findings built by the API's own finding module
|
|
69
69
|
bunx tsc --noEmit
|
|
70
|
-
|
|
70
|
+
KEEPSNAPPY_KEY=… bun src/bin.ts # stdio
|
|
71
71
|
```
|
package/dist/keepsnappy-mcp.js
CHANGED
|
@@ -14053,7 +14053,7 @@ class StdioServerTransport {
|
|
|
14053
14053
|
}
|
|
14054
14054
|
|
|
14055
14055
|
// packages/mcp/src/client.ts
|
|
14056
|
-
var DEFAULT_API_URL = "https://keepsnappy-api-
|
|
14056
|
+
var DEFAULT_API_URL = "https://keepsnappy-api-922918702810.europe-west1.run.app";
|
|
14057
14057
|
|
|
14058
14058
|
class ApiError extends Error {
|
|
14059
14059
|
status;
|
|
@@ -14070,13 +14070,13 @@ async function get(t, path, query = {}) {
|
|
|
14070
14070
|
for (const [k, v] of Object.entries(query))
|
|
14071
14071
|
if (v !== undefined && v !== "")
|
|
14072
14072
|
u.searchParams.set(k, String(v));
|
|
14073
|
-
const r = await (t.fetch ?? fetch)(u, { headers: { "x-
|
|
14073
|
+
const r = await (t.fetch ?? fetch)(u, { headers: { "x-keepsnappy-key": t.key, accept: "application/json" } });
|
|
14074
14074
|
const text = await r.text();
|
|
14075
14075
|
const body = text ? JSON.parse(text) : {};
|
|
14076
14076
|
if (!r.ok) {
|
|
14077
14077
|
const code = typeof body.error === "string" ? body.error : String(r.status);
|
|
14078
14078
|
const message = typeof body.message === "string" ? body.message : code;
|
|
14079
|
-
const hint = r.status === 401 ? " (check KEEPSNAPPY_KEY
|
|
14079
|
+
const hint = r.status === 401 ? " (check KEEPSNAPPY_KEY)" : r.status === 403 ? " (the key needs the read or run scope)" : "";
|
|
14080
14080
|
throw new ApiError(r.status, code, `${message}${hint}`);
|
|
14081
14081
|
}
|
|
14082
14082
|
return body;
|
|
@@ -22064,20 +22064,20 @@ var MEASURES = {
|
|
|
22064
22064
|
worse: "none",
|
|
22065
22065
|
at: ["screen", "launch", "press", "drag", "other"],
|
|
22066
22066
|
producer: "derive",
|
|
22067
|
-
label: "Time of the
|
|
22068
|
-
short: "time of the
|
|
22067
|
+
label: "Time of the KeepSnappy SDK in the window",
|
|
22068
|
+
short: "time of the KeepSnappy SDK",
|
|
22069
22069
|
decimals: 1,
|
|
22070
|
-
what: "Time the
|
|
22070
|
+
what: "Time the KeepSnappy SDK spent on the JavaScript thread after each screen update in the window (reading the update, its profile and, when on, the React export); evidence only, never decides a result. Clock time, so it also holds pauses for garbage collection or other threads while the SDK ran. Not known when the window had more updates than the SDK keeps times for"
|
|
22071
22071
|
},
|
|
22072
22072
|
sdk_hook_commits: {
|
|
22073
22073
|
unit: "count",
|
|
22074
22074
|
worse: "none",
|
|
22075
22075
|
at: ["screen", "launch", "press", "drag", "other"],
|
|
22076
22076
|
producer: "derive",
|
|
22077
|
-
label: "Screen updates the
|
|
22077
|
+
label: "Screen updates the KeepSnappy SDK read",
|
|
22078
22078
|
short: "screen updates the SDK read",
|
|
22079
22079
|
decimals: 0,
|
|
22080
|
-
what: "How many screen updates the
|
|
22080
|
+
what: "How many screen updates the KeepSnappy SDK read in the window, the ones its time is summed over"
|
|
22081
22081
|
},
|
|
22082
22082
|
sdk_hook_capped: {
|
|
22083
22083
|
unit: "count",
|
|
@@ -22087,17 +22087,17 @@ var MEASURES = {
|
|
|
22087
22087
|
label: "Windows with too many updates to time the SDK",
|
|
22088
22088
|
short: "windows the SDK time is not known for",
|
|
22089
22089
|
decimals: 0,
|
|
22090
|
-
what: "Windows of this place with more screen updates than the
|
|
22090
|
+
what: "Windows of this place with more screen updates than the KeepSnappy SDK keeps times for (5000), so its time there is not given"
|
|
22091
22091
|
},
|
|
22092
22092
|
js_thread_app_ms: {
|
|
22093
22093
|
unit: "ms",
|
|
22094
22094
|
worse: "none",
|
|
22095
22095
|
at: ["screen", "launch", "press", "drag", "other"],
|
|
22096
22096
|
producer: "derive",
|
|
22097
|
-
label: "JavaScript thread CPU time without the
|
|
22097
|
+
label: "JavaScript thread CPU time without the KeepSnappy SDK",
|
|
22098
22098
|
short: "JavaScript thread time without the SDK",
|
|
22099
22099
|
decimals: 0,
|
|
22100
|
-
what: "JavaScript thread CPU time in the window minus the time the
|
|
22100
|
+
what: "JavaScript thread CPU time in the window minus the time the KeepSnappy SDK spent after screen updates (not below 0); evidence only, never decides a result. Approximate either way: the thread time is CPU time in 10 ms steps on Android, the SDK time is clock time and includes pauses inside it"
|
|
22101
22101
|
},
|
|
22102
22102
|
main_thread_ms: {
|
|
22103
22103
|
unit: "ms",
|
|
@@ -23190,7 +23190,7 @@ function createServer(target) {
|
|
|
23190
23190
|
title: "List performance findings",
|
|
23191
23191
|
description: "What got worse (Worse / Warning) or better in the app, worst first, as KeepSnappy measured it on real phones. The answer starts with whether and when the branch was measured and how its newest run set read: an empty list on a branch that was never measured, or whose newest run set was not judged, is not a clean result. Filter by a PR branch or main, journey, phone, and state (present by default; not-judged = the newest run set did not judge it, so it may still be there). Each finding says what changed, where, how sure, and has an id for get_finding. Page with offset.",
|
|
23192
23192
|
inputSchema: {
|
|
23193
|
-
branch: string2().optional().describe("A PR branch, e.g. feat/
|
|
23193
|
+
branch: string2().optional().describe("A PR branch, e.g. feat/keepsnappy-integration"),
|
|
23194
23194
|
main: boolean2().optional().describe("Findings of main over time instead of a branch"),
|
|
23195
23195
|
journey: string2().optional().describe("Journey key, e.g. live-ui"),
|
|
23196
23196
|
phone: string2().optional().describe('Part of the phone model, e.g. "iPhone 15"'),
|
|
@@ -23239,11 +23239,11 @@ function createServer(target) {
|
|
|
23239
23239
|
}
|
|
23240
23240
|
|
|
23241
23241
|
// packages/mcp/src/bin.ts
|
|
23242
|
-
var key = process.env.KEEPSNAPPY_KEY
|
|
23242
|
+
var key = process.env.KEEPSNAPPY_KEY;
|
|
23243
23243
|
if (!key) {
|
|
23244
23244
|
process.stderr.write(`keepsnappy-mcp: set KEEPSNAPPY_KEY to a KeepSnappy project key with read scope
|
|
23245
23245
|
`);
|
|
23246
23246
|
process.exit(2);
|
|
23247
23247
|
}
|
|
23248
|
-
var server = createServer({ url: process.env.KEEPSNAPPY_API ??
|
|
23248
|
+
var server = createServer({ url: process.env.KEEPSNAPPY_API ?? DEFAULT_API_URL, key });
|
|
23249
23249
|
await server.connect(new StdioServerTransport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keepsnappy/mcp",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.13",
|
|
4
4
|
"description": "KeepSnappy MCP server (stdio, read-only): performance findings, compare, window history and commits for coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
"node": ">=22"
|
|
17
17
|
},
|
|
18
18
|
"bin": {
|
|
19
|
-
"keepsnappy-mcp": "dist/keepsnappy-mcp.js"
|
|
20
|
-
"perflab-mcp": "dist/keepsnappy-mcp.js"
|
|
19
|
+
"keepsnappy-mcp": "dist/keepsnappy-mcp.js"
|
|
21
20
|
},
|
|
22
21
|
"files": [
|
|
23
22
|
"dist",
|