MCP server and proxy that compresses LLM prompts, tool output, and replies to cut token cost.
MCP Server: io.github.fkiene/llmtrim
This MCP server and proxy compresses LLM prompts, tool output, and replies to reduce token cost. It presents the same answers while aiming to lower input, output, and roundβtrip expenses. The repository describes ~5 ms/call behavior and no model to load.
π οΈ Key Features
MCP server and proxy for LLM prompt compression
Compresses LLM prompts, tool output, and replies
Includes cost-reduction metrics (listed as β31% input, β74% output, β66% round-trip cost)
Mentions ~5 ms/call and 112 live A/B cases
Provides additional CLAUDE CODE support via a served /sub through CLIProxyAPI
π Use Cases
Reducing token spend for LLM interactions that include tool use
Deploying as a local proxy for LLM API traffic
Serving Claude Code through the integrated CLIProxyAPI path
β‘ Developer Benefits
Lower input/output/roundβtrip cost targets as stated
No model loading requirement described
~5 ms/call latency expectation described
β οΈ Limitations
Only token/cost and latency characteristics are described; details like supported providers, exact MCP transport, and tool coverage beyond βtool outputβ are not provided in the excerpt.
You run Claude Code, Codex, Cursor, or your own app. Every turn, the tool sends a large request: system prompt, tools, history, raw command output. You pay for every token of that, including the parts that do not help the model.
A 200-line build log with two errors. Tool schemas resent on every call. JSON with hundreds of near-identical rows. That bulk is still billed.
llmtrim sits on your machine as a local proxy, trims the waste, and forwards a smaller request. The reply is unchanged. You keep the same tools and answers; you spend less.
code
before: your tool βββββ full request ββββββΆ OpenAI / Anthropic / β¦
βββββββββ reply ββββββββββ
after: your tool βββΆ llmtrim ββsmallerβββΆ OpenAI / Anthropic / β¦
(on your machine)
βββββββββ reply ββββββββββ (same answer)
Compression cannot raise your bill or break a request; worst case is zero savings. Everything runs locally, nothing is sent to us. In action β
For Claude Code the same install also wires /sub to route sessions through another subscription. Details β
Get started
bash
npm install -g @llmtrim/cli@latest && llmtrim setup
# open a new terminal, then keep working
llmtrim status
That's it. setup starts a local proxy and wires your shell. When Claude Code is present, it also turns on /sub. You do not run a separate install for that.
You want
Run
First install
llmtrim setup
New version
llmtrim update (then llmtrim ensure after npm/brew/cargo)
Something broken
llmtrim ensure Β· llmtrim doctor --fix Β· or f in status
Any tool that honors HTTPS_PROXY works (Claude Code, Codex, Cursor, Aider, your SDK). GitHub Copilot does not (certificate pinning). Full list β
Other installers (Homebrew, curl, Scoop, Cargo, Docker)
bash
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/fkiene/llmtrim/main/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/fkiene/llmtrim/main/install.ps1 | iex
# Package managers
brew install fkiene/tap/llmtrim
cargo binstall llmtrim
scoop install llmtrim
docker run -d -p 43117:43117 -v llmtrim-state:/data ghcr.io/fkiene/llmtrim
Menu-bar / system-tray popover with the same savings numbers. Bundled in Homebrew, Scoop, and npm; setup can enable open-at-login. Open with llmtrim tray. On Linux desktops, interactive ensure can fetch the tray binary from the latest release (needs libwebkit2gtk-4.1 and libayatana-appindicator3).
Is this safe?
Same technique as mitmproxy, scoped to LLM API hosts only. setup changes three things; llmtrim uninstall reverses all three:
Private CA in ~/.llmtrim/ (name-constrained; cannot intercept your bank or email)
Shell env: HTTPS_PROXY + CA trust
Login service: daemon at login
No API keys stored (your tool's auth is forwarded). Prompts never touch disk; only anonymous token counts. Recoverable tool results stay in bounded daemon RAM for five hours by default and disappear on restart. Full threat model: SECURITY.md.
llmtrim status # savings + health (aliases: monitor, gain)
llmtrim update # new release, restart daemon, refresh integrations
llmtrim ensure # match the recommended install state on this machine
Situation
Command
Watch savings
llmtrim status
After npm / brew / cargo upgrade
llmtrim ensure (or f in status)
Diagnose
llmtrim doctor Β· repair with doctor --fix
Pause / resume proxy
llmtrim stop Β· llmtrim start
Force one session through llmtrim
llmtrim wrap claude
Remove everything
llmtrim uninstall
After setup, update, or ensure, owned Claude Code /sub stays in sync with the binary.
Time series: llmtrim status --daily Β· --weekly Β· --monthly Β· --json Β· --csv.
In action
An agent ran a build. The tool returned 58 lines; two were errors. All 58 would have been billed.
4,662 chars β 978 (β79%). Errors stay verbatim. Repeated INFO lines fold into a template plus the values (lossless when the range is regular).
text
# before (noise + signal)
[2026-06-13T10:02:00Z] INFO compiling module core::worker::task_0 (incremental)
β¦ 28 more near-identical INFO lines β¦
[2026-06-13T10:02:31Z] ERROR src/worker/pool.rs:214: mismatched types: expected `usize`, found `i64`
β¦ 25 more INFO lines β¦
[2026-06-13T10:03:01Z] ERROR src/net/conn.rs:88: cannot borrow `buf` as mutable more than once
[2026-06-13T10:03:02Z] INFO build failed, 2 errors
# after (errors verbatim; INFO folded losslessly)
[{}] INFO compiling module core::worker::task_{} (incremental) [Γ30: (10:02:00Z..10:02:29Z step 1s; 0..29)]
[2026-06-13T10:02:31Z] ERROR src/worker/pool.rs:214: mismatched types: expected `usize`, found `i64`
[{}] INFO compiling module core::net::conn_{} (incremental) [Γ25: 10:02:32Z..10:02:56Z; 0..24]
[2026-06-13T10:03:01Z] ERROR src/net/conn.rs:88: cannot borrow `buf` as mutable more than once
[2026-06-13T10:03:02Z] INFO build failed, 2 errors
Log folding is one stage. Others kick in on different waste:
Waste
What happens
Build logs, diffs, grep dumps
Keep errors / changes / matches; fold the rest
Long pasted context
Keep chunks relevant to the question
Source code
Keep useful bodies; rest β signatures
Tool schemas every turn
Trim + keep the cache prefix stable
Huge JSON arrays
Compact table (TOON) or sample
Verbose model replies
Ask for terser output where safe
IMPORTANT
Compression cannot raise your bill or break a request. Each stage is re-measured with the provider's real tokenizer and undone if it does not save tokens. If the provider rejects the compressed body, the original is resent. Worst case is zero savings.
Existing prompt-cache prefixes (cache_control) are left alone. First-arrival recoverable shaping (llmtrim recall r_β¦) is off by default; live-zone tool-output windowing still runs, and a re-run of the same tool ships in full. To skip shaping for a command (verbatim stdout, including a terminal trailer), set toolout_passthrough = ["*gpt.sh*"] or prefix the command with LLMTRIM_TOOL_OUTPUT=passthrough. Lines starting with LLMTRIM_KEEP: survive windowing even when the rest is clipped.
All 10 compressors
Stages run in savings order. Nothing under a cache_control marker is rewritten.
Stage
What it does
When it runs
tool-output
Lossless template fold first, then window logs Β· diffs Β· grep Β· dumps down to errors / changes / matches; opt-in first-arrival recall can restore omitted cache-boundary results with llmtrim recall
tool results
cache discipline
Mark + stabilize the invariant prefix (sort tools/schema Β· OpenAI prompt_cache_key) so it stays cached
Default auto enables each stage only where it pays. safe is lossless-only. Config β
Claude Code
When ~/.claude exists, setup, update, and ensure wire /sub (and routed subagents). No separate install command for that.
Feature
What you get
/sub
Per-window: /sub on [optional:codex|kimi|grok] Β· /sub off Β· /sub status
Subscription reroute (`sub`) (opt-in; may conflict with provider ToS)
Send Claude Code through CLIProxyAPI instead of Anthropic, or as a fallback hop when the current path fails. Login is CLIProxyAPI's TUI; decide for yourself whether that fits the provider ToS.
bash
llmtrim sub on # install + start CLIProxyAPI, enable redirect
llmtrim sub on grok # write opus/sonnet/haiku/fable β that CLI's models
llmtrim sub auth # CLIProxyAPI TUI β sign in
llmtrim sub models # official + live sidecar models
llmtrim sub map on opus grok-4.6
llmtrim sub chain anthropic,codex
llmtrim sub mode fallback # try hops in order when the current one fails
llmtrim sub status
llmtrim sub off
Interactive: llmtrim status β tab 4 Sub β Off / Always / Fallback, then e to edit the
inputβoutput map (type to search CLIProxyAPI's catalog;
s save, a add, d delete). [] rotate the fallback first hop.
llmtrim update also updates CLIProxyAPI when you use it. Point LLMTRIM_CLIPROXY_URL at an
existing instance to skip the managed sidecar.
Route only a delegated Claude Code subagent while leaving the parent window unchanged:
bash
llmtrim agents install # also installed/refreshed by setup, update, and ensure
Then ask naturally: Implement it using a Grok subagent, use Terra, or review this with GPT Terra.
Provider-only agents preserve the child request's Claude tier through the configured mapping; an
explicit model agent pins that provider model. Request-local agents override the window /sub and
global policy only for their own requests. llmtrim agents uninstall removes only llmtrim-owned
agent files and records an opt-out so ensure leaves them removed.
This window only (installed with ensure; includes subagents; survives /clear):
text
/sub on [optional:cli-or-model] # gemini, codex, claude, grok, kimi, vertex, qwen, copilot, or a model id
/sub off
/sub status
Anthropic /login vs claude.ai connectors: with global sub in always mode, by
default llmtrim writes a dummy ANTHROPIC_AUTH_TOKEN into ~/.claude/settings.json (same
idea as claude-code-proxy's
ANTHROPIC_AUTH_TOKEN=unused) so Claude Code does not need a live Anthropic OAuth session.
The MITM strips that dummy token and sends /v1/messages to CLIProxyAPI; non-messages
Anthropic probes are answered locally so they never return 401 Invalid bearer token.
Claude Code treats any API-key auth as overriding claude.ai login, so claude.ai connectors
are disabled while the dummy token is set. That is expected. To keep connectors (and accept
Anthropic /login when the session expires):
bash
llmtrim sub anthropic-login keep # connectors OK; Anthropic login required
llmtrim sub anthropic-login skip # default: no Anthropic /login; connectors off
Restart Claude Code after sub on / sub off / sub mode / sub anthropic-login for the
settings change to take effect. Fallback whose first hop is "what's in use" still needs a real
Anthropic login.
Use it as a CLI, MCP, or library
Same engine, no proxy required. No extra model calls; compress runs in-process.
Provider call is server-side; a local proxy never sees it
Cursor Agent, Kiro
β
Routes through a vendor gateway, not a standard provider host
No proxy: any MCP client can call llmtrim as tools (llmtrim mcp install), or use the CLI / library.
Providers come from the llm_providers registry (OpenAI, Anthropic, Google, DeepSeek, Mistral, xAI, Moonshot, Zhipu, Qwen, OpenRouter, β¦) and update with it. Non-LLM connections pass through untouched.
Configuration
Default is fine for most traffic. auto inspects each request and picks compressors by shape (tools β agent, code β code, long Q&A β rag, else aggressive).
Override with LLMTRIM_PRESET=<name> or preset = "<name>" in $XDG_CONFIG_HOME/llmtrim/config.toml:
preset
When to use
auto(default)
Let llmtrim choose per request
safe
Lossless input only
aggressive
Max squeeze, quality-gated
Advanced presets
auto composes these per request shape, so most users never set them directly. Pick one when you know your traffic and want to skip shape detection:
preset
for
agent
tool-calling loops: prunes the tool block first-turn-only so the prompt cache stays warm
code
coding turns: skeletonize and minify code, compress pasted logs and diffs
rag
long context with a question: sentence-level retrieval
cache
a fixed prefix reused across many calls
reasoning
math and step-by-step workloads
frugal
isolates the agent-loop frugality directive alone, for clean benchmarking
Per-flag overrides (power users)
Every stage is individually tunable via config flags; preset wins over individual flags. The full table is long; see the field list in config.rs or run llmtrim compress --help. The most useful knobs:
These knobs are orthogonal to compression. Each resolves env-first, then from the config file, so set whichever fits. The env var wins when both are present.
env var
config key
meaning
LLMTRIM_EXTRA_HOSTS
extra_hosts
extra exact LLM-API hosts to intercept (comma-separated env / array in file), e.g. a self-hosted OpenAI-compatible endpoint
LLMTRIM_EXCLUDE_PROVIDERS
exclude_providers
wire shapes to skip compressing: openai / anthropic / google (e.g. anthropic to leave Claude Code untouched); coarse, covers every host of that shape
LLMTRIM_EXCLUDE_HOSTS
exclude_hosts
exact hostnames to skip compressing (e.g. openrouter.ai); precise, leaves other hosts of the same shape compressed
LLMTRIM_UPSTREAM_PROXY
upstream_proxy
route egress through another proxy (see below)
LLMTRIM_DB_PATH
db_path
ledger location
LLMTRIM_CAPTURE_DIR
capture_dir
before/after QA capture directory
LLMTRIM_CAPTURE_MAX_MB
capture_max_mb
capture corpus size ceiling (0 disables)
LLMTRIM_FIRST_ARRIVAL_RECALL
first_arrival_recall
recoverable first-arrival tool-output shaping (default false; set true to admit cache-boundary results for llmtrim recall)
LLMTRIM_TOOL_OUTPUT
toolout_passthrough
skip tool-output compression for matching commands: passthrough (all) or command globs (*gpt.sh*); also honoured as a command assignment or a result line. LLMTRIM_KEEP: lines are always retained
LLMTRIM_FIRST_ARRIVAL_RECALL_TTL_SECS
first_arrival_recall_ttl_secs
in-memory raw-result lifetime (default 18,000 seconds / five hours)
LLMTRIM_BIND
bind
listen IP (default loopback; 0.0.0.0 for containers)
LLMTRIM_BREAKDOWN_WINDOW
breakdown_window
context-window override for the cost breakdown
LLMTRIM_RETENTION_DAYS
retention_days
ledger age-retention in days
LLMTRIM_NO_UPDATE_CHECK
no_update_check
disable the passive update check
extra_hosts entries must be exact hostnames (llm.acme.com, never a bare acme.com): each one widens the name-constrained MITM CA, which regenerates automatically on the next launch to cover them.
Claude Code options (subscription reroute) are under Claude Code.
Upstream proxy (corporate egress or chaining local tools)
bash
export LLMTRIM_UPSTREAM_PROXY=http://host:port
# or with auth: http://user:pass@host:port (redacted in logs)
Outbound calls use CONNECT + verifying TLS; the upstream only sees the encrypted stream.
Looping to llmtrim's own listen address is rejected. Put the variable in the daemon's
launch environment (launchd / systemd), not only your interactive shell. Profile secrets
sit in plaintext.
Companion tools on another port (e.g. headroom) are fine.
The numbers
Every case is sent twice, once original and once compressed, then both answers are scored and billed at real rates. Cost and quality are measured together, not estimated, across 112 cases:
original
compressed
saved
input tokens
71,031
49,062
β31%
output tokens
25,843
6,628
β74%
round-trip cost
$0.0365
$0.0126
β66%
answer quality
78.9%
82.2%
no measured degradation
The token cuts are model-independent (β31% input, β74% output). The dollar saving tracks the model's output-to-input price ratio: β66% here, projecting to β57% at GPT-4o rates and β59% at Claude Sonnet rates. The proxy compresses only the new-content surface and never rewrites the cache-controlled prefix, so your prompt-cache discount survives.
Accuracy preserved on standard benchmarks
The same A/B on the standard academic suites, at a conservative shape-matched preset (qwen3-next-80b, paired 95% CI). Quality is the score on the original request vs the compressed one. GSM8K comes from the frontier above (n=12); the other three are the named benchmarks readers compare against (n=20 each):
benchmark
task
scorer
input saved
quality (orig β comp)
retention
GSM8K
grade-school math
numeric-exact
β47%ΒΉ
100% β 92%
β8pp
TruthfulQA (MC1)
factual truthfulness
choice-exact
0%
75% β 75%
+0.0Β±0.0pp
SQuAD v2
extractive QA
token-F1 / EM
11%
84% β 84%
β0.0Β±15.2pp
BFCL (live_multiple)
function calling
tool-call match
33%
95% β 95%
+0.0Β±15.2pp
Three rows compress with no quality loss; GSM8K is the one dip:
BFCL drops the tool schemas the query doesn't need (a menu of 2 to 37 candidates per call).
SQuAD v2 still answers its unanswerable questions correctly.
TruthfulQA holds factual accuracy exactly: its ~75-token prompts are almost all answer text, so the safe preset finds nothing to cut.
GSM8K trades β8pp of accuracy for β71% cost, so measure per workload before enabling its reasoning preset. ΒΉIts input goes negative because that preset injects a Chain-of-Draft instruction whose payoff is output-side (see the frontier table).
make -C crates/llmtrim-cli/bench data # pull real corpora (gsm8k, humaneval, dolly, hotpotqa, β¦)
(cd crates/llmtrim-cli && cargo run -q --features live -- bench suite) # live A/B across all corpora (needs OPENROUTER_API_KEY)
(cd crates/llmtrim-cli/bench/scripts && PYTHONPATH=. python3 -m benchkit.tools.chart) # regenerate the chart + table
How it compares
Each tool compresses one slice of the request. llmtrim compresses input and output, leaves the cached prefix untouched to keep the prompt cache stable, and scores on whether the answer survives the cut, not on tokens removed. Both axes below use the o200k_base encoder and reproduce from this repo.
llmtrim
Headroom
RTK
caveman
Compresses
input Β· output
input
tool/CLI output
model output
Skips no-op transforms
β
β
β
n/a
One static binary
β
Python + models
β
β
Input
Input reduction (deterministic) next to answer quality from a live A/B. Quality is the drop vs llmtrim at each tool's compared setting (β held, a statistical tie; β significantly lower), so a big reduction with a β means the tool bought tokens by losing answers:
Tool
Reduction
Quality vs llmtrim
Overhead
llmtrim auto
25%
β ref
~5 ms
llmtrim aggressive
28%
β ref
~5 ms
Headroom (ML on)
24%
β tie
~0.9 s
leanctx / LLMLingua-2
52-81%
β 18% lower
~6 s
entroly
80-89%
β 42% lower
<1 ms
Overhead is the median per-call compress time (Python wall-clock, not like-for-like CPU): Headroom and leanctx run ML on CPU here (faster on a GPU) and pay a one-time model load on top (~3 s and ~4 s); llmtrim is Rust and entroly is lexical, so neither does.
auto is the quality-gated default; aggressive accepts lossy cuts where the gate holds.
Headroom drops to 0% with its ML disabled (its routers no-op on prose).
leanctx and entroly are lossy with no quality gate; entroly has no low-reduction mode.
Headroom ties at matched reduction (24-25%, n=30, not significant) but its longer answers hit the model's output-token limit and get truncated 12 times to llmtrim's 2, the output inflation behind its higher cost. leanctx (measured at 26%) and entroly (at 69%, its mildest) score significantly lower than llmtrim (n=20), and fall further at their headline reductions (vs-leanctx, vs-entroly).
Output
Output reduction by asking for terser responses, on a paid live call over 9 coding prompts:
output cut
overhead / request
caveman
80%
949 tokens
llmtrim output_terse
69%
19 tokens
The cost is the 949-token system prompt caveman resends on every request (right column); llmtrim's is 19 for nearly the same cut. Both still net-save here, so caveman's deeper cut comes out ahead only when the output it removes is worth more than the 949 tokens it adds back (vs-caveman artifact).
The tools stack: RTK shrinks CLI output, then llmtrim compresses the tool schemas on top. Full head-to-heads: crates/llmtrim-cli/bench/README.md.
Known limits
These are surfaced by the same A/B that proves the savings:
Anthropic / Gemini token counts are approximate. There's no public exact tokenizer, so a BPE proxy is used and flagged in status. OpenAI is exact.
Output savings aren't measured live. The proxy compresses input; an output saving needs the A/B counterfactual, which only the offline benchmark runs. status "saved" is input-side.
The default is quality-gated, not lossless. Lossy stages run only where the eval shows quality holds. Want a byte-faithful round-trip? Use the safe preset.
"Lossless" is input-side, not response restoration. A lossless stage preserves the information the model reads (a folded log run, a TOON-encoded array, an abbreviation legend the model decodes in-prompt), and the token gate reverts any input cut that doesn't pay off. The engine does not transform the model's response back to an original form.
Acknowledgments
Every compressor is a deterministic implementation of published research: the ideas are theirs, the engineering and the token gate are ours.
llmtrim doctor # diagnose
llmtrim doctor --fix # diagnose + apply repairs
llmtrim ensure # same repair path
Each failing check names its fix. If a request was mangled, set LLMTRIM_CAPTURE_DIR and
open an issue with the before/after pair.
If llmtrim saved you money, a β helps others find it.
Star History
Licensed under MPL-2.0. Use llmtrim freely in your stack, including commercially, with no source-disclosure obligation for your own code; the file-level copyleft applies only to modifications you make to llmtrim's own source files. Contributions via DCO sign-off.