Skip to main content
Glama

echocache

An MCP server for a cached LLM response — the way an HTTP cache caches an expensive server response, not the way a browser caches a static asset. Ask a question you already answered once, and the answer echoes back instead of being re-derived.

Two lookup paths, inspired by two different kinds of caching:

  • Exact-match, HTTP-stylecache_get / cache_set key on (model, prompt, params), with ttl_seconds and stale_while_revalidate_seconds behaving like Cache-Control: fresh, stale, or expired.

  • Knowledge-graph recall — every stored entry is a node in a small similarity graph. cache_query finds related entries by meaning, not just exact key; cache_related walks graph edges (auto-linked "similar" entries, or explicit "derived-from" parents) to surface everything already known before an agent redoes work from scratch. cache_invalidate can cascade through those derived-from edges when a source changes.

Runs as a standard stdio MCP server, so it works with Claude Code, Claude Desktop, Cursor, VS Code + Copilot, or any other MCP-capable host — see Install below, and AGENTS.md for the tool-use protocol any connected agent should follow.

What it's for — and what it isn't

Cache what an agent concluded, never what it read. This is the whole design, and it is worth stating plainly because the intuitive use is the wrong one: serving a cached file read costs the reader exactly the tokens that reading the file cost, since the content still has to enter the context. So caching file reads saves nothing at any hit rate, and if the agent re-emits the file to store it, that's output-rate tokens paid for zero benefit. A cache only pays when a hit stands in for regenerating something.

Where it pays, and where measurement said it does not:

  • Pays best: an expensive research or judgment call. A real 22-tool-call research chain in this project's own history cost 37,119 output tokens to reach a 255-token conclusion — a 728x gap against serving that conclusion back, weighted for output pricing. A single reuse pays for the write ~145x over. Live-validated the same way with a fresh web-search-derived design decision, correctly recalled by different wording and correctly outranking an unrelated entry sharing surface vocabulary. Not proven to recur yet in this project's own history — but the payoff on one hit is large enough that low frequency isn't disqualifying, unlike a file read.

  • Pays conditionally: re-orientation across sessions. An agent reads a codebase to understand it, the session ends, and a later session needs that understanding again. On express/lib — six files, 62KB — re-reading the source costs 15,504 tokens against 549 to serve the cached orientation, about 28× fewer. That holds when the later session genuinely needs broad understanding; if it only needs one specific answer, it will grep and read a slice for ~900 tokens, and the cache isn't competitive.

  • Does not pay: replacing reads in a parallel dispatch. Thirty subagents in one code-review dispatch pulled ~374,000 tokens of content a sibling had already read — but 127 of their 166 reads used offset/limit, so they were already taking slices rather than whole files. Substituting a shared derivation for those slices measured 27% worse than what they actually did: grep is already a cheap, precise pointer, and a cached map competes with it on its own ground and loses.

The rule all three point at: cache what grep cannot reconstruct. A conclusion, a judgement, the reason something is the way it is, a cross-file synthesis no single search reveals, a research finding, the fact that something is absent. Never a location — grep finds those for less than the cache costs to consult — and never a file.

When a cached entry does carry file paths or line numbers, that is to point a reader at exact detail, not to replace reading it. And reach for cache_query rather than cache_get when looking for a match: a later session, or another agent, will not phrase the question the way the writer did.

What this is not: a way to avoid reading files, a source of truth, or a substitute for prompt caching within one conversation, which is cheaper and needs no server. echocache is for results that must outlive the context that produced them.

What Claude Code already does for free

If your only host is Claude Code, its own persistent memory already does the core of this: write a conclusion to a memory file instead of the files it came from, and a later session reads it back before redoing the work. That's the same rule this project converged on, running for free, with no server to register. This project's own findings and measurements from building it are stored there, not in echocache itself — worth noticing, since it means the tool wasn't used to cache the very research that produced it.

What's actually different, in order of how much it matters:

  • Cross-project sharing. Claude Code's memory is scoped to one project directory. echocache is one SQLite file any project on the machine can register against, so a conclusion reached in one repo is queryable from another. Real, but unproven: this project's own history shows zero instances of a conclusion actually getting reused across sessions, and cross-project reuse is a narrower bar than that.

  • Semantic recall. cache_query finds a match by meaning, independent of how it was phrased or which file it's filed under. Memory is retrieved by an always-loaded index plus the agent's own judgment about what to open — no vector search.

  • Host-agnostic. Works from Cursor, VS Code, Claude Desktop, or any other MCP client — memory is native to Claude Code specifically.

  • Explicit freshness. TTL/stale-while-revalidate freshness and hash-based derived_from invalidation catch a source going stale automatically. Memory has neither; staleness is caught only if an agent happens to notice.

