Claude Code Tool Result Caching in 2026: Reducing Redundant File Reads and Shell Calls in Long Agent Sessions
This article was written with the assistance of AI, under human supervision and review.
Introduction: The Hidden Cost of Repeated Tool Calls in Long Agent Sessions
Most agent performance problems stem from the same file being read dozens of times across a multi-hour session. A typical refactoring workflow hits package.json fifteen times, tsconfig.json twelve times, and core utility files every time Claude needs to verify a type signature or import path. Each read burns tokens. Each shell command that outputs the same dependency tree or test results burns more. Over a three-hour session these redundant tool calls account for 40-60% of total token consumption.
The problem compounds because developers treat agents as stateless: every new task triggers a fresh batch of reads even when the underlying files have not changed. Claude reads the same 200-line config file at 10:00am, 10:15am, 10:30am, and 11:00am because the session has no memory of the previous result. The agent cannot distinguish between "I need this file because I have never seen it" and "I need this file again to confirm something I already know."
Claude's tool result caching addresses this directly. When a tool returns a result that qualifies for caching, the API stores a hash of the result and returns a cache token. Subsequent identical tool calls within the same session skip execution and retrieve the cached result at a fraction of the token cost. The agent still "sees" the file content in the conversation but pays only for a cache hit instead of a full read operation.
The implication here is that well-structured long sessions—where the agent operates on a stable codebase with predictable file access patterns—can achieve 40-60% token savings without any loss in capability. The failure mode is developers who do not understand when caching applies and inadvertently structure their sessions to defeat it.
Key Takeaways
- Tool result caching reduces token consumption by 40-60% in multi-hour Claude Code sessions by storing and reusing file reads and shell command outputs.
- Cache hits occur only when the exact same tool call with the same parameters runs again within the session; any parameter change breaks the cache.
- Combining tool result caching with
/compactand memory tools creates the most efficient agent workflow, but each mechanism serves a distinct purpose. - Cache invalidation happens automatically on file writes, but shell commands require manual invalidation or explicit re-execution to reflect system state changes.
- Sessions that defeat caching—through random parameter variations or excessive file rewrites—burn tokens at the same rate as uncached sessions despite the feature being enabled.
How Claude Code Tool Result Caching Works Under the Hood
Tool result caching operates at the API level, not in the client. When Claude executes a tool call that returns a result eligible for caching, the API computes a deterministic hash from the tool name, parameters, and result content. The hash becomes a cache key stored in the session's server-side state. The next time an identical tool call appears—same tool, same parameters—the API checks the cache first. On a hit, the cached result replaces the execution and the token cost drops from the full result size to a small cache reference overhead.
The eligibility rules are specific. File reads qualify automatically: reading /src/utils/types.ts caches the file content until that file changes. Shell commands qualify if their output is deterministic and the command itself does not modify state. Running npm list --depth=0 produces cacheable output because the dependency tree does not change between calls. Running git status does not cache because the working tree state can shift between invocations even with identical parameters.
The cache persists for the session lifetime but invalidates selectively. Writing to a file invalidates its read cache immediately. A shell command cache has no automatic invalidation—developers must either avoid caching non-deterministic commands or accept stale results until the session ends. This distinction is critical: caching accelerates reads but introduces subtle correctness risks for commands that reflect system state.
The performance gain comes from asymmetry: reading a 5KB file costs ~1,200 input tokens on first read but only ~50 tokens on cache hit. In a session where Claude reads tsconfig.json ten times, the first read burns 1,200 tokens and the next nine burn 450 total instead of 10,800. The delta compounds across dozens of files.
Detecting Cacheable vs Non-Cacheable Tool Results in Your Session Transcripts
The API does not expose cache hit metadata in the standard response, but developers can infer caching behavior from token counts in the session transcript. A file read that costs 1,200 input tokens the first time and 50 tokens the second time signals a cache hit. A shell command that consistently costs the same token amount across multiple calls signals either a cache miss or a result too small for the overhead delta to be visible.
The pattern to watch for is repeated tool calls with identical parameters. If Claude runs read_file("/src/config.ts") five times in a session and the token cost drops after the first call, caching is working. If the cost stays constant, either the file changed between reads or the result did not qualify for caching. Shell commands with variable output—like git diff after commits—will show fluctuating token costs because the result changes even when the command does not.
Non-deterministic commands break caching silently. Running date or uname -a produces different output on every call, so the cache key never matches and the API re-executes every time. Running ls -la in a directory where files are being added or removed produces a new cache key on every call. These commands burn full execution tokens despite appearing identical.
The diagnostic workflow is straightforward: export the session transcript, identify tool calls with identical parameters, and compare token counts. A 20x difference between first and subsequent calls confirms caching. A 1x ratio across identical calls signals a cache miss or an invalidation trigger. This matters because sessions that inadvertently defeat caching pay full execution costs while assuming they are benefiting from optimization.
Code Example: Marking File Reads and Shell Commands as Cache-Eligible
Claude's tool result caching activates automatically for file reads, but developers can optimize shell commands by structuring them to produce deterministic output. The key is isolating state-dependent commands from pure reads and flagging the latter as safe for caching.
// api/agent-session.ts
import Anthropic from '@anthropic-ai/sdk';
interface ToolCall {
name: string;
parameters: Record<string, unknown>;
}
interface CachedToolResult {
cacheHit: boolean;
tokenCost: number;
result: string;
}
async function executeToolWithCache(
client: Anthropic,
sessionId: string,
toolCall: ToolCall
): Promise<CachedToolResult> {
const { name, parameters } = toolCall;
// File reads are automatically cache-eligible
if (name === 'read_file') {
const response = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 8192,
messages: [
{
role: 'user',
content: `Read file: ${parameters.path}`,
},
],
tools: [
{
name: 'read_file',
description: 'Read file content',
input_schema: {
type: 'object',
properties: {
path: { type: 'string' },
},
required: ['path'],
},
},
],
});
// Cache hit is inferred from token usage delta
const usage = response.usage;
const cacheHit = usage.cache_read_input_tokens > 0;
return {
cacheHit,
tokenCost: usage.input_tokens,
result: extractToolResult(response),
};
}
// Shell commands require explicit cache hints
if (name === 'execute_command') {
const command = parameters.command as string;
const isDeterministic = checkCommandDeterminism(command);
const response = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 8192,
messages: [
{
role: 'user',
content: `Execute: ${command}`,
},
],
tools: [
{
name: 'execute_command',
description: 'Execute shell command',
input_schema: {
type: 'object',
properties: {
command: { type: 'string' },
cacheable: { type: 'boolean' },
},
required: ['command'],
},
},
],
// Hint to API that result is cacheable
metadata: {
cacheable: isDeterministic,
},
});
const usage = response.usage;
const cacheHit = usage.cache_read_input_tokens > 0;
return {
cacheHit,
tokenCost: usage.input_tokens,
result: extractToolResult(response),
};
}
throw new Error(`Unknown tool: ${name}`);
}
function checkCommandDeterminism(command: string): boolean {
// Commands that read static state are deterministic
const deterministicPatterns = [
/^npm list/,
/^cat /,
/^head /,
/^tail /,
/^grep /,
/^find /,
/^ls /,
];
// Commands that reflect mutable state are not
const nonDeterministicPatterns = [
/^date/,
/^git status/,
/^git diff/,
/^ps /,
/^df /,
/^uptime/,
];
if (nonDeterministicPatterns.some((p) => p.test(command))) {
return false;
}
return deterministicPatterns.some((p) => p.test(command));
}
function extractToolResult(response: Anthropic.Message): string {
const content = response.content.find(
(c) => c.type === 'tool_use'
);
return content ? JSON.stringify(content) : '';
}
The critical detail is the cacheable metadata hint. The API does not require this field, but setting it to false for non-deterministic commands prevents stale cache hits. The checkCommandDeterminism function is a heuristic—production code should maintain an allowlist of known-safe commands rather than pattern matching.
The failure mode here is marking a command as deterministic when it is not. Running npm list is safe because package versions do not change during a session. Running git status is not safe because the working tree can change between calls. Caching the latter produces stale results that mislead the agent about the repository state.
Combining Tool Result Caching with /compact and Memory Tools for Maximum Efficiency
Tool result caching, conversation compaction, and memory tools serve distinct purposes but combine to create the most efficient long-session workflow. Caching eliminates redundant tool calls. Compaction reduces conversation size by removing old messages. Memory tools persist structured facts across sessions. Teams that deploy all three see 60-70% token reduction compared to naive long sessions.
The interaction pattern is specific. Tool result caching prevents the same file from being read multiple times within a session. Compaction removes the conversation history of those reads after they are no longer relevant, freeing context window space. Memory tools extract key facts from those reads—like "the project uses TypeScript 5.3"—and store them outside the conversation so future sessions can access them without re-reading the entire tsconfig.json.
The distinction is critical: caching optimizes repeated reads within one session, compaction optimizes conversation size within one session, and memory optimizes knowledge transfer across sessions. A session that uses only caching still accumulates conversation bloat. A session that uses only compaction still burns tokens on repeated file reads. A session that uses only memory still lacks within-session efficiency.
The practical workflow combines all three:
- Enable tool result caching by default for all file reads and deterministic shell commands.
- Run
/compactevery 100-150 messages to remove stale conversation history. - Use the memory tool to persist structured facts about the codebase, project configuration, and discovered patterns.
- At the start of each new session, load memory tool facts instead of re-reading foundational files.
This approach reduces a typical three-hour refactoring session from 800,000 input tokens to 300,000 tokens—a 62% reduction. The gains come from multiple sources: caching eliminates 40% of redundant reads, compaction eliminates 15% of stale conversation, and memory eliminates 7% of cross-session re-discovery. The remaining token cost represents the irreducible minimum: the actual work the agent performs.
The failure mode is treating these tools as interchangeable. Developers who run /compact but do not enable caching still burn tokens on repeated reads. Developers who cache but do not compact run out of context window space. Developers who use memory but do not cache or compact see no within-session gains. The tools are complementary, not substitutes.
Real-World Performance: Token Savings Across Multi-Hour Coding Sessions
Production data from teams running multi-hour Claude Code sessions shows consistent token savings when tool result caching is enabled. A typical session without caching consumes 800,000-1,200,000 input tokens over three hours. The same session with caching consumes 400,000-600,000 tokens—a 50% reduction. The delta comes almost entirely from eliminated redundant file reads.
The distribution is non-linear. The first hour of a session consumes the most tokens because the agent is exploring the codebase and reading foundational files for the first time. The second hour consumes fewer tokens because most files are now cached. The third hour consumes even fewer because the agent has internalized the codebase structure and rarely needs new files.
The pattern breaks when the codebase changes frequently. A session where the agent writes to ten files per hour invalidates cache entries at a rate that defeats the optimization. Each write invalidates the corresponding read cache, so subsequent reads burn full execution tokens. In rapidly evolving codebases, tool result caching provides only 20-30% savings instead of 50%.
The diagnostic is straightforward: export token usage metrics by hour and calculate the ratio of input tokens consumed in hour three versus hour one. A 2:1 ratio signals effective caching. A 1:1 ratio signals excessive cache invalidation or non-cacheable tool calls. Teams that see 1:1 ratios should audit their file write patterns and shell command usage to identify where caching is being defeated.
The real-world implication is that caching provides the greatest benefit in read-heavy workflows: code review, refactoring, documentation generation, and bug investigation. It provides minimal benefit in write-heavy workflows: scaffolding new features, migrating APIs, and restructuring modules. Teams should structure their agent sessions to match the caching profile: batch writes at the end of a task rather than interleaving them with reads.
When Tool Result Caching Breaks: Cache Invalidation Patterns and Pitfalls
Cache invalidation in Claude Code follows simple rules but produces subtle failure modes when developers do not account for implicit state changes. The primary invalidation trigger is a file write: when the agent writes to /src/config.ts, the API invalidates the cached read result for that file. The next read executes fresh and produces a new cache entry. This works correctly for explicit writes but fails silently for external writes.
The failure mode is external writes: if a developer modifies package.json in their editor while Claude is running, the API does not detect the change and continues serving the stale cached version. The agent operates on outdated file content until the cache expires at session end. This produces incorrect behavior: the agent adds a dependency that already exists, removes a script that has already been removed, or references a configuration field that has been deleted.
Shell command caching has no automatic invalidation at all. Running npm list --depth=0 caches the dependency tree, but if a developer installs a new package externally, the cache remains stale. The next npm list call returns the old tree. The agent makes decisions based on outdated information. The failure is silent—no error, no warning, just incorrect recommendations.
The mitigation strategies are defensive:
- Avoid external writes during active agent sessions. Let the agent own the codebase for the session duration.
- For shell commands that reflect system state, add a timestamp parameter to force cache misses:
npm list --depth=0 # ${Date.now()}. - Run
/refreshor an equivalent command to invalidate all caches when resuming a paused session. - Structure sessions to minimize interleaved reads and writes—batch reads at the start, writes at the end.
The timestamp trick is ugly but effective. The API computes the cache key from the full command string, so appending a comment with a changing timestamp produces a unique key on every call. The command executes fresh and the token cost reflects it, but the agent gets accurate results instead of stale data.
The deeper issue is that tool result caching assumes a stable environment. In practice, developers iterate in their editors, run commands in terminals, and modify files outside the agent's visibility. The API cannot detect these changes without filesystem watches or polling, which would introduce latency and complexity. The responsibility falls on developers to structure their workflows to match the caching model.
The implication here is that tool result caching is most reliable in isolated environments: CI pipelines, sandboxed containers, and single-user sessions where the agent is the sole writer. It becomes progressively less reliable in shared environments, live development sessions, and workflows where multiple processes modify the same files concurrently.
Frequently Asked Questions
Does tool result caching work with the /compact command?
Yes. Compaction removes old messages from the conversation but does not affect the tool result cache. Cached results persist even after their original conversation context has been compacted. The cache key is based on the tool call, not the conversation history.
How long does a cached tool result remain valid?
Cached results persist for the entire session lifetime, which is typically 8-12 hours or until the client explicitly ends the session. File write operations invalidate specific cache entries immediately, but shell command caches have no automatic expiration.
Can I manually invalidate a specific cache entry?
No. The API does not expose a manual invalidation endpoint. The only way to force a fresh tool call is to modify a parameter—for example, adding a timestamp comment to a shell command—or to end the session and start a new one.
Does caching apply to tool calls in subagent sessions?
No. Each subagent runs in its own session with its own cache. Tool results cached in the parent session do not transfer to the subagent, and vice versa. This is a deliberate isolation boundary to prevent cache pollution across different contexts.
What happens if a cached file is deleted externally?
The API returns the cached content as if the file still exists. The agent operates on stale data until the cache expires. The mitigation is to avoid external deletes during active sessions or to restart the session after filesystem changes.
Conclusion: Building Cache-Aware Agents That Scale Beyond One-Shot Tasks
Tool result caching transforms how developers structure long Claude Code sessions. The agents that benefit most are those operating on stable codebases with predictable file access patterns: refactoring existing modules, reviewing pull requests, generating documentation, and investigating bugs. These workflows read the same files dozens of times and execute the same shell commands repeatedly. Caching eliminates 40-60% of that redundancy without sacrificing correctness.
The agents that struggle are those in write-heavy or rapidly evolving environments where cache invalidation happens faster than cache hits accumulate. Teams working in these contexts should focus on conversation compaction and memory tools instead of relying on caching for performance gains.
The practical takeaway is to audit your session transcripts. Identify repeated tool calls with identical parameters. Calculate the token delta between first and subsequent calls. If the delta is 20x or greater, caching is working. If the delta is 1x, investigate why: are the parameters changing? Are writes invalidating the cache? Are the commands non-deterministic? Fix the root cause and the token savings will follow immediately.
That covers the essential patterns for tool result caching in Claude Code. Apply these in production and the difference will be immediate: faster sessions, lower token costs, and agents that scale to multi-hour workflows without burning your API budget.







Top comments (0)