docs/features/toon-output.md
MCPProxy can re-encode tool-call results as TOON — a compact, human-readable serialization that beats JSON on large uniform tabular arrays (database rows, list endpoints, log dumps). The feature is adaptive: each result is encoded only when it is actually tabular and the TOON emission is measurably smaller than the JSON the agent would otherwise receive. Everything else passes through byte-identically.
Off by default. No behavior changes until you opt in.
The spec-083 profiler measured TOON against compact JSON on real mcpproxy payloads:
retrieve_tools) are therefore permanently out of scope.A blanket "TOON everything" switch would burn tokens on most responses to win on a few. Adaptive mode encodes only where TOON wins by a configurable margin, and by construction never produces a response larger than passthrough (the encoder compares complete emissions — marker included — before choosing).
{
"toon_output": "adaptive",
"toon_min_savings_pct": 15
}
| Field | Default | Values |
|---|---|---|
toon_output | "off" | off | adaptive | always |
toon_min_savings_pct | 15 | integer 1–90 |
off (default): all responses byte-identical to pre-feature behavior.adaptive (recommended): encode a result text block only when it (a) parses as JSON, (b) classifies as tabular-uniform, and (c) the complete TOON emission (marker + decode hint + body) is at least toon_min_savings_pct percent smaller than the exact passthrough block. Near-ties pass through — they are not worth the agent-side decode risk.always: encode every JSON-parseable block — any JSON value, not just tabular — regardless of the size comparison. Benchmarking/debugging only; can increase token cost. Non-JSON text still passes through.Changes hot-reload: save the config file and the next tool call uses the new mode. No restart, no client reconfiguration.
{
"toon_output": "adaptive",
"mcpServers": [
{ "name": "legacy-agent", "url": "https://...", "toon_output": "off" }
]
}
A non-empty per-server toon_output overrides the global for that server's tools (precedence: per-server > global > default off). Use it to exempt a server whose consuming agent cannot tolerate non-JSON results.
A text block is tabular-uniform when it is:
Empty arrays, arrays of non-objects, scalars, and nested structures do not qualify. The classifier is deliberately conservative — even when it misclassifies, the size comparison is the backstop: nothing is ever emitted larger than passthrough in adaptive mode.
Every encoded block is prefixed with one deterministic marker line (the decode contract with agents; also echoed in the call_tool_* tool descriptions so agents learn it in-session):
[mcpproxy:toon/v1] TOON-encoded JSON (toon-format.org); decode to JSON before reuse - tool arguments must still be sent as JSON.
users[3]{id,name,active}:
1,Ada,true
2,Linus,true
3,Grace,false
Passthrough blocks carry no marker and are unchanged. Tool arguments always remain JSON — only result rendering is affected.
The encoder sits at exactly one seam — the text-block rendering of call_tool_read/call_tool_write/call_tool_destructive responses — positioned so every existing safety stage is unaffected:
structuredContent; only the text rendering is re-encoded.Never encoded: retrieve_tools and all listings (measured net-negative), code-execution paths (agent-written programs expect JSON), and direct-mode server tools (unmodified upstream passthrough).
Every tool call records its per-block encoding decision in the activity log metadata:
mcpproxy activity list --request-id <id> -o json
# metadata.toon_output.mode → resolved mode
# metadata.toon_output.blocks[].outcome ∈
# encoded | passthrough-not-tabular | passthrough-below-threshold | passthrough-error
# bytes_before / bytes_after present on encoded blocks
Byte savings approximate token savings for this payload class; the spec-083 profiler reports true token deltas per release.