For a single user on a single host in one project, memory already captures most of the value here for free. What's left as echocache's actual case is narrower than "a cache for LLM responses": it's specifically sharing a derivation across projects or hosts that don't already share a memory store — and that narrower case is unproven, not just untested, until it's been measured the way everything else in this document has.

Related MCP server: mcp-llm

Install

No clone or build needed — register it straight from npm.

Claude Code

claude mcp add echocache -- npx -y echocache

Claude Desktop / Cursor / VS Code — add a stdio entry to the host's MCP config (claude_desktop_config.json, .cursor/mcp.json, .vscode/mcp.json):

{
  "mcpServers": {
    "echocache": {
      "command": "npx",
      "args": ["-y", "echocache"]
    }
  }
}

Any other MCP-capable host takes the same launch command; only the config file differs. Then point your agent at AGENTS.md so it knows when to reach for the cache — the protocol matters more than the wiring, since caching the wrong things costs tokens rather than saving them.

Configuration

Every setting is an environment variable, all optional:

Variable

Default

Meaning

ECHOCACHE_DB_PATH

~/.echocache/cache.db

SQLite file location

ECHOCACHE_MAX_ENTRIES

10000

LRU ceiling on retained entries

ECHOCACHE_MAX_BYTES

268435456 (256MB)

LRU ceiling on retained response bytes

ECHOCACHE_DEFAULT_TTL_SECONDS

86400 (1 day)

Freshness lifetime when a caller omits one

ECHOCACHE_SIMILARITY_THRESHOLD

0.25

Similarity floor for auto-linking entries

ECHOCACHE_LINK_CANDIDATE_POOL

500

Recent entries a new write is compared against

ECHOCACHE_ENCRYPTION_KEY

unset

64 hex chars (32 bytes); enables AES-256-GCM at rest

The database directory is created 0700 and its files 0600. Set an encryption key to also encrypt entry contents at rest:

export ECHOCACHE_ENCRYPTION_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")

Turning encryption on or off requires a fresh database — there is no in-place migration, and a key/database mismatch is refused at startup rather than failing on some later read.

Tools

Tool

Purpose

cache_get

Exact-match lookup with fresh / stale / expired freshness

cache_set

Store a result; auto-links it into the similarity graph

cache_query

Semantic search across all cached entries

cache_related

Graph traversal from one entry to entries linked to it

cache_invalidate

Delete an entry, optionally cascading to its dependents

cache_stats

Exact-match hit rate, queryHits/queryMisses, and tokens served

One SQLite file backs all of them, shared across every project that registers the server — a conclusion reached in one repo is queryable from another. Concurrent readers and writers from separate processes are the expected case, not an edge case.

Developing

git clone https://github.com/kskurtveit/echocache && cd echocache
npm install
npm run check        # typecheck + tests
npm start            # or: npm run dev

See CLAUDE.md for architecture and the module reference.

License

MIT

Available Tools

6 tools
cache_getA

Look up a cached LLM response by exact (model, prompt, params) match, like an HTTP cache checking a request against its cache key. Call this BEFORE issuing an expensive prompt to a model. Returns hit:false on a miss or an expired entry — in that case, run the prompt yourself and store the result with cache_set. On a hit, freshness mirrors HTTP semantics: fresh means use it as-is; stale means it is past its TTL but within its stale-while-revalidate window, so you may still use it but consider refreshing it.

