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 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
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -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
+ }>;