#!/usr/bin/env node
/**
* TOON MCP Server
*
* Model Context Protocol server that provides tools for encoding
* various data formats to TOON (Token-Oriented Object Notation)
* for reduced token usage with LLMs.
*/
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
const {
CallToolRequestSchema,
ListToolsRequestSchema,
} = require('@modelcontextprotocol/sdk/types.js');
const { ToonEncoder } = require('./toon-encoder.js');
const server = new Server(
{
name: 'toon-mcp',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Define available tools
const tools = [
{
name: 'toon_encode',
description: `Convert data to TOON (Token-Oriented Object Notation) format to reduce token usage by 50-70%.
Supports: JSON, CSV, TSV, XML, HTML tables, YAML, and raw objects.
TOON uses a header-based format where field names are defined once:
- JSON: {"id":1,"name":"test"} → [id,name]\\n1,test
- Reduces repetitive keys in arrays of objects
Use this before sending large datasets to LLMs to save tokens and costs.`,
inputSchema: {
type: 'object',
properties: {
data: {
type: 'string',
description: 'The data to encode (JSON, CSV, XML, YAML, or other supported format)',
},
format: {
type: 'string',
enum: ['auto', 'json', 'csv', 'tsv', 'xml', 'html-table', 'yaml'],
description: 'Input format (default: auto-detect)',
},
},
required: ['data'],
},
},
{
name: 'toon_decode',
description: 'Decode TOON format back to JSON object or array.',
inputSchema: {
type: 'object',
properties: {
toon: {
type: 'string',
description: 'TOON-formatted data to decode',
},
},
required: ['toon'],
},
},
{
name: 'toon_analyze',
description: 'Analyze data and show potential token savings with TOON encoding.',
inputSchema: {
type: 'object',
properties: {
data: {
type: 'string',
description: 'The data to analyze',
},
format: {
type: 'string',
enum: ['auto', 'json', 'csv', 'tsv', 'xml', 'html-table', 'yaml'],
description: 'Input format (default: auto-detect)',
},
},
required: ['data'],
},
},
{
name: 'toon_optimize_prompt',
description: `Optimize a prompt containing data for token efficiency.
Detects data structures within the prompt and converts them to TOON format,
adding instructions for the LLM to understand the format.
Returns the optimized prompt with estimated token savings.`,
inputSchema: {
type: 'object',
properties: {
prompt: {
type: 'string',
description: 'The full prompt containing data to optimize',
},
},
required: ['prompt'],
},
},
];
// Handle tool listing
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools,
}));
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'toon_encode': {
const { data, format = 'auto' } = args;
const detectedFormat = format === 'auto' ? ToonEncoder.detectFormat(data) : format;
const encoded = ToonEncoder.encode(data, { format: detectedFormat });
const stats = ToonEncoder.getStats(data, encoded);
return {
content: [
{
type: 'text',
text: `**TOON Encoded Output**
\`\`\`
${encoded}
\`\`\`
**Stats:**
- Format detected: ${detectedFormat}
- Original: ${stats.original.chars} chars (~${stats.original.tokens} tokens)
- Encoded: ${stats.encoded.chars} chars (~${stats.encoded.tokens} tokens)
- Savings: ${stats.savings.chars} chars (~${stats.savings.tokens} tokens) = **${stats.savings.percent}**
**LLM Instructions:**
Include this with your prompt:
> Data is in TOON format: [header] defines columns, followed by comma-separated rows.`,
},
],
};
}
case 'toon_decode': {
const { toon } = args;
const decoded = ToonEncoder.decode(toon);
return {
content: [
{
type: 'text',
text: `**Decoded JSON:**
\`\`\`json
${JSON.stringify(decoded, null, 2)}
\`\`\``,
},
],
};
}
case 'toon_analyze': {
const { data, format = 'auto' } = args;
const detectedFormat = format === 'auto' ? ToonEncoder.detectFormat(data) : format;
const encoded = ToonEncoder.encode(data, { format: detectedFormat });
const stats = ToonEncoder.getStats(data, encoded);
// Cost estimation for different models
const costs = {
'gpt-4o-mini': 0.15,
'gpt-4o': 2.50,
'claude-sonnet': 3.00,
'claude-opus': 15.00,
};
const costAnalysis = Object.entries(costs).map(([model, price]) => {
const origCost = (stats.original.tokens / 1000000) * price;
const encCost = (stats.encoded.tokens / 1000000) * price;
return `- ${model}: $${origCost.toFixed(6)} → $${encCost.toFixed(6)} (save $${(origCost - encCost).toFixed(6)})`;
}).join('\n');
return {
content: [
{
type: 'text',
text: `**TOON Analysis Report**
**Format:** ${detectedFormat}
**Size Comparison:**
| Metric | Original | TOON | Savings |
|--------|----------|------|---------|
| Characters | ${stats.original.chars} | ${stats.encoded.chars} | ${stats.savings.chars} |
| Est. Tokens | ${stats.original.tokens} | ${stats.encoded.tokens} | ${stats.savings.tokens} |
| Reduction | - | - | **${stats.savings.percent}** |
**Cost per 1M requests:**
${costAnalysis}
**Recommendation:** ${parseFloat(stats.savings.percent) > 30 ? '✅ TOON encoding recommended' : '⚠️ Minimal savings - consider if worth the complexity'}`,
},
],
};
}
case 'toon_optimize_prompt': {
const { prompt } = args;
// Find JSON arrays in the prompt
const jsonPattern = /\[[\s\S]*?\{[\s\S]*?\}[\s\S]*?\]/g;
let optimizedPrompt = prompt;
let totalOrigTokens = 0;
let totalEncTokens = 0;
let replacements = 0;
const matches = prompt.match(jsonPattern);
if (matches) {
for (const match of matches) {
try {
const parsed = JSON.parse(match);
if (Array.isArray(parsed) && parsed.length > 0) {
const encoded = ToonEncoder.encode(parsed);
const origTokens = ToonEncoder.estimateTokens(match);
const encTokens = ToonEncoder.estimateTokens(encoded);
if (encTokens < origTokens * 0.7) { // Only replace if >30% savings
optimizedPrompt = optimizedPrompt.replace(match, encoded);
totalOrigTokens += origTokens;
totalEncTokens += encTokens;
replacements++;
}
}
} catch {
// Not valid JSON, skip
}
}
}
const toonInstructions = replacements > 0
? '\n\nNote: Data sections are in TOON format where [headers] define columns and subsequent lines are comma-separated values.\n'
: '';
return {
content: [
{
type: 'text',
text: `**Optimized Prompt**
${optimizedPrompt}${toonInstructions}
---
**Optimization Stats:**
- Data sections found: ${matches?.length || 0}
- Sections optimized: ${replacements}
- Tokens saved: ~${totalOrigTokens - totalEncTokens} (${totalOrigTokens > 0 ? ((1 - totalEncTokens / totalOrigTokens) * 100).toFixed(1) : 0}%)`,
},
],
};
}
default:
return {
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
isError: true,
};
}
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${error.message}` }],
isError: true,
};
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('TOON MCP Server running on stdio');
}
main().catch(console.error);