ParametersJSON Schema
NameRequiredDescriptionDefault
modelYesModel/tool identifier the response came from, e.g. "claude-sonnet-5"
paramsNoOther call parameters that affect the response (temperature, system prompt, etc.)
promptYesThe exact prompt or request text

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations and no output schema, the description carries the behavioral burden and does it well: it discloses exact-match semantics, the hit:false miss/expired case, and the fresh vs stale distinction. It could be slightly more explicit about the shape of a hit:true response, but the freshness semantics go beyond what the schema reveals.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences, all of which contribute: exact-match semantics, when to call, what to do on miss, and freshness handling. It is slightly dense but not wasteful, and the most important usage directive is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a cache lookup with no annotations and no output schema, the description gives the essential runtime semantics: miss behavior, expired-entry behavior, and fresh/stale handling. It could explicitly state the hit:true payload yields the cached response and mention alternatives like cache_query for non-exact lookups, but overall it is complete enough to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already covers all three parameters with 100% coverage, so the baseline is 3. The description adds value by explaining that model, prompt, and params together form the cache key for an exact match, which connects the parameters to the tool's behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Look up a cached LLM response by exact (model, prompt, params) match.' The exact-match qualifier and the HTTP cache analogy clearly define what the tool does and how it differs from sibling tools like cache_set or cache_query.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly tells the agent to call this 'BEFORE issuing an expensive prompt' and instructs what to do on a miss: run the prompt and store with cache_set. It lacks explicit comparisons to cache_query or cache_related, so it does not fully differentiate all sibling alternatives, but the core usage context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

cache_invalidateA

Delete a cache entry, e.g. because the underlying source it was based on changed. With cascade:true, also deletes every entry that declared this one as a derived_from parent, and recursively theirs — dependency-graph invalidation instead of a manual hunt for stale copies.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYes
cascadeNoAlso delete entries derived from this one, default false

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided at all, the description carries the full burden of behavioral disclosure. It does well: it reveals the destructive nature ('Delete'), the cascade semantics ('every entry that declared this one as a derived_from parent'), and recursion behavior. It does not mention edge cases like missing-id handling or irreversibility, but the core behaviors are transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler; the core action is front-loaded and the cascade nuance follows. The second sentence is semantically dense with nested clauses but earns its length by explaining recursive behavior and the rationale. Slightly compressed structure, but every part adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive 2-parameter tool with no annotations and no output schema, the description covers the main essentials: purpose, trigger scenario, and the cascade option's full behavior. Missing details are limited to edge-case behavior (nonexistent id, error handling, reversibility), which are conventional for cache invalidation and not severe gaps for an agent to proceed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 50%: the cascade parameter has a schema description but id does not. The description enriches the cascade semantics well (derived_from parent, recursive invalidation), adding meaning beyond the schema's 'Also delete entries derived from this one'. However, id receives no additional semantic context, and the description doesn't fully compensate for the missing id doc, though the parameter is fairly self-explanatory.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Delete a cache entry') and the specific resource, with a concrete motivating example ('underlying source it was based on changed'). It is clearly distinct from sibling cache_get/cache_set operations by virtue of being a deletion, though it does not explicitly name and differentiate from sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a clear when-to-use scenario: invalidate when the underlying source changed. It also explains the key decision between cascading and non-cascading deletion ('dependency-graph invalidation instead of a manual hunt for stale copies'). It lacks explicit exclusions or named alternatives, but the context is sufficiently clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

cache_queryA

Semantic search across all cached entries, independent of exact key matching. Use this when you suspect something related was already computed even though the prompt wording differs — the knowledge-graph equivalent of a cache lookup by meaning instead of by exact key.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesText to search for semantically related cache entries
top_kNoMax results, default 5
min_similarityNoSimilarity floor 0-1, default 0.3

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description carries the full burden of behavioral disclosure. It adds useful context: the search spans all cached entries and matches by meaning rather than exact key, implying a read-only lookup. However, it does not state whether it mutates anything, what it returns, or any performance implications of scanning all entries.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core function and then the trigger condition. The 'knowledge-graph equivalent' metaphor adds color but somewhat restates the first sentence, making it slightly less tight than it could be. Overall, it is still concise and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers what the tool does and when to use it, and the schema covers parameters. However, since there is no output schema, the description omits any indication of return shape or size. It also doesn't mention potential cost or side effects of searching 'all cached entries,' so the profile is adequate but has clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with each parameter already documented meaningfully (text, top_k, min_similarity). The tool description adds no parameter-specific information beyond what the schema provides, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Semantic search across all cached entries.' It also draws a clear contrast with 'exact key matching,' which distinguishes it from sibling tools like cache_get. An agent can tell what this tool does without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says when to use the tool: 'when you suspect something related was already computed even though the prompt wording differs.' It implies exact-key lookups are not its purpose by stressing 'independent of exact key matching,' though it doesn't name a specific alternative tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

cache_setA

