Toolgz – cut LLM tool-definition tokens ~80% without hurting accuracy Toolgz, a new open-source library, cuts LLM tool-definition tokens by approximately 80% without hurting accuracy, reclaiming up to 40k tokens of context per request. In a 420-run cross-provider sweep across four frontier models (Claude Opus 5, Grok 4.5, Gemini 3.1 Pro Preview, GPT 5.6 Sol), Toolgz achieved 60/60 tasks completed with zero hallucinated tool names and zero malformed arguments, while reducing prompt tokens by 71–85% and lowering costs by 7–78%. Your agent spends 30–50k tokens of context on tool definitions before the user types a word. toolgz gets ~80% of it back. 420-run cross-provider sweep measured-results · 4 frontier models · zero runtime dependencies · generated before/after /dperussina/toolgz/blob/main/docs/BEFORE-AFTER.md npm install toolgz You connect a few MCP servers. Each ships 20–50 tools. Every tool is a JSON Schema with a sentence of prose per parameter. That block renders at the front of every single request . A realistic tool definition is ~420 tokens, and roughly 400 of them are prose the model doesn't need in order to pick correctly. Fifty tools is 20k tokens. A hundred is 40k. Prompt caching makes those tokens cheap . It does not make them take up less room . Reclaiming the room is what this does. js import { compress, forAnthropic } from "toolgz"; const c = compress myTools ; // your existing MCP/SDK tool array const { tools, system } = forAnthropic c ; // send these instead Then translate the model's call back before you dispatch: js const r = c.resolve block.name, block.input ; if r.kind === "call" await myDispatch r.name, r.args ; // real name, real args myDispatch gets exactly what it got before. Nothing downstream changes. Four frontier models, seven strategies, five tool-selection tasks, 3 reps — 420 runs on the current sweep, 1,200+ across all rounds. Every raw per-run record is committed in bench/results/ /dperussina/toolgz/blob/main/bench/results ; recompute any figure with npx tsx bench/analyze-multi.ts .| Provider | Model | Tool block | Prompt tokens | Cost | Latency | Tasks | |---|---|---|---|---|---|---| | Anthropic | claude-opus-5 | 9,242 → 1,284 | 30,817 → 4,628 −85% | −78% | 15.0s → 12.1s | 15/15 | | xAI | grok-4.5 | 6,421 → 775 | 17,522 → 2,663 −85% | −70% | 6.1s → 4.6s | 15/15 | gemini-3.1-pro-preview | 5,264 → 732 | 10,948 → 2,302 −79% | −62% | 5.6s → 5.5s | 15/15 | | | OpenAI | gpt-5.6-sol | 2,752 → 573 | 7,694 → 2,196 −71% | −7% | 6.8s → 5.6s | 15/15 | Reasoning is enabled on all four at high effort, so this is a like-for-like frontier comparison. 60/60 tasks completed, zero hallucinated tool names, zero malformed arguments — and it is faster than uncompressed on every provider. That was the thing to disprove, and we tried hard to. The task suite is built from deliberately confusable tool clusters — search issues vs list issues , comment-vs-update, approve-vs-merge, the same three products side by side — where the correct choice turns on the tool name that compression takes away. The model doesn't lose the ability to choose — it converts a recall problem into a retrieval problem and looks up what it needs. The default map style exists because of the red cell: bare tool names failed on grok-4.5 deterministically , 3 of 3 attempts on one scenario, answering with zero tool calls and no error raised. Naming the required arguments fixed it. The first cross-provider sweep found cost going up 15% on OpenAI even while context fell 69%. The dispatcher was spending extra turns, and on a reasoning model every turn pays for a fresh round of thinking. So we captured the calls that were being rejected instead of guessing, and found three bugs in this library : models pass query to a parameter named q 14 of 18 rejections , they sometimes call the map code as the tool name, and they sometimes pass arguments flat instead of nested. Fixing all three took OpenAI from +15% to −7% and drove malformed arguments to zero on every provider . OpenAI's −7% is still the smallest saving, and honestly so: reasoning output dominates its bill, so a smaller prompt moves the total less. Context-window occupancy remains the primary claim — cost follows from it, by an amount that depends on your reasoning settings. Ask the library. It returns 1 or 3, never 2, and explains itself: js import { recommendLevel } from "toolgz"; const { level, reason } = recommendLevel myTools ; | Level | Sends | Real names | Provider schema enforcement | Use when | |---|---|---|---|---| 1 | one native tool each, signature-line descriptions | yes | yes | default. Small or wide-and-sparse tool sets. Zero measured downside. | | 2 | one compound tool per namespace | yes | no | you need readable op names on the wire. Otherwise skip. | 3 | one dispatcher + one lookup tool | codes | no | large, deep tool sets. The 80% number above. | Level 0 is a passthrough, for A/B testing inside your own app. Level 1 is free — measured: fewer tokens, zero malformed arguments, zero extra turns, latency no worse. Level 2 is dominated by level 3 on every axis, including producing more malformed arguments; it is not a stepping stone. Two tools on the wire regardless of how many you start with, and a map in the system prompt behind a cache breakpoint: