CLAUDE LABJP
PRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular pricePARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline managementTRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industriesBETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during SeptemberLIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from todayRELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yetPRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular pricePARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline managementTRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industriesBETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during SeptemberLIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from todayRELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yet
Articles/API & SDK
API & SDK/2026-05-20Advanced

Compressing Tool Results in Claude Agents — Aggregating Large Responses Without Bloating Context

When a database returns 8,000 rows, a scrape returns 200KB of HTML, or a file read returns several megabytes, dropping the raw payload into your Claude tool result wrecks both cost and quality. This guide presents a three-layer compression architecture — schema projection, summarization, and reference handles — with TypeScript examples from a production agent pipeline.

claude-agent2tool-use23context-management5mcp20production111

Premium Article

What happens when your database tool returns 8,000 rows in a single call, or your web fetch tool drops 200KB of HTML into the conversation? In my own multi-site auto-posting pipeline as an indie developer running six sites alongside an app business that has crossed 50 million cumulative downloads, this was the first wall I hit. A single tool call would consume more than 60,000 tokens, get called three or four times per loop, and saturate the context window before the agent could finish its job. Cost went up, but more painfully, output quality went down.

Tool result compression is a quiet but decisive part of agent design that compounds as you run agents longer. Anthropic's official docs do a great job explaining how to define tools, but the question of how to constrain the return side is essentially left to the implementer. Here I want to share the three-layer architecture I run in production for content generation, content auditing, and revenue analysis agents — with TypeScript samples you can adapt.

Three failure modes that bloated tool results trigger

The reasoning "the context window is bigger now, so raw payloads are fine" is wrong on both cost and quality grounds. Three things degrade at once:

  1. Direct token cost: Claude Sonnet 4.6 at $3 per million input tokens, multiplied by 60,000 tokens × 4 tool calls = $0.72 per request. A pipeline doing 200 such requests per day burns roughly $4,300 per month in pure overhead
  2. Instruction-following degradation: The longer the input, the weaker the relative weight of your system prompt and instructions. In my own runs, the rate of style-guide violations roughly doubles once cumulative tool results pass 30,000 tokens
  3. Cache efficiency collapse: With prompt caching enabled, tool results that change every call destroy your cache-hit rate, dragging it down in proportion to how many tools you call

So compression is not just a cost-optimization concern. It is a reliability-level architectural decision for the whole agent.

The three-layer compression model

The taxonomy I use in production looks like this. The top layer is safest (least information loss), the bottom layer is the most powerful (highest compression ratio). In practice I pick one layer per tool, and sometimes stack two.

Layer 1: Schema projection (return only the fields that matter)

The least lossy form of compression is to never include unnecessary fields. A Stripe Charge object has 50+ fields, but an agent doing spend analysis usually only needs id, amount, currency, created, and description.

async function searchCharges(query: SearchQuery) {
  const charges = await stripe.charges.search({
    query: query.text,
    limit: 100,
  });
 
  // 50+ fields × 100 rows ~= 100k+ tokens → 5 fields × 100 rows ~= 10k tokens
  return {
    data: charges.data.map((c) => ({
      id: c.id,
      amount: c.amount,
      currency: c.currency,
      created: c.created,
      description: c.description,
    })),
    has_more: charges.has_more,
    next_cursor: charges.data[charges.data.length - 1]?.id,
  };
}

Projection alone usually buys 70–90% token reduction. When you write MCP servers that wrap external APIs, ask yourself which fields exist for human dashboard consumption vs. which fields the agent actually uses to decide. That distinction lands you in this layer naturally.

Layer 2: Summarization (have Haiku digest the payload first)

Some tools return free-form text that schema projection cannot squeeze. Web search, file reads, log retrieval — all of these. The pattern that works best for me is a cascade where Haiku compresses the payload before it ever reaches the Sonnet/Opus loop.

import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
 
async function fetchAndSummarize(url: string, focus: string) {
  const html = await fetch(url).then((r) => r.text());
  const text = stripHtml(html).slice(0, 50_000); // about 25,000 tokens
 
  // 5,000-token input → 400-token summary
  const summary = await client.messages.create({
    model: "claude-haiku-4-5-20251001",
    max_tokens: 400,
    system: `Summarize the following web page. Focus angle: ${focus}.
Facts only. No speculation. Keep all important numbers verbatim.`,
    messages: [{ role: "user", content: text }],
  });
 
  return {
    url,
    focus,
    summary: summary.content[0].type === "text" ? summary.content[0].text : "",
    original_length: text.length,
  };
}

The critical detail is passing the focus as a tool parameter. When Claude itself declares what it wants to know at the moment of calling, Haiku produces summaries aligned with the caller's intent, which dramatically reduces follow-up turns.

This mirrors a pattern Dolice (my indie app studio operating since 2014) has used on the ad-monetization side: a cheap layer screens, an expensive layer commits. The same economic logic applies to agent design — a Haiku cascade in front of Sonnet/Opus is the AdMob mediation of LLM orchestration.

Layer 3: Reference handles (return only an ID, not the data)

When the agent doesn't need to read the payload, only act on it, the strongest move is to make the tool return "saved, here is the handle" and let Claude drill down via separate tools.

const blobStore = new Map<string, unknown>();
 
async function executeQuery(sql: string) {
  const rows = await db.query(sql);
  const handle = `query-${crypto.randomUUID()}`;
  blobStore.set(handle, rows);
 
  // Don't return thousands of rows; return the handle plus a tiny preview
  return {
    handle,
    row_count: rows.length,
    sample: rows.slice(0, 3),
    columns: rows[0] ? Object.keys(rows[0]) : [],
    note: "use `query_result_aggregate` or `query_result_filter` with the handle to drill down",
  };
}
 
async function queryResultAggregate(handle: string, groupBy: string, agg: string) {
  const rows = (blobStore.get(handle) ?? []) as Record<string, unknown>[];
  return aggregate(rows, groupBy, agg);
}

The reference-handle pattern positions Claude as an analyst issuing aggregation directives rather than someone scrolling the raw data. In production I back blobStore with Cloudflare KV or Durable Objects with a one-hour TTL.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Quantify the three failure modes that bloated tool results cause — token cost, instruction-following degradation, and cache breakdown
Adopt a three-layer compression model (projection / summarization / reference handle) with TypeScript code you can paste in
Walk away with rules of thumb for choosing the right compression layer per tool, plus production metrics to track
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

API & SDK2026-07-28
The Line That Disappears at 100K: Measuring What Tool-Output Spill Actually Keeps
When agent tool output passes 100,000 characters, the full text spills to a file and the model sees only a head-truncated preview. Here are measured survival rates from a real repository, and the output envelope I built to push decision-relevant lines to the front.
API & SDK2026-06-29
When Context Editing Made My Agent Re-run the Same Search — Field Notes on Clear Boundaries and Cache Invalidation
After turning on Context Editing to auto-clear tool results, the agent forgot what it had just read, re-ran the same tool, and the cache rebuilt every turn so costs went up. Field notes on instrumenting the silent regression and setting trigger, keep, and clear_at_least from measured data.
API & SDK2026-06-22
Putting a Ceiling on the pause_turn Loop: Running Long Server Tools Safely Unattended
A production design for continuing pause_turn safely in unattended runs, where long server tools like web_search and code execution are involved. Covers branching all four stop_reason values in one loop, capping continuations and wall-clock time, and accumulating usage across paused segments.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →