token-save-mcp 0.1.0__tar.gz
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.
- token_save_mcp-0.1.0/.github/workflows/ci.yml +57 -0
- token_save_mcp-0.1.0/.github/workflows/publish.yml +31 -0
- token_save_mcp-0.1.0/.gitignore +10 -0
- token_save_mcp-0.1.0/CHANGELOG.md +29 -0
- token_save_mcp-0.1.0/CONTRIBUTING.md +27 -0
- token_save_mcp-0.1.0/LICENSE +21 -0
- token_save_mcp-0.1.0/PKG-INFO +326 -0
- token_save_mcp-0.1.0/README.md +284 -0
- token_save_mcp-0.1.0/docs/demo.gif +0 -0
- token_save_mcp-0.1.0/docs/make_demo.py +176 -0
- token_save_mcp-0.1.0/hooks/check-file-size +94 -0
- token_save_mcp-0.1.0/pyproject.toml +44 -0
- token_save_mcp-0.1.0/src/token_save_mcp/__init__.py +3 -0
- token_save_mcp-0.1.0/src/token_save_mcp/cli.py +482 -0
- token_save_mcp-0.1.0/src/token_save_mcp/server.py +640 -0
- token_save_mcp-0.1.0/tests/test_hook.sh +70 -0
- token_save_mcp-0.1.0/tests/test_server.py +560 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest]
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e .
|
|
29
|
+
|
|
30
|
+
- name: Server tests (offline, no API calls)
|
|
31
|
+
run: python tests/test_server.py
|
|
32
|
+
|
|
33
|
+
- name: Ensure jq (the hook needs it)
|
|
34
|
+
run: |
|
|
35
|
+
command -v jq >/dev/null || {
|
|
36
|
+
if [ "$RUNNER_OS" = "macOS" ]; then brew install jq;
|
|
37
|
+
else sudo apt-get update && sudo apt-get install -y jq; fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- name: Hook tests
|
|
41
|
+
run: bash tests/test_hook.sh
|
|
42
|
+
|
|
43
|
+
- name: CLI smoke test
|
|
44
|
+
run: |
|
|
45
|
+
token-save-mcp --help
|
|
46
|
+
token-save-mcp doctor --offline || true
|
|
47
|
+
|
|
48
|
+
build:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
- uses: actions/setup-python@v5
|
|
53
|
+
with:
|
|
54
|
+
python-version: "3.12"
|
|
55
|
+
- run: pip install build twine
|
|
56
|
+
- run: python -m build
|
|
57
|
+
- run: twine check dist/*
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: pypi
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write # trusted publishing — no API token stored here
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Build
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip build twine
|
|
27
|
+
python -m build
|
|
28
|
+
twine check dist/*
|
|
29
|
+
|
|
30
|
+
- name: Publish
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] — 2026-09-18
|
|
4
|
+
|
|
5
|
+
First public release.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `bulk_read` — delegate reading files to a worker model; only the answer
|
|
9
|
+
enters the agent's context.
|
|
10
|
+
- `code_write` — generate code from a spec and a reference file, optionally
|
|
11
|
+
written straight to disk so the generated code never enters context.
|
|
12
|
+
- `status` — print the live configuration and probe the worker.
|
|
13
|
+
- PreToolUse hook that blocks `Read` on files over a line or byte threshold
|
|
14
|
+
and redirects to `bulk_read`. Opt-in, reversible, and available in a softer
|
|
15
|
+
`warn` mode that allows the read but reports what it cost.
|
|
16
|
+
- `token-save-mcp init` — one-command setup, no hand-edited JSON.
|
|
17
|
+
- `token-save-mcp doctor` — diagnoses config, dependencies, registration and
|
|
18
|
+
makes a live worker call.
|
|
19
|
+
- Provider presets: Ollama Cloud, OpenRouter, DeepSeek, Groq, local runtimes.
|
|
20
|
+
Any OpenAI-compatible endpoint via `TOKENSAVE_BASE_URL`.
|
|
21
|
+
- `token-save-mcp stats` — a local JSONL ledger of every call, with a
|
|
22
|
+
shareable badge. Never leaves the machine; opt out with `TOKENSAVE_NO_LEDGER`.
|
|
23
|
+
- 95 offline tests plus 19 hook routing tests; the transport is stubbed, so the
|
|
24
|
+
suite costs nothing to run.
|
|
25
|
+
|
|
26
|
+
### Notes
|
|
27
|
+
- Works with both `mcp` 1.x (`FastMCP`) and 2.x (`MCPServer`).
|
|
28
|
+
- A SOCKS proxy without `httpx[socks]` now produces an actionable message
|
|
29
|
+
instead of a stack trace from inside httpx.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Bug reports and patches are welcome.
|
|
4
|
+
|
|
5
|
+
## Running the tests
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install -e .
|
|
9
|
+
python tests/test_server.py # 95 offline tests — no API calls
|
|
10
|
+
bash tests/test_hook.sh # 19 hook routing tests (needs jq)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The transport is stubbed, so the suite is free to run and safe in CI.
|
|
14
|
+
|
|
15
|
+
## Ground rules
|
|
16
|
+
|
|
17
|
+
- **A new behaviour needs a test.** The suite exists because several real
|
|
18
|
+
defects (a dangling-symlink write, a corpus that ate all memory, a hook that
|
|
19
|
+
emitted invalid JSON on odd filenames) were found by review and would
|
|
20
|
+
otherwise have come back.
|
|
21
|
+
- **Don't pin a model roster.** Any model id the provider serves is valid; a
|
|
22
|
+
hardcoded list goes stale the moment a provider ships something new.
|
|
23
|
+
- **Savings must stay measured.** The footer reports the provider's own `usage`
|
|
24
|
+
numbers. Don't replace them with estimates.
|
|
25
|
+
- **The hook fails open.** If anything is uncertain — no jq, malformed input,
|
|
26
|
+
unreadable file — it allows the read. Blocking by accident is worse than
|
|
27
|
+
missing a saving.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Habartru
|
|
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.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: token-save-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server that keeps large files out of your coding agent's context by delegating reads to a cheap worker model. Enforced by a hook, measured per call.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Habartru/token_save_mcp
|
|
6
|
+
Project-URL: Issues, https://github.com/Habartru/token_save_mcp/issues
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2026 Habartru
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Keywords: ai-coding,claude,claude-code,context-window,cursor,developer-tools,llm,mcp,model-context-protocol,ollama,openrouter,token-optimization
|
|
30
|
+
Classifier: Development Status :: 4 - Beta
|
|
31
|
+
Classifier: Intended Audience :: Developers
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
37
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
38
|
+
Requires-Python: >=3.10
|
|
39
|
+
Requires-Dist: mcp>=1.2
|
|
40
|
+
Requires-Dist: openai>=1.60
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# token-save-mcp
|
|
44
|
+
|
|
45
|
+
**Your coding agent burns its context reading files. This stops it — and shows you the receipt.**
|
|
46
|
+
|
|
47
|
+
[](https://github.com/Habartru/token_save_mcp/actions/workflows/ci.yml)
|
|
48
|
+
[](https://pypi.org/project/token-save-mcp/)
|
|
49
|
+
[](https://github.com/Habartru/token_save_mcp/blob/main/LICENSE)
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
|
|
52
|
+
An MCP server that sends big files to a cheap worker model and returns only the
|
|
53
|
+
answer. The file bytes are paid for once, in the worker's context — not
|
|
54
|
+
permanently in your agent's.
|
|
55
|
+
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
The hook **blocks** the expensive read and redirects it. The answer comes back
|
|
59
|
+
with the worker's **real token usage from the API response** — not an estimate,
|
|
60
|
+
a receipt:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
─────────────────────────────────────────────────────────────
|
|
64
|
+
token-save: 1 file, 606 lines | direct read ≈7,042 tok →
|
|
65
|
+
into context ≈234 tok (saved 6,808 · 97%)
|
|
66
|
+
worker: glm-5.3-flash | 6,155 in / 278 out | 4.0s
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install token-save-mcp
|
|
75
|
+
export OPENROUTER_API_KEY=... # or OLLAMA_API_KEY, DEEPSEEK_API_KEY…
|
|
76
|
+
token-save-mcp init --provider openrouter --hook
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
That's it — `init` registers the MCP server and installs the hook. No
|
|
80
|
+
hand-edited JSON. Drop `--hook` if you want the tools without enforcement.
|
|
81
|
+
|
|
82
|
+
Verify with `token-save-mcp doctor`, which checks the config and makes one
|
|
83
|
+
live call to prove the worker answers:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
✓ provider: openrouter -> https://openrouter.ai/api/v1
|
|
87
|
+
✓ worker model: deepseek/deepseek-chat
|
|
88
|
+
✓ MCP server registered and connected
|
|
89
|
+
✓ enforcement hook installed
|
|
90
|
+
✓ worker replied in 2.0s (21 in / 21 out)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary>Providers</summary>
|
|
95
|
+
|
|
96
|
+
Any OpenAI-compatible endpoint works. Presets:
|
|
97
|
+
|
|
98
|
+
| `--provider` | Endpoint | Key variable |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `ollama` *(default)* | ollama.com | `OLLAMA_API_KEY` |
|
|
101
|
+
| `openrouter` | openrouter.ai | `OPENROUTER_API_KEY` |
|
|
102
|
+
| `deepseek` | api.deepseek.com | `DEEPSEEK_API_KEY` |
|
|
103
|
+
| `groq` | api.groq.com | `GROQ_API_KEY` |
|
|
104
|
+
| `local` | localhost:11434 | *(none)* |
|
|
105
|
+
|
|
106
|
+
Anything else: set `TOKENSAVE_BASE_URL` and `TOKENSAVE_API_KEY` directly.
|
|
107
|
+
With `local` your code never leaves the machine.
|
|
108
|
+
|
|
109
|
+
The transport is the plain OpenAI SDK pointed at a `base_url`, so any
|
|
110
|
+
OpenAI-compatible endpoint works. The measurements below were taken against
|
|
111
|
+
Ollama Cloud; the other presets are configured and exercised by the test suite
|
|
112
|
+
but their numbers will differ with the model you pick.
|
|
113
|
+
|
|
114
|
+
</details>
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## The part nobody else does: enforcement
|
|
119
|
+
|
|
120
|
+
Every token-saving tool has the same failure mode — **the agent forgets to use
|
|
121
|
+
it**. A tool the model may ignore gets ignored, and your savings are whatever
|
|
122
|
+
the model felt like that day.
|
|
123
|
+
|
|
124
|
+
`token-save-mcp install-hook` registers a `PreToolUse` hook that **blocks**
|
|
125
|
+
`Read` on files over the threshold and redirects the agent to `bulk_read`:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
Read("src/server.py")
|
|
129
|
+
→ BLOCKED: This file is 606 lines (threshold: 350).
|
|
130
|
+
Use bulk_read to delegate this read instead.
|
|
131
|
+
Need exact content to EDIT? Re-read with offset/limit — that passes through.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
What still passes through, by design:
|
|
135
|
+
|
|
136
|
+
- **Targeted reads** (`offset`/`limit`) — editing needs exact text
|
|
137
|
+
- **Small files** — under the threshold, delegating costs more than it saves
|
|
138
|
+
- **Binaries** and missing files — nothing to summarise
|
|
139
|
+
|
|
140
|
+
**Not ready to be told no?** Install it in warn mode instead — the read goes
|
|
141
|
+
through, but you see what it cost:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
token-save-mcp install-hook --hook-mode warn
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Enforcement is **opt-in** and reversible: `token-save-mcp uninstall-hook`.
|
|
148
|
+
|
|
149
|
+
> The hook is Claude Code only. The `bulk_read` / `code_write` tools are plain
|
|
150
|
+
> MCP and work in any client — Cursor, Cline, Windsurf, Codex — just without
|
|
151
|
+
> the enforcement layer.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Tools
|
|
156
|
+
|
|
157
|
+
### `bulk_read(question, paths, model?, effort?)`
|
|
158
|
+
|
|
159
|
+
Read files without pulling them into context.
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
bulk_read(
|
|
163
|
+
question="Which methods touch the database, and where is auth enforced?",
|
|
164
|
+
paths=["src/service.py", "src/handlers.py"]
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Use for:** surveying unfamiliar code, "what does this do", tracing a flow
|
|
169
|
+
across files, finding where something is handled.
|
|
170
|
+
|
|
171
|
+
**Don't use for:** editing (you need exact text — use a targeted read),
|
|
172
|
+
debugging that needs your own reasoning over raw code, or files under ~350
|
|
173
|
+
lines where delegation overhead exceeds the saving. The tool tells you when
|
|
174
|
+
you've crossed that line rather than silently burning a call.
|
|
175
|
+
|
|
176
|
+
### `code_write(spec, reference, target?, model?, effort?)`
|
|
177
|
+
|
|
178
|
+
Generate boilerplate matching an existing file's style. With `target`, the code
|
|
179
|
+
is written **straight to disk** and only a confirmation returns — the generated
|
|
180
|
+
code never enters your context at all.
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
code_write(
|
|
184
|
+
spec="pytest suite for clamp(value, lo, hi), covering both bounds and lo>hi",
|
|
185
|
+
reference=["tests/test_total.py"],
|
|
186
|
+
target="tests/test_clamp.py"
|
|
187
|
+
)
|
|
188
|
+
→ Wrote tests/test_clamp.py (32 lines). Not read into your context.
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Never overwrites: the target is created with `O_EXCL`, which also refuses to
|
|
192
|
+
follow a dangling symlink.
|
|
193
|
+
|
|
194
|
+
### `status()`
|
|
195
|
+
|
|
196
|
+
Prints the live configuration and makes one tiny call to prove the worker is
|
|
197
|
+
actually reachable.
|
|
198
|
+
|
|
199
|
+
### `token-save-mcp stats`
|
|
200
|
+
|
|
201
|
+
Every call appends one line to a local ledger, so you can see what the tool has
|
|
202
|
+
actually saved you. Example output after a few weeks of use:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
$ token-save-mcp stats --badge
|
|
206
|
+
|
|
207
|
+
token-save-mcp — all time
|
|
208
|
+
|
|
209
|
+
148 calls · 71,204 lines of code read by a worker
|
|
210
|
+
context saved: 812,455 tokens (94%)
|
|
211
|
+
worker time: 612s total
|
|
212
|
+
|
|
213
|
+
Markdown badge:
|
|
214
|
+

|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The ledger is a plain JSONL file in `~/.token-save/` and never leaves your
|
|
218
|
+
machine. `--since 7` limits the window; `TOKENSAVE_NO_LEDGER=1` turns recording
|
|
219
|
+
off entirely.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Measured savings
|
|
224
|
+
|
|
225
|
+
Real runs, not projections. Each number is the footer from an actual call:
|
|
226
|
+
|
|
227
|
+
| What | Size | Direct read | Via token-save | Saved |
|
|
228
|
+
|---|---|---|---|---|
|
|
229
|
+
| This project's own server.py | 606 lines | ≈7,042 tok | ≈234 tok | **97%** |
|
|
230
|
+
| A large TypeScript handler | 602 lines | ≈13,340 tok | ≈689 tok | **95%** |
|
|
231
|
+
| Production Python service | 443 lines | ≈5,788 tok | ≈684 tok | **88%** |
|
|
232
|
+
| 4 files across a codebase | 1,910 lines | ≈28,379 tok | ≈304 tok | **99%** |
|
|
233
|
+
| Code generation to disk | 58 lines written | — | 0 tok | **100%** |
|
|
234
|
+
|
|
235
|
+
**Method:** "direct read" is the file's own size at ~3.6 chars/token (source
|
|
236
|
+
code is denser than prose); "via token-save" is the returned answer measured the
|
|
237
|
+
same way. The worker's in/out numbers come from the provider's `usage` field.
|
|
238
|
+
Reproduce any row by running the same call — the footer prints on every one.
|
|
239
|
+
|
|
240
|
+
**Where it's weaker, honestly:** on a 281-line diff the saving was 67%, because
|
|
241
|
+
a short input with a long answer is the worst case. The tool says so in its own
|
|
242
|
+
output. Savings are best where the file is big and the question is narrow.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## How it compares
|
|
247
|
+
|
|
248
|
+
Different tools solve "too many tokens" in genuinely different ways:
|
|
249
|
+
|
|
250
|
+
| | Approach | Enforced? | Savings figure |
|
|
251
|
+
|---|---|---|---|
|
|
252
|
+
| **token-save-mcp** | LLM worker reads, returns an answer | **Yes** — hook blocks Read | Measured per call |
|
|
253
|
+
| Static AST tools | Parse the tree, return exact symbols | No | Deterministic |
|
|
254
|
+
| Other delegation MCPs | LLM worker, single provider | No | Usually estimated |
|
|
255
|
+
|
|
256
|
+
**Static AST tools are better than this one** at "give me the exact body of
|
|
257
|
+
`handleRequest`" — they're free, instant, and can't hallucinate. Reach for them
|
|
258
|
+
for symbol lookup.
|
|
259
|
+
|
|
260
|
+
This tool is for **semantic questions over large files** — "what does this
|
|
261
|
+
service do", "where does auth happen", "which of these files handle retries" —
|
|
262
|
+
where you want an answer, not an extract. That costs a worker call and a few
|
|
263
|
+
seconds, and a worker can be wrong. Use both.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Configuration
|
|
268
|
+
|
|
269
|
+
| Variable | Default | Purpose |
|
|
270
|
+
|---|---|---|
|
|
271
|
+
| `TOKENSAVE_PROVIDER` | `ollama` | Preset: ollama, openrouter, deepseek, groq, local |
|
|
272
|
+
| `TOKENSAVE_API_KEY` | — | Overrides the preset's key variable |
|
|
273
|
+
| `TOKENSAVE_BASE_URL` | preset | Any OpenAI-compatible endpoint |
|
|
274
|
+
| `TOKENSAVE_MODEL` | preset | Worker model id |
|
|
275
|
+
| `TOKENSAVE_MIN_LINES` | `350` | Hook threshold, and the "too small" warning |
|
|
276
|
+
| `TOKENSAVE_HOOK_MODE` | `block` | `warn` allows the read but flags the cost |
|
|
277
|
+
| `TOKENSAVE_HOOK_MAX_BYTES` | `100000` | Also block on size — catches minified files |
|
|
278
|
+
| `TOKENSAVE_MAX_CORPUS_BYTES` | `2000000` | Ceiling on one request |
|
|
279
|
+
| `TOKENSAVE_TIMEOUT` | `600` | Seconds per call |
|
|
280
|
+
| `TOKENSAVE_MAX_RETRIES` | `4` | Retries on transient failures |
|
|
281
|
+
| `TOKENSAVE_MAX_CONCURRENCY` | `3` | Match your provider's limit |
|
|
282
|
+
| `TOKENSAVE_LEDGER` | `~/.token-save/ledger.jsonl` | Where `stats` reads from |
|
|
283
|
+
| `TOKENSAVE_NO_LEDGER` | unset | Set to disable local recording |
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## When not to use this
|
|
288
|
+
|
|
289
|
+
Being clear about this is the point, not a disclaimer:
|
|
290
|
+
|
|
291
|
+
- **You need exact text to edit.** Use a targeted read. The hook lets those through.
|
|
292
|
+
- **You're debugging subtle behaviour.** Summaries lose the detail that matters.
|
|
293
|
+
- **The file is small.** Under ~350 lines, reading directly is cheaper and faster.
|
|
294
|
+
- **The worker can be wrong.** It's an LLM. For anything you'll act on blindly,
|
|
295
|
+
verify against the source. Static tools don't have this failure mode.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Development
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
git clone https://github.com/Habartru/token_save_mcp
|
|
303
|
+
cd token_save_mcp
|
|
304
|
+
pip install -e ".[dev]"
|
|
305
|
+
|
|
306
|
+
python tests/test_server.py # 83 offline tests, no API calls
|
|
307
|
+
bash tests/test_hook.sh # 19 hook routing tests
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The test suite stubs the transport, so it costs nothing to run and is safe in
|
|
311
|
+
CI. It covers the retry loop, corpus assembly, fence stripping, the disk-write
|
|
312
|
+
guards, and every hook routing decision.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Credits
|
|
317
|
+
|
|
318
|
+
The delegation-plus-hook pattern is adapted from the `shunt` plugin in
|
|
319
|
+
[spotify/portal-ai-plugins](https://github.com/spotify/portal-ai-plugins)
|
|
320
|
+
(Apache-2.0), which routes the same kind of work through Spotify's internal
|
|
321
|
+
Portal CLI. This project keeps the idea and swaps the transport for any
|
|
322
|
+
OpenAI-compatible provider, so no corporate Portal instance is required. Files
|
|
323
|
+
also travel in-process rather than through `argv`, which removes the 128 KiB
|
|
324
|
+
per-argument limit on Linux.
|
|
325
|
+
|
|
326
|
+
MIT licensed.
|