Store a prompt/response pair in the cache. Call this AFTER getting a fresh response from a model that cache_get did not have. New entries are automatically linked in a similarity graph to existing entries with related content, so cache_related and cache_query can surface them later even without an exact key match. Pass derived_from with parent entry ids if this result was built from other cached entries — invalidating a parent with cascade can then clean up anything derived from it.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagsNo
modelYes
paramsNo
promptYes
responseYesThe response text to cache
ttl_secondsNoFreshness lifetime in seconds. null = never expires. Default: 1 day
derived_fromNoIds of parent entries this was built from
stale_while_revalidate_secondsNoExtra window after TTL expiry where the entry is still returned, marked stale

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden, and it discloses non-obvious behavior: automatic similarity-graph linking, its effect on cache_related and cache_query, and cascade invalidation via derived_from. It stops short of stating duplicate-key behavior, return values, or key composition, but the main side effects are covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences, each earning its place: purpose, usage timing, automatic linking behavior, and derived_from semantics. The most important action is front-loaded and no filler is present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the critical workflow (post-miss write, graph linkage, cascade invalidation) well enough for an agent to invoke correctly with the required fields. Missing return-value expectations and duplicate-handling behavior are minor gaps given the absence of an output schema, but the core context is solid.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 50%, and the description adds real meaning for derived_from (parent/cascade relationships). However, tags and the free-form params object receive no guidance, and the description does not compensate for those undocumented parameters. Optional fields like ttl_seconds rely entirely on the schema's default notes.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence 'Store a prompt/response pair in the cache' states a specific verb and resource, and the references to cache_get, cache_related, and cache_query clearly situate it among the sibling tools. An agent can immediately distinguish this write operation from the read/query/invalidate siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly instructs when to call: 'AFTER getting a fresh response from a model that cache_get did not have,' which names the triggering condition and the primary alternative (cache_get). The derived_from guidance adds a conditional usage rule for derived entries, leaving little ambiguity about when this tool is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

cache_statsA

Cache analytics: entry/edge counts, exact-match hit rate, queryHits/queryMisses for semantic recall, and tokensServed — the token count handed back from cache. Note that tokensServed equals tokens saved only for entries that stand in for work which would otherwise be regenerated; serving a cached file read costs the same tokens as re-reading the file, so it saves nothing.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It adds a genuinely useful behavioral nuance: tokensServed equals tokens saved only for entries avoiding regeneration, not for cached file reads. It does not explicitly state read-only behavior, but 'analytics' and the absence of mutation verbs imply no side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences with the label front-loaded in 'Cache analytics'. The first sentence enumerates the return metrics; the second adds a necessary caveat about tokensServed. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter analytics tool with no annotations or output schema, the description covers the main return values and even clarifies a subtle metric. It does not specify the exact response container or explicitly state that the call is read-only, but those are minor gaps for this simple tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so parameter documentation is moot. The rubric's baseline for 0 params is 4, and the description needs no clarifification of parameter semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the tool as 'Cache analytics' and lists concrete metrics (entry/edge counts, exact-match hit rate, queryHits/queryMisses, tokensServed), making its role clear. It is distinguishable from the sibling get/set/query/related/invalidate tools by being an analytics/stats tool, though it lacks an explicit verb like 'retrieves'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not mention siblings or exclusions, but the phrase 'Cache analytics' plus the metric list implies this is the tool to use when cache statistics are needed. There is no explicit when-not-to-use guidance, so it stops short of a 4.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 6 tool updatesv0.1.1
    • First observedcache_get
    • First observedcache_invalidate
    • First observedcache_query
    • First observedcache_related
    • First observedcache_set
    • First observedcache_stats

TDQS

A4.1/5.0
Disambiguation5/5

Each tool targets a clearly distinct operation: exact-match lookup, storing new entries, semantic search, graph traversal, invalidation, and analytics. Even though cache_query and cache_related both deal with connected content, the descriptions make their different access patterns unambiguous.

Naming Consistency4/5

All tools share a consistent cache_ prefix and mostly use a verb as the second element: get, set, query, invalidate, stats. cache_related is the one outlier since 'related' is not a verb, but the overall pattern is still highly predictable.

Tool Count5/5

Six tools is well-scoped for a cache server: read, write, semantic access, graph traversal, invalidation, and statistics. Each tool earns its place without redundancy or bloat.

Completeness4/5

The cache lifecycle is well covered: exact lookup, storage with derived-from tracking, semantic retrieval, related-entry traversal, invalidation with cascade, and stats. Minor gaps like bulk clearing or listing all entries could be useful but are not essential for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kskurtveit/echocache'

If you have feedback or need assistance with the MCP directory API, please join our Discord server