A trivial test through tiktoken [1] (though technically you really have to match the tokenizer to the specific LLM) would have shown them that ∅ was a poor choice.
Even from the perspective of learned training data, you can probably just intuit that from a frequency standpoint alone the empty-set symbol ∅ can’t possibly have appeared that often outside of things like set theory and logic.
It took me way more time than expected, I was thinking: "Just wrap the REST API, 2h, done".
The MCP payload has nothing to do with the REST API one. Because you need to make it interpretable and context efficient even so it is structured data.
It was really interesting work and I suppose very little people are taking the time to rethink what is sent over the wire while creating a MCP server. If so, we would not have MCPs with the minimal payload being 500kB of JSON soup.
If you send my MCP through your "save token filter", I can guarantee you, that you will have trash down the line.
One thing that I noticed is that it’s often better to return tool names with argument names, i.e. return “search_web(query)” instead of just “search_web” when listing tools. Otherwise models often tend to hallucinate argument names and an extra turn is required to correct the mistake.
One additional advantage that such tools provide is that when you use different coding agents you don’t have to set up all the MCP servers in every agent, you just set up one (or point the agent to the cli like in this project).
People want a token efficient MCP CLI client. Whether this actually is one is less relevant.
Tokenization is not some black box, you can run tokenizers and check them.
You're just returning the name of the tool, the rest of the information (description/input schema) is definitely lost. Cut to the LLM making mistakes in calling the tool with incorrect schema or calling the wrong tools altogether, recovering, wasting tokens and cycles.
Leave alone 97%.
> Your agent calls 20 tools. Each returns 500-3,000 tokens wrapped in {"content":[{"type":"text","text":"..."}]}.
This is a problem with your tool design. Most MCPs are fully vibe coded without any thought about tool selection.
> On a 128K context window, that's 30-55% gone. Not on work. On syntax.
Tool output is not "syntax" you donkey clanker.
Again, use the code approach, let the LLM filter out the JSON using tools. This TOON thing is just vibes. Most of the time your tool output should not even be JSON. It should be well formatted markdown. In cases where it's large structured data, your LLM should have tools (code / jq) to dissect it. So TOON is pointless.
search_web fetch_url
~~~
But, why not just use CLIs for each tool? That seems to be where things are going anyway
And using MCP as an internal communication method seems odd when you could use the APIs directly
Been experimenting with it in the OpenCode V2 beta and it's pretty great. The combination of tool search, call chaining and field projections feels just right and saves a lot of context. LLMs are good at writing code, who would have thought that?
Q: is there a measurable difference compared to harnesses with tool search?