Every MCP server my agents used was paying twice.
The first payment is the tool list. When an agent connects to an MCP server, every tool's name, description and JSON schema goes into the context window. It sits there on every turn, whether the model calls the tool or not. The second payment is the result. A tool call returns the whole response, and the whole response lands in context. Nobody trims it.
On the nine MCP servers I run day to day, the raw tool listing is 236,818 bytes. That is before the agent has done anything.
So I built declick. It reads a source once and writes a CLI with named verbs. The agent runs one line from the shell and gets trimmed JSON back.
What it actually does
Point it at something and give it a name:
npm i -g declick
declick add https://petstore3.swagger.io/api/v3/openapi.json --name petstore
declick run petstore get-user-by-name user1 --fields username,email
That is an OpenAPI spec. The same command takes an MCP server (stdio or streamable HTTP), a GraphQL schema, a Postman or Insomnia collection, a HAR capture, a SQLite database, another CLI's own help text, a web page, or a Windows app. Ten engines, and every one of them writes the same shape of CLI.
Every generated CLI honors one contract. Output is a JSON envelope with ok, data and meta. --fields and --limit cut the result before it reaches the model. --dry-run works on anything that mutates. Five exit codes: 0 ok, 1 error, 2 not found, 3 blocked by policy, 4 auth needed. The fourth line above hits a live server. Try get-pet-by-id instead and you get exit 4, because the spec declares an API key and you have not set one. That is the contract doing its job, not a bug in the demo.
The part the agent cares about is declick describe <name>. It fits the whole surface of an adapter in under 500 tokens, so the model can afford to read it every single time instead of carrying 258 schemas around on the off chance.
The number, and the caveat
I did not want to claim a saving, I wanted to measure one. scripts/bench-tokens.mjs in the repo connects to real MCP servers, records what an MCP client would put in context for the tool listing, and compares it to declick describe for the same servers.
Nine servers, 258 tools. Raw: 236,818 bytes. declick: 58,309 bytes. 4.1x smaller.
The caveat matters. A single call's payload is not smaller through declick. The envelope adds a little, so a bare call is about 0.8x. The saving is the surface and the trimming: the agent reads a small description instead of a huge one, and asks for two fields instead of forty. The method and the caveats are in docs/bench.md and you can rerun it on your own adapters.
Why a CLI when MCP already exists
MCP is fine. The problem is where the cost lands.
With MCP the schemas are pushed into context up front and the results arrive untrimmed. With a CLI the surface is read on demand and the output is cut with --fields before it lands. The model decides what it needs, then pays for only that.
There is a second reason that ended up mattering more for me. Subagents in Claude Code have no MCP tools. Every fan-out I ran either re-fetched data in the main loop or gave the workers nothing. A CLI works from any Bash tool, so a subagent gets exactly the same GitHub, docs and database access the main loop has. That one change made parallel work worth doing.
And declick compiles MCP servers too, so you do not have to pick. declick setup reads the MCP servers your agents already have configured, builds an adapter for each, and drops a rules block into CLAUDE.md or AGENTS.md so the agent uses them without being told. declick setup --revert puts every file back, byte for byte.
Things that landed in 0.6.0
The second call used to pay the first one's startup. declick daemon start keeps stdio MCP servers warm between calls. On a server that takes 600 ms to boot, a call drops from 703 ms to 59 ms.
There is now a default ceiling of 8 KB on data. A list drops tail rows, a string gets sliced, and an object keeps every key with the biggest values replaced by a note like <41208 bytes; add --fields or --limit>. The shape survives, so the agent can write the right --fields on the next call instead of guessing.
--where k=v filters rows on the machine that has them, before --fields and --limit. --cache <seconds> answers read-only verbs from a stored response. And declick audit --sum adds up what every adapter actually cost this session in bytes, one row per adapter, so the saving is a number you can read rather than one I told you.
How it got built, honestly
Most of the seven thousand lines were written by Claude Code, including the YAML parser, the CDP client and the MCP client. Zero runtime dependencies was the constraint I set and the agents kept to it. 631 tests.
The agents were also the first users, and they found the sharp edges before any human did. An agent that gets exit 4 with the env var name in the message fixes its own auth. An agent that hits the 8 KB ceiling with a hint naming the fields that fit writes the --fields on the next call.
Two lessons cost me something. The DashClaw guard, which lets a policy server approve or block a mutating call, shipped with a wire format bug that 566 tests did not catch, because every guard test was a mock. There is a contract test now that validates the real body against DashClaw's real schema. And this morning, the Windows CI job started timing out on one test. The test spawned npx node, and npx does not look at your PATH. It resolves node as the npm package called node and downloads a whole runtime from the registry. Locally that was cached, so it passed for days. A cold runner paid 13 seconds for it until the suite grew and it tipped over the timeout.
The license, stated plainly
0.3.0 on npm is MIT and stays MIT. Releases after it are under the Elastic License 2.0: read it, run it, change it, ship it inside your own product, just do not resell it as a managed service. Free for individuals and for companies under ten people. Ten or more buy seats at $19 per developer per month or $190 per year. Nothing in the CLI checks a key today. I would rather say that here than have you find it in the README.
Where I want to hear it is wrong
Still open: a macOS and Linux desktop backend (the desktop engine is Windows only today), and the web engine needs a recipe file per site rather than guessing.
If you run agents against MCP servers, try declick setup and then declick audit --sum after a day. If the contract is wrong for your agents, tell me where. That is more useful to me than praise. The repo is github.com/ucsandman/declick, the docs are at declick.dev, and the Show HN thread from today is here.
