mcp-baggage 0.1.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/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/bin.d.ts +9 -0
- package/dist/bin.js +14 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +34 -0
- package/dist/cli.js +215 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +163 -0
- package/dist/config.js.map +1 -0
- package/dist/exact.d.ts +23 -0
- package/dist/exact.js +69 -0
- package/dist/exact.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +50 -0
- package/dist/mcp.js +288 -0
- package/dist/mcp.js.map +1 -0
- package/dist/report.d.ts +52 -0
- package/dist/report.js +166 -0
- package/dist/report.js.map +1 -0
- package/dist/tokens.d.ts +31 -0
- package/dist/tokens.js +67 -0
- package/dist/tokens.js.map +1 -0
- package/dist/toml.d.ts +11 -0
- package/dist/toml.js +145 -0
- package/dist/toml.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/usage.d.ts +43 -0
- package/dist/usage.js +119 -0
- package/dist/usage.js.map +1 -0
- package/dist/weigh.d.ts +41 -0
- package/dist/weigh.js +79 -0
- package/dist/weigh.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mtalhasahin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Baggage
|
|
2
|
+
|
|
3
|
+
**What each MCP server carries into your context window, and how much of it you actually use.**
|
|
4
|
+
|
|
5
|
+
Installing an MCP server is free. Keeping one is not. Every tool a server declares is sent to the
|
|
6
|
+
model on every single request — its name, its description and its whole JSON schema — whether or not
|
|
7
|
+
anything ever calls it. Ten servers is a few thousand tokens off the top of every turn, in every
|
|
8
|
+
session, forever. Nobody shows you that number.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
npx mcp-baggage
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
server tools tokens of ctx used
|
|
16
|
+
|
|
17
|
+
playwright 24 8.9k 4.5% 0/24 never called
|
|
18
|
+
github 35 6.1k 3.1% 3/35
|
|
19
|
+
filesystem 14 2.4k 1.2% 9/14
|
|
20
|
+
sentry 12 1.4k 0.7% 1/12
|
|
21
|
+
postgres 6 702 0.4% 4/6
|
|
22
|
+
broken — — — — server exited (1): MCP_TOKEN is not set
|
|
23
|
+
|
|
24
|
+
total 91 19.5k 9.8%
|
|
25
|
+
|
|
26
|
+
every request carries 19.5k tokens of tool definitions — 9.8% of a 200k window,
|
|
27
|
+
before a line of your own code is read. (estimated — --exact counts)
|
|
28
|
+
|
|
29
|
+
12.7k of it — 65% — has not been called in 30 days.
|
|
30
|
+
|
|
31
|
+
nothing at all has been called from:
|
|
32
|
+
|
|
33
|
+
playwright 8.9k ~/.cursor/mcp.json
|
|
34
|
+
|
|
35
|
+
and these tools, in servers you do use:
|
|
36
|
+
|
|
37
|
+
mcp__github__create_or_update_file 287
|
|
38
|
+
mcp__github__list_workflow_runs 241
|
|
39
|
+
mcp__github__update_pull_request_branch 224
|
|
40
|
+
… and 29 more
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The two halves of that table are the whole idea. The left is what you pay on every request. The
|
|
44
|
+
right is what you got for it. Nobody sets out to spend a tenth of their context on a browser
|
|
45
|
+
automation server they last used in March — it is just that the cost is invisible and the decision
|
|
46
|
+
was never put in front of them.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## What it does
|
|
51
|
+
|
|
52
|
+
- Finds every MCP config on the machine — Claude Code, Cursor, Codex, VS Code, Windsurf — and reads
|
|
53
|
+
whichever exist.
|
|
54
|
+
- Starts each server, asks it what it carries, and closes the connection. **No tool is ever called.**
|
|
55
|
+
- Weighs each tool as the model will be shown it: `mcp__server__tool`, the description and the input
|
|
56
|
+
schema, serialized the way the request serializes it.
|
|
57
|
+
- Reads your Claude Code transcripts for `tool_use` blocks, so every tool gets a count of the times
|
|
58
|
+
it was actually reached for.
|
|
59
|
+
- Folds duplicates: the same server in three configs is weighed once and the report names all three.
|
|
60
|
+
|
|
61
|
+
## What it does not do
|
|
62
|
+
|
|
63
|
+
It does not call tools, write files, change any config, or send your schemas anywhere — except under
|
|
64
|
+
`--exact`, which posts the tool definitions to Anthropic's token-counting endpoint and nothing else.
|
|
65
|
+
It has no dependencies. It spawns the servers your own config already tells your editor to spawn,
|
|
66
|
+
and you can see exactly which ones first:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx mcp-baggage --list
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## The number
|
|
75
|
+
|
|
76
|
+
There is no public tokenizer for Claude, so the default figure is an estimate. It is not
|
|
77
|
+
`length / 4`: tool definitions are JSON, and JSON is mostly punctuation, indentation and short
|
|
78
|
+
identifiers, which that ratio gets wrong in both directions at once. Instead the text is cut into
|
|
79
|
+
runs — letters, digits, whitespace, punctuation — and each is priced the way a byte-pair tokenizer
|
|
80
|
+
is known to treat it.
|
|
81
|
+
|
|
82
|
+
It is a model of a tokenizer, not the tokenizer, and it is good enough for the question it is for:
|
|
83
|
+
*which of these is the big one*. When the number itself has to be right:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ANTHROPIC_API_KEY=... npx mcp-baggage --exact
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
That sends each server's tool definitions to `/v1/messages/count_tokens`, which returns what the
|
|
90
|
+
model would actually be charged — framing, separators and all — and subtracts a baseline call with
|
|
91
|
+
no tools. It runs no inference and bills nothing.
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
mcp-baggage [options]
|
|
97
|
+
|
|
98
|
+
--tools list every tool with its own weight
|
|
99
|
+
--list show the servers found, without starting any of them
|
|
100
|
+
--json machine-readable output
|
|
101
|
+
--exact count with the Anthropic API instead of estimating
|
|
102
|
+
|
|
103
|
+
--days <n> days of transcripts to read for usage (default 30)
|
|
104
|
+
--no-usage skip transcripts entirely
|
|
105
|
+
--window <n> context window to measure the share against (default 200000)
|
|
106
|
+
--client <name> only servers from one client
|
|
107
|
+
--all include servers the config has switched off
|
|
108
|
+
--timeout <ms> how long a server gets to answer (default 20000)
|
|
109
|
+
--cwd <path> treat another directory as the project
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Progress goes to stderr and the report to stdout, so `mcp-baggage --json > baggage.json` gives you
|
|
113
|
+
the figures and still shows you what is happening while a dozen servers start up.
|
|
114
|
+
|
|
115
|
+
## Where it looks
|
|
116
|
+
|
|
117
|
+
| Client | User | Project |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| Claude Code | `~/.claude.json` | `.mcp.json` |
|
|
120
|
+
| Cursor | `~/.cursor/mcp.json` | `.cursor/mcp.json` |
|
|
121
|
+
| VS Code | — | `.vscode/mcp.json` |
|
|
122
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` | — |
|
|
123
|
+
| Codex | `~/.codex/config.toml` | — |
|
|
124
|
+
|
|
125
|
+
Both transports are spoken: stdio, where the server is a process to spawn, and streamable HTTP,
|
|
126
|
+
where it is a URL. A server that cannot be reached is a row in the table with its own error, not a
|
|
127
|
+
failed run — and it is left out of the total rather than quietly counted as zero.
|
|
128
|
+
|
|
129
|
+
Usage counts come from Claude Code's transcripts, because it is the only client that keeps them in a
|
|
130
|
+
documented place. With no transcripts, the `used` column reads `—`. **Unknown is never printed as
|
|
131
|
+
zero**, and a tool that was never measured is never called waste.
|
|
132
|
+
|
|
133
|
+
## In a script
|
|
134
|
+
|
|
135
|
+
The same figures, without the table:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
import { scan } from 'mcp-baggage';
|
|
139
|
+
|
|
140
|
+
const { tokens, servers } = await scan();
|
|
141
|
+
if (tokens > 15_000) {
|
|
142
|
+
console.error(`MCP tool definitions are ${tokens} tokens; budget is 15000`);
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Install
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
npx mcp-baggage # no install
|
|
151
|
+
npm i -g mcp-baggage # or keep it around
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Node 20.10 or newer. No dependencies.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
MIT © [mtalhasahin](https://github.com/mtalhasahin)
|
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The entry point, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* `cli.ts` holds the argument parsing and the run so that both can be tested
|
|
6
|
+
* without a process; this file is the part that cannot be — it takes the exit
|
|
7
|
+
* code and ends.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The entry point, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* `cli.ts` holds the argument parsing and the run so that both can be tested
|
|
6
|
+
* without a process; this file is the part that cannot be — it takes the exit
|
|
7
|
+
* code and ends.
|
|
8
|
+
*/
|
|
9
|
+
import { main } from "./cli.js";
|
|
10
|
+
main(process.argv.slice(2)).then((code) => process.exit(code), (e) => {
|
|
11
|
+
process.stderr.write(`${e instanceof Error ? (e.stack ?? e.message) : String(e)}\n`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,CAAU,EAAE,EAAE;IACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CACF,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command.
|
|
3
|
+
*
|
|
4
|
+
* Runs with no arguments and no configuration: find the configs, ask every
|
|
5
|
+
* server what it carries, read the transcripts for what was used, print one
|
|
6
|
+
* table. Everything else is a flag for a narrower question.
|
|
7
|
+
*
|
|
8
|
+
* Progress goes to stderr and the report to stdout, so `mcp-baggage > out.txt`
|
|
9
|
+
* keeps the report and still shows the person what is happening while a dozen
|
|
10
|
+
* servers start up.
|
|
11
|
+
*/
|
|
12
|
+
type Options = {
|
|
13
|
+
tools: boolean;
|
|
14
|
+
list: boolean;
|
|
15
|
+
json: boolean;
|
|
16
|
+
exact: boolean;
|
|
17
|
+
usage: boolean;
|
|
18
|
+
all: boolean;
|
|
19
|
+
days: number;
|
|
20
|
+
window: number;
|
|
21
|
+
timeout: number;
|
|
22
|
+
client?: string;
|
|
23
|
+
cwd: string;
|
|
24
|
+
};
|
|
25
|
+
/** Flags, with the errors a person can act on rather than a stack trace. */
|
|
26
|
+
export declare function parseArgs(argv: readonly string[]): Options | {
|
|
27
|
+
help: true;
|
|
28
|
+
} | {
|
|
29
|
+
version: true;
|
|
30
|
+
} | {
|
|
31
|
+
error: string;
|
|
32
|
+
};
|
|
33
|
+
export declare function main(argv: readonly string[]): Promise<number>;
|
|
34
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command.
|
|
3
|
+
*
|
|
4
|
+
* Runs with no arguments and no configuration: find the configs, ask every
|
|
5
|
+
* server what it carries, read the transcripts for what was used, print one
|
|
6
|
+
* table. Everything else is a flag for a narrower question.
|
|
7
|
+
*
|
|
8
|
+
* Progress goes to stderr and the report to stdout, so `mcp-baggage > out.txt`
|
|
9
|
+
* keeps the report and still shows the person what is happening while a dozen
|
|
10
|
+
* servers start up.
|
|
11
|
+
*/
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { discover } from "./config.js";
|
|
14
|
+
import { exactly } from "./exact.js";
|
|
15
|
+
import { DEFAULT_TIMEOUT, inspectAll } from "./mcp.js";
|
|
16
|
+
import { asJson, breakdown, DEFAULT_WINDOW, report } from "./report.js";
|
|
17
|
+
import { DEFAULT_DAYS, lookup, readUsage } from "./usage.js";
|
|
18
|
+
import { heaviestFirst, weigh } from "./weigh.js";
|
|
19
|
+
const VERSION = '0.1.0';
|
|
20
|
+
const HELP = `mcp-baggage ${VERSION}
|
|
21
|
+
|
|
22
|
+
What each MCP server carries into your context window, and how much of it
|
|
23
|
+
you actually use.
|
|
24
|
+
|
|
25
|
+
mcp-baggage [options]
|
|
26
|
+
|
|
27
|
+
--tools list every tool with its own weight
|
|
28
|
+
--list show the servers found, without starting any of them
|
|
29
|
+
--json machine-readable output
|
|
30
|
+
--exact count with the Anthropic API instead of estimating
|
|
31
|
+
(needs ANTHROPIC_API_KEY; bills nothing)
|
|
32
|
+
|
|
33
|
+
--days <n> days of transcripts to read for usage (default ${DEFAULT_DAYS})
|
|
34
|
+
--no-usage skip transcripts entirely
|
|
35
|
+
--window <n> context window to measure the share against (default ${DEFAULT_WINDOW})
|
|
36
|
+
--client <name> only servers from one client: claude-code, cursor, codex, vscode, windsurf
|
|
37
|
+
--all include servers the config has switched off
|
|
38
|
+
--timeout <ms> how long a server gets to answer (default ${DEFAULT_TIMEOUT})
|
|
39
|
+
--cwd <path> treat another directory as the project
|
|
40
|
+
--version, --help
|
|
41
|
+
`;
|
|
42
|
+
/** Flags, with the errors a person can act on rather than a stack trace. */
|
|
43
|
+
export function parseArgs(argv) {
|
|
44
|
+
const options = {
|
|
45
|
+
tools: false,
|
|
46
|
+
list: false,
|
|
47
|
+
json: false,
|
|
48
|
+
exact: false,
|
|
49
|
+
usage: true,
|
|
50
|
+
all: false,
|
|
51
|
+
days: DEFAULT_DAYS,
|
|
52
|
+
window: DEFAULT_WINDOW,
|
|
53
|
+
timeout: DEFAULT_TIMEOUT,
|
|
54
|
+
cwd: process.cwd(),
|
|
55
|
+
};
|
|
56
|
+
const number = (raw, flag) => {
|
|
57
|
+
const n = Number(raw);
|
|
58
|
+
return raw !== undefined && Number.isFinite(n) && n > 0 ? n : `${flag} needs a positive number`;
|
|
59
|
+
};
|
|
60
|
+
for (let i = 0; i < argv.length; i++) {
|
|
61
|
+
const arg = argv[i];
|
|
62
|
+
const next = argv[i + 1];
|
|
63
|
+
switch (arg) {
|
|
64
|
+
case '--help':
|
|
65
|
+
case '-h':
|
|
66
|
+
return { help: true };
|
|
67
|
+
case '--version':
|
|
68
|
+
case '-v':
|
|
69
|
+
return { version: true };
|
|
70
|
+
case '--tools':
|
|
71
|
+
options.tools = true;
|
|
72
|
+
break;
|
|
73
|
+
case '--list':
|
|
74
|
+
options.list = true;
|
|
75
|
+
break;
|
|
76
|
+
case '--json':
|
|
77
|
+
options.json = true;
|
|
78
|
+
break;
|
|
79
|
+
case '--exact':
|
|
80
|
+
options.exact = true;
|
|
81
|
+
break;
|
|
82
|
+
case '--no-usage':
|
|
83
|
+
options.usage = false;
|
|
84
|
+
break;
|
|
85
|
+
case '--all':
|
|
86
|
+
options.all = true;
|
|
87
|
+
break;
|
|
88
|
+
case '--days':
|
|
89
|
+
case '--window':
|
|
90
|
+
case '--timeout': {
|
|
91
|
+
const value = number(next, arg);
|
|
92
|
+
if (typeof value === 'string')
|
|
93
|
+
return { error: value };
|
|
94
|
+
if (arg === '--days')
|
|
95
|
+
options.days = value;
|
|
96
|
+
if (arg === '--window')
|
|
97
|
+
options.window = value;
|
|
98
|
+
if (arg === '--timeout')
|
|
99
|
+
options.timeout = value;
|
|
100
|
+
i++;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case '--client':
|
|
104
|
+
if (!next)
|
|
105
|
+
return { error: '--client needs a name' };
|
|
106
|
+
options.client = next;
|
|
107
|
+
i++;
|
|
108
|
+
break;
|
|
109
|
+
case '--cwd':
|
|
110
|
+
if (!next)
|
|
111
|
+
return { error: '--cwd needs a path' };
|
|
112
|
+
options.cwd = next;
|
|
113
|
+
i++;
|
|
114
|
+
break;
|
|
115
|
+
default:
|
|
116
|
+
return { error: `unknown option: ${arg}` };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return options;
|
|
120
|
+
}
|
|
121
|
+
const out = (lines) => {
|
|
122
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
123
|
+
};
|
|
124
|
+
const note = (text) => {
|
|
125
|
+
if (process.stderr.isTTY)
|
|
126
|
+
process.stderr.write(`${text}\n`);
|
|
127
|
+
};
|
|
128
|
+
export async function main(argv) {
|
|
129
|
+
const parsed = parseArgs(argv);
|
|
130
|
+
if ('help' in parsed) {
|
|
131
|
+
process.stdout.write(HELP);
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
if ('version' in parsed) {
|
|
135
|
+
process.stdout.write(`${VERSION}\n`);
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
if ('error' in parsed) {
|
|
139
|
+
process.stderr.write(`${parsed.error}\n\nmcp-baggage --help\n`);
|
|
140
|
+
return 2;
|
|
141
|
+
}
|
|
142
|
+
const options = parsed;
|
|
143
|
+
const { specs, files } = await discover(options.cwd);
|
|
144
|
+
const wanted = specs
|
|
145
|
+
.filter((s) => options.all || s.enabled)
|
|
146
|
+
.filter((s) => !options.client || s.carriedBy.includes(options.client));
|
|
147
|
+
if (wanted.length === 0) {
|
|
148
|
+
out([
|
|
149
|
+
specs.length === 0
|
|
150
|
+
? 'no MCP servers found in any config on this machine'
|
|
151
|
+
: `no servers match — ${specs.length} found, all filtered out`,
|
|
152
|
+
'',
|
|
153
|
+
files.length ? `looked in:\n${files.map((f) => ` ${f}`).join('\n')}` : 'looked in the usual places and found no config',
|
|
154
|
+
]);
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
if (options.list) {
|
|
158
|
+
out([
|
|
159
|
+
`${wanted.length} server${wanted.length === 1 ? '' : 's'} in ${files.length} config${files.length === 1 ? '' : 's'}`,
|
|
160
|
+
'',
|
|
161
|
+
...wanted.map((s) => ` ${s.name.padEnd(20).slice(0, 20)} ${s.transport.padEnd(6)} ${s.carriedBy.join(', ').padEnd(24)} ${s.enabled ? '' : '(switched off) '}${s.source}`),
|
|
162
|
+
]);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
note(`asking ${wanted.length} server${wanted.length === 1 ? '' : 's'} what they carry…`);
|
|
166
|
+
let done = 0;
|
|
167
|
+
const inventories = await inspectAll(wanted, {
|
|
168
|
+
timeout: options.timeout,
|
|
169
|
+
onDone: (inv) => {
|
|
170
|
+
done++;
|
|
171
|
+
const what = inv.error ? `failed: ${inv.error}` : `${inv.tools.length} tools`;
|
|
172
|
+
note(` [${done}/${wanted.length}] ${inv.server.name} — ${what}`);
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
const usage = options.usage ? await readUsage(options.days) : null;
|
|
176
|
+
if (options.usage && !usage)
|
|
177
|
+
note('no Claude Code transcripts found — usage is left unknown');
|
|
178
|
+
let exactTokens = null;
|
|
179
|
+
if (options.exact) {
|
|
180
|
+
const apiKey = process.env['ANTHROPIC_API_KEY'];
|
|
181
|
+
if (!apiKey) {
|
|
182
|
+
process.stderr.write('--exact needs ANTHROPIC_API_KEY in the environment\n');
|
|
183
|
+
return 2;
|
|
184
|
+
}
|
|
185
|
+
note('counting with the API…');
|
|
186
|
+
try {
|
|
187
|
+
exactTokens = await exactly(inventories, apiKey);
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
process.stderr.write(`exact count failed, falling back to the estimate: ${e instanceof Error ? e.message : e}\n`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const callsOf = lookup(usage);
|
|
194
|
+
const weighed = heaviestFirst(inventories.map((inv) => {
|
|
195
|
+
const server = weigh(inv, callsOf);
|
|
196
|
+
const exact = exactTokens?.get(inv.server.name);
|
|
197
|
+
return exact === undefined ? server : { ...server, tokens: exact };
|
|
198
|
+
}));
|
|
199
|
+
if (options.json) {
|
|
200
|
+
out([JSON.stringify(asJson(weighed, options.window, exactTokens !== null), null, 2)]);
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
note('');
|
|
204
|
+
out([
|
|
205
|
+
...report(weighed, {
|
|
206
|
+
window: options.window,
|
|
207
|
+
days: usage ? usage.days : null,
|
|
208
|
+
home: homedir(),
|
|
209
|
+
exact: exactTokens !== null,
|
|
210
|
+
}),
|
|
211
|
+
...(options.tools ? breakdown(weighed) : []),
|
|
212
|
+
]);
|
|
213
|
+
return weighed.every((s) => s.error) ? 1 : 0;
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the servers are declared, and what they are.
|
|
3
|
+
*
|
|
4
|
+
* Every client keeps its own file in its own shape, and most people have the
|
|
5
|
+
* same handful of servers in two or three of them. This finds the files, reads
|
|
6
|
+
* whichever exist, and folds identical servers together — a server is the same
|
|
7
|
+
* server when it is the same command or the same URL, whatever each config
|
|
8
|
+
* chose to call it.
|
|
9
|
+
*/
|
|
10
|
+
import type { ServerSpec } from './types.ts';
|
|
11
|
+
/** One config file that might exist. */
|
|
12
|
+
export type Candidate = {
|
|
13
|
+
client: string;
|
|
14
|
+
scope: 'user' | 'project';
|
|
15
|
+
path: string;
|
|
16
|
+
format: 'json' | 'toml';
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The files worth looking at, in the order a report should mention them.
|
|
20
|
+
*
|
|
21
|
+
* Project files come after the user ones for each client so that a project
|
|
22
|
+
* entry overrides the machine-wide one of the same name, which is how every
|
|
23
|
+
* client resolves it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function candidates(cwd: string, home?: string): Candidate[];
|
|
26
|
+
/**
|
|
27
|
+
* The servers one file declares.
|
|
28
|
+
*
|
|
29
|
+
* Exported and taking text rather than a path so that it can be tested against
|
|
30
|
+
* every client's shape without a filesystem: `mcpServers` for Claude Code,
|
|
31
|
+
* Cursor and Windsurf, `servers` for VS Code, `mcp_servers` for Codex.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseConfig(text: string, at: Candidate, cwd?: string): ServerSpec[];
|
|
34
|
+
/** What makes two entries the same server: the thing that gets run, not the name. */
|
|
35
|
+
export declare function identity(spec: ServerSpec): string;
|
|
36
|
+
/**
|
|
37
|
+
* Folds duplicates together.
|
|
38
|
+
*
|
|
39
|
+
* The first sighting wins the name and the file shown, later ones only add
|
|
40
|
+
* their client to `carriedBy` — except that a project-scoped entry replaces a
|
|
41
|
+
* user-scoped one, which is the override every client applies.
|
|
42
|
+
*/
|
|
43
|
+
export declare function merge(specs: readonly ServerSpec[]): ServerSpec[];
|
|
44
|
+
/** Every server declared on this machine, for this directory. */
|
|
45
|
+
export declare function discover(cwd: string, home?: string): Promise<{
|
|
46
|
+
specs: ServerSpec[];
|
|
47
|
+
files: string[];
|
|
48
|
+
}>;
|