When you're building MCP servers or sending data to LLMs, every token counts. JSON is verbose. Repeated keys, curly braces, quotes, colons. For a 50-record response, you're burning thousands of tokens on structure alone.
I ran into this problem building an MCP server for Stagent, a booking platform for music agencies. The data is gnarly: thousands of bookings, each with nested event details, venue info, artist data, financial terms. A typical query returns deeply nested objects that look like this:
{ "id": "p6YZkB4m", "status": "confirmed", "artist": { "id": "PGreBnRL", "name": "René Bourgeois" }, "event": { "id": "3G5wmW4X", "name": "Jetset Kidz", "status": "completed", "type": null }, "venue": { "name": "Aramon", "city": null, "country": null }, "buyer": { "id": "8GOePv49", "name": "Jet Set Seven Gmbh" }, "starts_at": "2013-06-18"}
That's a lot of repeated structure when you have hundreds of bookings. I looked at existing TOON packages for Laravel, but none handled nested objects properly. They'd keep the nesting intact, missing the biggest optimization opportunity.
So I built Laravel TOON.
What It Does
TOON (Token-Optimized Object Notation) is a compact, YAML-like format. The key feature is intelligent nested object flattening. Instead of preserving nested structures, it flattens them using dot notation in the header:
$bookings = [ [ 'id' => 'abc123', 'artist' => ['name' => 'Amelie Lens', 'id' => 'art1'], 'event' => [ 'name' => 'Awakenings', 'venue' => ['city' => 'Amsterdam', 'country' => 'NL'] ], 'fee' => 15000, ], [ 'id' => 'def456', 'artist' => ['name' => 'Charlotte de Witte', 'id' => 'art2'], 'event' => [ 'name' => 'Tomorrowland', 'venue' => ['city' => 'Boom', 'country' => 'BE'] ], 'fee' => 25000, ], [ 'id' => 'ghi789', 'artist' => ['name' => 'Adam Beyer', 'id' => 'art3'], 'event' => [ 'name' => 'Time Warp', 'venue' => ['city' => 'Mannheim', 'country' => 'DE'] ], 'fee' => 20000, ],]; Toon::encode($bookings);
Output:
items[3]{id,artist.name,artist.id,event.name,event.venue.city,event.venue.country,fee}: abc123,Amelie Lens,art1,Awakenings,Amsterdam,NL,15000 def456,Charlotte de Witte,art2,Tomorrowland,Boom,BE,25000 ghi789,Adam Beyer,art3,Time Warp,Mannheim,DE,20000
The nested event.venue.city and artist.name paths get flattened into column headers. Values become rows. No repeated keys. No braces. No quotes around strings that don't need them.
Real-World Benchmarks
From production data with 17,000+ bookings:
| Records | JSON | TOON | Saved | Savings |
|---|---|---|---|---|
| 10 | 2,868 | 1,223 | 1,645 | 57.4% |
| 25 | 7,355 | 3,002 | 4,353 | 59.2% |
| 50 | 14,720 | 5,924 | 8,796 | 59.8% |
| 100 | 30,316 | 12,723 | 17,593 | 58.0% |
For a typical paginated response of 50 records, that's roughly 2,200 tokens saved per request.
Configuration Options
The package includes options to squeeze out extra savings:
// config/toon.phpreturn [ // Omit null, empty strings, or false values 'omit' => ['null', 'empty'], // Strip specific keys entirely 'omit_keys' => ['created_at', 'updated_at', 'deleted_at'], // Shorten verbose keys 'key_aliases' => [ 'organization_id' => 'org_id', 'description' => 'desc', ], // Truncate long strings 'truncate_strings' => 200, // Control nesting depth (default: 3) 'max_flatten_depth' => 3,];
Use Cases
MCP Servers - Return compact data from tool calls:
use MischaSigtermans\Toon\Facades\Toon; class ListBookings extends Tool{ public function handle(Request $request): Response { $bookings = Booking::query() ->with(['event.venue', 'artist', 'event.buyer']) ->limit(50) ->get() ->map(fn ($b) => $this->formatBooking($b)); return Response::text(Toon::encode([ 'count' => $bookings->count(), 'bookings' => $bookings, ])); }}
LLM Context - Pack more into your context window:
$context = Toon::encode([ 'user' => $user->only(['id', 'name', 'role']), 'recent_bookings' => $bookings, 'stats' => $statistics,]); $response = $llm->chat("Analyze this data: {$context}");
Measure Savings - See exactly what you're saving:
$diff = Toon::diff($data); // Returns:// [// 'json_chars' => 14720,// 'toon_chars' => 5924,// 'saved_chars' => 8796,// 'savings_percent' => 59.8,// ]
Works Great with Laravel MCP
If you're using the new Laravel MCP package to expose your application to AI assistants, TOON is a natural fit. MCP tool responses often return database records, and those add up fast. Wrapping your tool responses in Toon::encode() means the AI gets the same data with fewer tokens, leaving more room in the context window for reasoning.
Installation
composer require mischasigtermans/laravel-toon
Full round-trip fidelity. Decode gets you the exact original structure back, types preserved:
$original = Toon::decode($encoded);
Find out more about Laravel TOON on GitHub, and follow Mischa’s blog: mischa.sigtermans.me