mcp-context-cost 0.3.0 → 0.5.0
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 +202 -43
- package/dist/audit/audit.d.ts +101 -0
- package/dist/audit/audit.js +492 -16
- package/dist/audit/config.d.ts +38 -0
- package/dist/audit/config.js +64 -0
- package/dist/audit/deferral.d.ts +346 -0
- package/dist/audit/deferral.js +376 -0
- package/dist/audit/diff.d.ts +124 -0
- package/dist/audit/diff.js +318 -0
- package/dist/audit/run.d.ts +34 -0
- package/dist/audit/run.js +45 -2
- package/dist/cli.d.ts +21 -0
- package/dist/cli.js +141 -7
- package/dist/core/adoption.d.ts +226 -0
- package/dist/core/adoption.js +432 -0
- package/dist/core/canonical.d.ts +6 -0
- package/dist/core/canonical.js +3 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/session-start.d.ts +102 -0
- package/dist/core/session-start.js +186 -0
- package/dist/core/types.d.ts +8 -0
- package/dist/sweep/client.d.ts +6 -1
- package/dist/sweep/client.js +1 -0
- package/dist/sweep/dashboard.d.ts +18 -0
- package/dist/sweep/dashboard.js +74 -10
- package/dist/sweep/docker.d.ts +31 -0
- package/dist/sweep/docker.js +20 -11
- package/dist/sweep/harness-guard.d.ts +57 -0
- package/dist/sweep/harness-guard.js +144 -0
- package/dist/sweep/history.d.ts +33 -1
- package/dist/sweep/history.js +60 -5
- package/dist/sweep/regen.js +6 -1
- package/dist/sweep/report.d.ts +18 -0
- package/dist/sweep/report.js +79 -5
- package/dist/sweep/run.d.ts +43 -0
- package/dist/sweep/run.js +135 -37
- package/dist/sweep/server-pages.js +31 -6
- package/dist/sweep/session-start.d.ts +3 -0
- package/dist/sweep/session-start.js +103 -0
- package/dist/sweep/shard.d.ts +41 -0
- package/dist/sweep/shard.js +58 -0
- package/dist/sweep/sweep-all.js +57 -2
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -3,37 +3,12 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/mcp-context-cost)
|
|
4
4
|
[](https://github.com/athakur3/mcp-context-cost/actions/workflows/ci.yml)
|
|
5
5
|
|
|
6
|
-
**
|
|
7
|
-
|
|
6
|
+
**What do the MCP servers in your config cost you before you type anything — and what did
|
|
7
|
+
that last config change add to every session you will ever run?**
|
|
8
8
|
|
|
9
|
-
Every MCP server you wire into an agent injects its tool schemas into the model's context
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
| server | context cost | tools |
|
|
14
|
-
|---|---:|---:|
|
|
15
|
-
| github (official) | **54,422 tokens** | 44 |
|
|
16
|
-
| brave-search | 25,456 | 8 |
|
|
17
|
-
| notion | 17,500 | 24 |
|
|
18
|
-
| playwright *(4.8M installs/week)* | 4,024 | 24 |
|
|
19
|
-
| filesystem (reference) | 2,823 | 14 |
|
|
20
|
-
| markitdown | 64 | 1 |
|
|
21
|
-
|
|
22
|
-
*(57 of 82 popular servers measured, 2026-08-16 sweep — full table in
|
|
23
|
-
[results/leaderboard.md](results/leaderboard.md); every failure is listed with its reason.
|
|
24
|
-
Each measured server also has a [detail page](https://athakur3.github.io/mcp-context-cost/servers/)
|
|
25
|
-
showing which tools its tokens are in.)*
|
|
26
|
-
|
|
27
|
-
This project makes that cost **legible and disputable**:
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
[context cost | 12,430 tokens] ← shields.io badge, linked to the methodology
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## What does *your* setup cost?
|
|
34
|
-
|
|
35
|
-
The leaderboard measures one server at a time. You don't run one server — you run a stack.
|
|
36
|
-
Point `audit` at your own MCP config and it measures every server you actually have installed:
|
|
9
|
+
Every MCP server you wire into an agent injects its tool schemas into the model's context on
|
|
10
|
+
every single request. You pay that whether or not the agent ends up using the tools, and no
|
|
11
|
+
client shows you the number. Point `audit` at your own MCP config:
|
|
37
12
|
|
|
38
13
|
```bash
|
|
39
14
|
npx -y mcp-context-cost audit
|
|
@@ -49,12 +24,18 @@ claude-desktop ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
|
49
24
|
────────────────────────────────────────────
|
|
50
25
|
total 37 7,901
|
|
51
26
|
|
|
52
|
-
|
|
53
|
-
|
|
27
|
+
7,901 tokens of tool schemas — 4.0% of a 200,000-token context window.
|
|
28
|
+
No default deferral is on record for claude-desktop, so every request
|
|
29
|
+
carries these tokens before you type anything — an absence of a record
|
|
30
|
+
about the client, not a measurement of it.
|
|
54
31
|
|
|
55
32
|
heaviest tools
|
|
56
33
|
sequential-thinking · sequentialthinking 990
|
|
57
34
|
memory · search_nodes 323
|
|
35
|
+
|
|
36
|
+
trim: disabling 3 tools (sequential-thinking·sequentialthinking, memory·search_nodes,
|
|
37
|
+
memory·open_nodes) would recover 1,635 tokens (20.7% of this config) — if your client
|
|
38
|
+
supports per-tool filtering.
|
|
58
39
|
```
|
|
59
40
|
|
|
60
41
|
It finds configs for Claude Desktop, Claude Code (`~/.claude.json`, `.mcp.json`), Cursor,
|
|
@@ -67,6 +48,72 @@ Totals are reported per config file, never merged: a context window belongs to o
|
|
|
67
48
|
session, so summing Cursor's servers into Claude Desktop's total would describe a session
|
|
68
49
|
nobody runs.
|
|
69
50
|
|
|
51
|
+
### Where this cost is paid in full, and where it is deferred away
|
|
52
|
+
|
|
53
|
+
Not every client puts every tool definition in context on every request, so the total above
|
|
54
|
+
is not automatically your bill. Which client reads the config, and how that client is
|
|
55
|
+
configured **on this machine**, decides it — and `audit` reads that rather than assuming it.
|
|
56
|
+
|
|
57
|
+
**Clients with no default deferral on record** — Claude Desktop, Cursor, VS Code, Windsurf.
|
|
58
|
+
The total is what every request carries, as in the example above. That sentence is an
|
|
59
|
+
absence of a record about those clients, not a measurement of them, and the report says so
|
|
60
|
+
in those words.
|
|
61
|
+
|
|
62
|
+
**Claude Code defers MCP tool definitions by default** (its **tool search**): they are not
|
|
63
|
+
in context at session start, and load when the model reaches for one. Three variables move
|
|
64
|
+
that, and `audit` reads all three — from the shell it runs in *and* from the `env` block of
|
|
65
|
+
Claude Code's own settings files (managed, `.claude/settings.local.json`,
|
|
66
|
+
`.claude/settings.json`, `~/.claude/settings.json`), because a machine that switched
|
|
67
|
+
deferral off in a settings file is not a machine running the default:
|
|
68
|
+
|
|
69
|
+
| setting | what the audit reports |
|
|
70
|
+
|---|---|
|
|
71
|
+
| nothing set (the default) | every definition deferred, at any size — no threshold applies |
|
|
72
|
+
| `ENABLE_TOOL_SEARCH=true` | same: every definition deferred |
|
|
73
|
+
| `ENABLE_TOOL_SEARCH=false` | deferral off — every request carries the full total |
|
|
74
|
+
| `ENABLE_TOOL_SEARCH=auto` / `auto:N` | deferred only once definitions reach 10% / N% of the context window |
|
|
75
|
+
| `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` set | tool search off — read first, because `ENABLE_TOOL_SEARCH` cannot override it |
|
|
76
|
+
| `ANTHROPIC_BASE_URL` off `api.anthropic.com` | falls back to loading up front — consulted only while `ENABLE_TOOL_SEARCH` is unset |
|
|
77
|
+
| anything else in `ENABLE_TOOL_SEARCH` | not a documented value, so nothing is claimed from it |
|
|
78
|
+
|
|
79
|
+
On a machine where none of them is set, the same stack reads:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
7,901 tokens of tool schemas — 4.0% of a 200,000-token context window.
|
|
83
|
+
claude-code defers every MCP tool definition (tool search), with no threshold —
|
|
84
|
+
ENABLE_TOOL_SEARCH is unset here, which is the documented default. These tokens are NOT loaded
|
|
85
|
+
up front at any size; they load when the model reaches for a tool. Size
|
|
86
|
+
decides nothing here, so none of the arithmetic above changes the answer.
|
|
87
|
+
Where this was read — Claude Code takes these variables from the shell it
|
|
88
|
+
starts in and from the env block of its own settings files:
|
|
89
|
+
this shell — sets none of them
|
|
90
|
+
4 other settings file(s) it reads are not on this machine
|
|
91
|
+
The full number is paid where deferral does not apply:
|
|
92
|
+
a Microsoft Foundry deployment hosted on Azure, which rejects tool search server-side
|
|
93
|
+
Google Cloud's Agent Platform on a model earlier than the Claude 4.5 generation
|
|
94
|
+
a model without support for tool_reference blocks (before Sonnet 4.5 / Haiku 4.5 / Opus 4.5)
|
|
95
|
+
a server pinned with "alwaysLoad": true, whose tools load at session start regardless
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Set `ENABLE_TOOL_SEARCH=false` in that shell and the same config reports the opposite —
|
|
99
|
+
`loads every tool definition up front here`, naming the variable and the place it was read
|
|
100
|
+
from. Deferring is also not free: what a deferring client *does* load at session start —
|
|
101
|
+
tool names plus the server's `instructions` — is measured per server and published in the
|
|
102
|
+
leaderboard's `session start` column, and for at least one server in the published set it
|
|
103
|
+
costs **more** than loading the definitions would.
|
|
104
|
+
|
|
105
|
+
Three things the report will not do: it will not convert between units silently (in
|
|
106
|
+
threshold mode the stack is compared as a range, because the audit counts wire bytes and the
|
|
107
|
+
threshold is counted in what the client sends to the API — measured at 0.20×–1.92× across 20
|
|
108
|
+
servers); it will not pick a winner when two places on the machine set the same variable to
|
|
109
|
+
different values, or when a settings file exists and cannot be read; and it will not pass an
|
|
110
|
+
absence of a record off as a measurement. The first two print as unanswered questions. The
|
|
111
|
+
third prints as an answer that names itself: for the four discovered clients with no default
|
|
112
|
+
on record — `claude-desktop`, `cursor`, `vscode`, `windsurf` — the tokens are counted as
|
|
113
|
+
loaded up front, and the report says so in those words, "an absence of a record about the
|
|
114
|
+
client, not a measurement of it".
|
|
115
|
+
Full model, sources and dates: [METHODOLOGY §who pays the number](docs/METHODOLOGY.md#who-pays).
|
|
116
|
+
|
|
70
117
|
**In CI**, make it a gate — the bundlesize move for agents:
|
|
71
118
|
|
|
72
119
|
```bash
|
|
@@ -74,8 +121,107 @@ npx -y mcp-context-cost audit --config .mcp.json --budget 20000
|
|
|
74
121
|
# exits 1 when the stack exceeds the budget, so a PR adding a 25K-token server fails
|
|
75
122
|
```
|
|
76
123
|
|
|
77
|
-
|
|
78
|
-
|
|
124
|
+
The budget is an absolute ceiling. What a reviewer actually wants to know is what *this pull
|
|
125
|
+
request* did, so record a baseline and diff against it:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx -y mcp-context-cost audit --config .mcp.json --json > baseline.json # on main
|
|
129
|
+
npx -y mcp-context-cost audit --config .mcp.json --baseline baseline.json --max-increase 2000
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
diff vs baseline measured 2026-08-18T01:51:49.555Z (methodology 1.0)
|
|
134
|
+
|
|
135
|
+
.mcp.json
|
|
136
|
+
2,378 → 5,201 +2,823
|
|
137
|
+
|
|
138
|
+
added filesystem — → 2,823 +2,823
|
|
139
|
+
(1 server unchanged)
|
|
140
|
+
|
|
141
|
+
This change adds 2,823 tokens to every request in this client — 1.2% → 2.6% of a
|
|
142
|
+
200,000-token context window.
|
|
143
|
+
|
|
144
|
+
INCREASE FAIL:
|
|
145
|
+
.mcp.json: +2,823 tokens per request, over the 2,000 allowed
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
> **Version note.** `--baseline` and `--max-increase` shipped in **0.4.0** (published
|
|
149
|
+
> 2026-08-18, and the current `latest`), so the command above gates on
|
|
150
|
+
> `npx -y mcp-context-cost@latest`. Pinning to **0.3.0 or earlier** does not gate, and fails
|
|
151
|
+
> quietly: those builds ignore flags they do not recognise, so the same command produces a
|
|
152
|
+
> plain audit and **exit 0** — a passing CI check on a gate that never ran. 0.4.0 rejects
|
|
153
|
+
> unknown flags with exit 2 instead. Pin at or above 0.4.0, or do not pin.
|
|
154
|
+
|
|
155
|
+
A baseline is just a stored `audit --json` report, so any artifact store works. Without
|
|
156
|
+
`--max-increase` the diff is informational and the exit code is unchanged.
|
|
157
|
+
|
|
158
|
+
[`examples/github-actions.yml`](examples/github-actions.yml) is the whole thing as a workflow:
|
|
159
|
+
measure the base branch, measure the PR, fail on the difference.
|
|
160
|
+
|
|
161
|
+
`--max-increase` fails on more than the number — it also fails whenever the increase could
|
|
162
|
+
not be established. A server that measured yesterday and won't start today takes its tokens
|
|
163
|
+
out of the total in exactly the way uninstalling it would, and reporting that as a saving is
|
|
164
|
+
the one mistake this tool must not make. So a server that crossed the measured/unmeasured
|
|
165
|
+
line, a config with no baseline, or a baseline config this run never found each fail the
|
|
166
|
+
gate and name themselves:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
Not a clean comparison: a server changed measured-ness between the two runs.
|
|
170
|
+
The measured total moved −2,378, but that is not what your config did.
|
|
171
|
+
|
|
172
|
+
memory: measured 2,378 in the baseline and could not be measured now — its cost is
|
|
173
|
+
missing from the total, not gone from your config
|
|
174
|
+
→ true cost is at least 2,378 higher than the 0 measured now.
|
|
175
|
+
|
|
176
|
+
INCREASE FAIL:
|
|
177
|
+
.mcp.json: a server changed measured-ness, so the change could not be established exactly
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Add `--claude` to annotate each server with its Anthropic-request cost from the published
|
|
181
|
+
[Claude divergence](docs/METHODOLOGY.md#claude-divergence) run — an exact number when the
|
|
182
|
+
published capture hash matches what you have installed, `—` (silence, not a stale guess)
|
|
183
|
+
when it doesn't (today the run covers the top 20 measured servers, so most installs will
|
|
184
|
+
show a mix):
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
server tools tokens share claude
|
|
188
|
+
github 44 54,422 95.8% 18,406
|
|
189
|
+
memory 9 2,378 4.2% —
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Flags: `--json` (full report on stdout, progress on stderr), `--budget N`,
|
|
193
|
+
`--baseline <report.json>`, `--max-increase N`, `--context N` (default 200,000),
|
|
194
|
+
`--timeout ms`, `--concurrency N`, `--docker`, `--claude`.
|
|
195
|
+
|
|
196
|
+
## Where the numbers come from
|
|
197
|
+
|
|
198
|
+
The number `audit` gives you is the same measurement, run across a curated set of public
|
|
199
|
+
servers — which is how you can tell it is a measurement and not this tool's opinion. It also
|
|
200
|
+
shows what you are choosing between: across the 69 servers measured, cost spans **1,700×**,
|
|
201
|
+
from the 32-token `postgres` reference server to github's 54,422. The table below starts at
|
|
202
|
+
markitdown's 64 tokens, an 850× spread; the full range is in
|
|
203
|
+
[results/leaderboard.md](results/leaderboard.md).
|
|
204
|
+
|
|
205
|
+
| server | context cost | tools |
|
|
206
|
+
|---|---:|---:|
|
|
207
|
+
| github (official) | **54,422 tokens** | 44 |
|
|
208
|
+
| brave-search | 25,456 | 8 |
|
|
209
|
+
| notion | 17,500 | 24 |
|
|
210
|
+
| playwright *(4.8M installs/week)* | 4,024 | 24 |
|
|
211
|
+
| filesystem (reference) | 2,823 | 14 |
|
|
212
|
+
| markitdown | 64 | 1 |
|
|
213
|
+
|
|
214
|
+
*(69 of 82 popular servers measured, sweeps of 2026-08-18 and 2026-08-19 — full table in
|
|
215
|
+
[results/leaderboard.md](results/leaderboard.md); every failure is listed with its reason.
|
|
216
|
+
Each measured server also has a [detail page](https://athakur3.github.io/mcp-context-cost/servers/)
|
|
217
|
+
showing which tools its tokens are in.)*
|
|
218
|
+
|
|
219
|
+
If you publish a server, the same measurement is available as a badge, so your users can see
|
|
220
|
+
the cost before they install rather than after:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
[context cost | 12,430 tokens] ← shields.io badge, linked to the methodology
|
|
224
|
+
```
|
|
79
225
|
|
|
80
226
|
## What it costs on Claude
|
|
81
227
|
|
|
@@ -94,9 +240,9 @@ the method is [Claude divergence](docs/METHODOLOGY.md#claude-divergence).
|
|
|
94
240
|
|
|
95
241
|
## Why trust the number?
|
|
96
242
|
|
|
97
|
-
Every
|
|
98
|
-
SHA-256 of its canonical bytes, the pinned tokenizer (`o200k_base`), and the
|
|
99
|
-
command. Disputes reduce to a byte-level diff:
|
|
243
|
+
Every published number is backed by a `measurement.json` containing the raw `tools/list`
|
|
244
|
+
capture, the SHA-256 of its canonical bytes, the pinned tokenizer (`o200k_base`), and the
|
|
245
|
+
exact launch command. Disputes reduce to a byte-level diff:
|
|
100
246
|
|
|
101
247
|
```bash
|
|
102
248
|
npx -y mcp-context-cost verify results/github/measurement.json
|
|
@@ -110,6 +256,11 @@ Add `--json` for scripting (`{ ok, serverName, rederivedTokens, rederivedSha, pr
|
|
|
110
256
|
on stdout, `badge` omitted on failure). Exit codes: `0` ok, `1` verification/measurement
|
|
111
257
|
failed, `2` usage error.
|
|
112
258
|
|
|
259
|
+
`audit` runs that same code path on your own machine and reports each server's
|
|
260
|
+
`canonicalSha256` in `--json`, so you can check that the version you installed is byte-identical
|
|
261
|
+
to the one that was published — which is exactly what `--claude` uses to decide whether it is
|
|
262
|
+
allowed to show you a number.
|
|
263
|
+
|
|
113
264
|
Full definition: [docs/METHODOLOGY.md](docs/METHODOLOGY.md) — what is counted, what the
|
|
114
265
|
number is *not*, config policy, failure taxonomy, frozen color bands, known divergences.
|
|
115
266
|
|
|
@@ -119,7 +270,7 @@ number is *not*, config policy, failure taxonomy, frozen color bands, known dive
|
|
|
119
270
|
|---|---|
|
|
120
271
|
| `src/core/` | the measurement spec, executable — canonical form, tokenizer, bands, badge JSON |
|
|
121
272
|
| `src/sweep/` | raw-wire MCP stdio client + Dockerized batch sweep + leaderboard/dashboard generators |
|
|
122
|
-
| `src/audit/` | client-config discovery (5 clients, JSONC-tolerant)
|
|
273
|
+
| `src/audit/` | client-config discovery (5 clients, JSONC-tolerant), the per-stack report, and the baseline diff |
|
|
123
274
|
| `src/cli.ts` | `audit` (measure your own stack), `verify` (re-derive any published number), `measure` |
|
|
124
275
|
| `spec/fixtures/` | golden vectors shared by the TypeScript and bash implementations |
|
|
125
276
|
| `tools/` | the one script that calls a network API (Claude divergence); kept out of the package so the library stays offline |
|
|
@@ -146,28 +297,36 @@ Point the link at the measurement behind the number — for servers in this swee
|
|
|
146
297
|
`https://athakur3.github.io/mcp-context-cost/servers/<name>.html`; otherwise the
|
|
147
298
|
methodology page. A badge nobody can audit is decoration.
|
|
148
299
|
|
|
300
|
+
How many projects outside this repository actually display it is a dated reading rather
|
|
301
|
+
than a guess — [docs/adoption.md](docs/adoption.md), regenerated by `npm run adoption`,
|
|
302
|
+
which publishes the queries it ran and every file it examined. A zero there means the
|
|
303
|
+
search ran and found none; if it could not run, it says that instead of publishing a zero.
|
|
304
|
+
|
|
149
305
|
Or self-serve from CI via the (staged) mcp-tokens-action badge inputs — see
|
|
150
306
|
[upstream/action-patch.md](upstream/action-patch.md).
|
|
151
307
|
|
|
152
308
|
## Development
|
|
153
309
|
|
|
154
310
|
```bash
|
|
155
|
-
npm test #
|
|
311
|
+
npm test # 403 TS tests incl. golden fixtures + dispute drills
|
|
156
312
|
npx tsc --noEmit # typecheck
|
|
157
313
|
./upstream/tests/badge-test.sh # 21 bash tests — byte-identical to the TS reference
|
|
158
314
|
npm run sweep:all -- --docker # full curated sweep (Docker isolation)
|
|
159
315
|
```
|
|
160
316
|
|
|
161
|
-
Notable engineering choices: the MCP client is a deliberate ~
|
|
317
|
+
Notable engineering choices: the MCP client is a deliberate ~220-line raw-wire
|
|
162
318
|
implementation (SDK schema-parsing can reorder keys, which would corrupt canonical bytes);
|
|
163
319
|
sweep servers run in credential-free Docker containers with recorded isolation; the badge
|
|
164
320
|
color bands are frozen against the observed distribution of the first full sweep.
|
|
165
321
|
|
|
166
322
|
## Status
|
|
167
323
|
|
|
168
|
-
Active.
|
|
169
|
-
|
|
170
|
-
|
|
324
|
+
Active. 46 of the 69 numbers come from the sweep of 2026-08-19 and 23 from 2026-08-18. Two
|
|
325
|
+
weekly jobs re-measure the set — one reference server (`memory`) every Monday, and a rotating
|
|
326
|
+
sixth of the list every Wednesday, so the full set turns over in six weeks — but treat the
|
|
327
|
+
leaderboard as a dated snapshot rather than a live feed. Badge PRs are open across the
|
|
328
|
+
ecosystem and [sd2k/mcp-tokens-action#5](https://github.com/sd2k/mcp-tokens-action/pull/5)
|
|
329
|
+
proposes the self-serve badge path upstream. See [ROADMAP.md](ROADMAP.md) for what's next —
|
|
171
330
|
contributions welcome, especially new `servers.yaml` entries.
|
|
172
331
|
|
|
173
332
|
MIT © 2026
|
package/dist/audit/audit.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { type DivergenceRun } from '../core/divergence.js';
|
|
1
2
|
import type { Measurement, MeasurementStatus, ToolMeasurement } from '../core/types.js';
|
|
2
3
|
import type { ConfiguredServer, LoadedConfig } from './config.js';
|
|
4
|
+
import { type DeferralVerdict, type ToolSearchEnv, type ToolSearchSource } from './deferral.js';
|
|
5
|
+
import { type AuditDiff, type IncreaseGate } from './diff.js';
|
|
3
6
|
export declare const DEFAULT_CONTEXT_WINDOW = 200000;
|
|
4
7
|
export type AuditStatus = MeasurementStatus | 'remote-not-measurable';
|
|
5
8
|
export interface AuditServerResult {
|
|
@@ -15,6 +18,13 @@ export interface AuditServerResult {
|
|
|
15
18
|
/** Names only — a server's env values never enter a report. */
|
|
16
19
|
envVarNames: string[];
|
|
17
20
|
canonicalSha256?: string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Anthropic-request cost from the published Claude divergence run, only when
|
|
23
|
+
* its captured hash matches this install (`--claude`). `null` means the
|
|
24
|
+
* install doesn't match what was published — silence, not a stale guess.
|
|
25
|
+
* `undefined` means `--claude` wasn't requested at all.
|
|
26
|
+
*/
|
|
27
|
+
claudeTokens?: number | null;
|
|
18
28
|
notes?: string;
|
|
19
29
|
}
|
|
20
30
|
export interface HeaviestTool {
|
|
@@ -22,6 +32,17 @@ export interface HeaviestTool {
|
|
|
22
32
|
tool: string;
|
|
23
33
|
tokens: number;
|
|
24
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* What turning off the heaviest few tools would recover, for clients that let
|
|
37
|
+
* you disable individual tools rather than whole servers (Claude Code's
|
|
38
|
+
* per-tool permission rules, Cursor's per-tool toggles). `null` when there's
|
|
39
|
+
* nothing worth trimming (one tool total, or no measured tokens).
|
|
40
|
+
*/
|
|
41
|
+
export interface TrimAdvice {
|
|
42
|
+
tools: HeaviestTool[];
|
|
43
|
+
recoverableTokens: number;
|
|
44
|
+
recoverableShare: number;
|
|
45
|
+
}
|
|
25
46
|
export interface AuditConfigResult {
|
|
26
47
|
client: string;
|
|
27
48
|
source: string;
|
|
@@ -32,7 +53,47 @@ export interface AuditConfigResult {
|
|
|
32
53
|
servers: AuditServerResult[];
|
|
33
54
|
skipped: AuditServerResult[];
|
|
34
55
|
heaviestTools: HeaviestTool[];
|
|
56
|
+
trimAdvice: TrimAdvice | null;
|
|
57
|
+
/**
|
|
58
|
+
* Whether this client loads the total up front or defers it, and — when the
|
|
59
|
+
* client decides that by a threshold — which side of it this stack is on.
|
|
60
|
+
* Every config carries one: the answer "no deferral is on record for this
|
|
61
|
+
* client" is a reading, not a gap.
|
|
62
|
+
*
|
|
63
|
+
* Configs that one session loads together share a single verdict object, so
|
|
64
|
+
* `deferral.sources` can name more files than this config's own `source`.
|
|
65
|
+
*/
|
|
66
|
+
deferral: DeferralVerdict;
|
|
67
|
+
}
|
|
68
|
+
export interface BudgetFitStep {
|
|
69
|
+
name: string;
|
|
70
|
+
tokens: number;
|
|
71
|
+
/** What the config still costs after removing this one and everything above it. */
|
|
72
|
+
remaining: number;
|
|
35
73
|
}
|
|
74
|
+
export interface BudgetFit {
|
|
75
|
+
/** How far over the limit the worst config starts. */
|
|
76
|
+
overBy: number;
|
|
77
|
+
/** Heaviest-first removals until the remainder fits. Empty if nothing can be removed. */
|
|
78
|
+
drop: BudgetFitStep[];
|
|
79
|
+
keptCount: number;
|
|
80
|
+
keptTokens: number;
|
|
81
|
+
/** False when removing every measured server still would not fit — a limit set too low. */
|
|
82
|
+
feasible: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The smallest heaviest-first set of servers that gets a config under its budget.
|
|
86
|
+
*
|
|
87
|
+
* `--budget` used to print "BUDGET FAIL: 84,455 > 20,000" and stop, which tells a reader
|
|
88
|
+
* they have a problem and nothing about the shape of it. The whole point of the audit
|
|
89
|
+
* surface is that the person running it is the person paying the tokens, and "you are over"
|
|
90
|
+
* is a measurement where "these two are why" is a decision.
|
|
91
|
+
*
|
|
92
|
+
* Heaviest-first is ONE ordering, not a recommendation: this cannot know which servers you
|
|
93
|
+
* need, and dropping by weight will sometimes name the one you cannot live without. That
|
|
94
|
+
* caveat is printed with the result rather than left implied.
|
|
95
|
+
*/
|
|
96
|
+
export declare function planBudgetFit(config: AuditConfigResult, limit: number): BudgetFit;
|
|
36
97
|
export interface AuditReport {
|
|
37
98
|
methodologyVersion: string;
|
|
38
99
|
encoding: 'o200k_base';
|
|
@@ -44,11 +105,35 @@ export interface AuditReport {
|
|
|
44
105
|
worstTotal: number;
|
|
45
106
|
worstSource: string;
|
|
46
107
|
over: boolean;
|
|
108
|
+
/** Present only when over budget: the arithmetic of getting back under it. */
|
|
109
|
+
fit?: BudgetFit;
|
|
47
110
|
};
|
|
111
|
+
/** Present only when a divergence run was supplied (`--claude`). */
|
|
112
|
+
claudeDivergence?: {
|
|
113
|
+
model: string;
|
|
114
|
+
measuredAt: string;
|
|
115
|
+
};
|
|
116
|
+
/** Present only when a baseline report was supplied (`--baseline`). */
|
|
117
|
+
diff?: AuditDiff;
|
|
118
|
+
/** Present only when `--max-increase` was supplied alongside a baseline. */
|
|
119
|
+
increaseGate?: IncreaseGate;
|
|
48
120
|
problems: string[];
|
|
49
121
|
}
|
|
50
122
|
/** Cache key for measurement reuse: the exact argv two configs would spawn. */
|
|
51
123
|
export declare function serverKey(s: ConfiguredServer): string;
|
|
124
|
+
/**
|
|
125
|
+
* The measurement keys that stand for more than one distinct server.
|
|
126
|
+
*
|
|
127
|
+
* `serverKey` is the argv alone, so two entries running the same command under
|
|
128
|
+
* different environments are measured once and both are given that one number.
|
|
129
|
+
* Environment decides what a server serves — `GITHUB_TOOLSETS` on
|
|
130
|
+
* `github-mcp-server` selects which toolsets it lists — so for entries under one
|
|
131
|
+
* of these keys, the number reported is one entry's, not each one's.
|
|
132
|
+
*
|
|
133
|
+
* Same argv AND same environment is not collapsed: two clients pointing at an
|
|
134
|
+
* identical server are one measurement, which is the reuse this key is for.
|
|
135
|
+
*/
|
|
136
|
+
export declare function collapsedKeys(configs: LoadedConfig[]): Set<string>;
|
|
52
137
|
/**
|
|
53
138
|
* Assemble the report from configs + measurements. Pure: `runAudit` does the
|
|
54
139
|
* spawning, this does the arithmetic, so totals and shares are testable without
|
|
@@ -58,6 +143,22 @@ export declare function buildReport(configs: LoadedConfig[], measured: Map<strin
|
|
|
58
143
|
contextWindow?: number;
|
|
59
144
|
budget?: number;
|
|
60
145
|
generatedAt?: string;
|
|
146
|
+
/** Published `tools-delta/v1` run to join against (`--claude`); omit to skip the join. */
|
|
147
|
+
divergence?: DivergenceRun | null;
|
|
148
|
+
/**
|
|
149
|
+
* The audited machine's SHELL tool-search variables. Passed in rather than
|
|
150
|
+
* read here so this stays pure and a report is reproducible from its
|
|
151
|
+
* inputs; `runAudit` supplies the real environment. Omitted means the shell
|
|
152
|
+
* set nothing.
|
|
153
|
+
*/
|
|
154
|
+
env?: ToolSearchEnv;
|
|
155
|
+
/**
|
|
156
|
+
* The other place those variables come from: Claude Code's own settings
|
|
157
|
+
* files, highest precedence first, as `loadSettingsSources` read them.
|
|
158
|
+
* `runAudit` supplies these. Omitted means they were not read here — which
|
|
159
|
+
* the report says, rather than reporting a default it did not establish.
|
|
160
|
+
*/
|
|
161
|
+
settings?: ToolSearchSource[];
|
|
61
162
|
}): AuditReport;
|
|
62
163
|
/** Human output. JSON output is the report object itself. */
|
|
63
164
|
export declare function formatReport(report: AuditReport): string;
|