Laravel Packages

Laravel TOON: Reduce LLM Token Usage by 40-60%

Published
Laravel TOON: Reduce LLM Token Usage by 40-60% image

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.php
return [
// 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

Sponsored

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
Laravel Starter Kits Now Ship with Vite+ image

Laravel Starter Kits Now Ship with Vite+

Read article
Pessimistic Locking in Laravel Eloquent with refreshForUpdate() image

Pessimistic Locking in Laravel Eloquent with refreshForUpdate()

Read article
Mask Query Bindings in Laravel Exception Messages image

Mask Query Bindings in Laravel Exception Messages

Read article
whereBinary(): Case-Sensitive MySQL Queries in Laravel image

whereBinary(): Case-Sensitive MySQL Queries in Laravel

Read article
Compile PHP to Native Binaries with TypePHP image

Compile PHP to Native Binaries with TypePHP

Read article
State of Laravel 2026 Survey Is Now Open image

State of Laravel 2026 Survey Is Now Open

Read article