# You pay for the same context three times a day

> Source: <https://dev.to/sanjayrani_kamminana_0b74/you-pay-for-the-same-context-three-times-a-day-3dic>
> Published: 2026-09-11 09:49:52+00:00

I opened Claude to reason about a schema change. Then Cursor, to make it. Then Claude again the next morning, because the session was gone.

Each of those was a cold start. Same repo, same decision history, same five files re-read from scratch, same explanation of why the `Order` → `Payment` edge is nullable typed out for the third time. Nothing in that sequence was new information. All of it was billed.

That's the part of agent memory that bothers me more than forgetting. Forgetting is annoying. Re-reading is *expensive*, and it's expensive in a way that scales with how useful the agent is — the more context it needs, the more you pay to rebuild it, every session, in every tool.

MCP is the obvious place to fix this, and mostly it isn't used that way. Most MCP servers are verbs: send the message, open the PR, run the query. Very few are a place where context *lives*.

Do the arithmetic on a normal session. Twelve files at 400 lines each is somewhere north of 60k tokens before the agent has said anything useful. A retrieval step over a vector store trims that, but not as much as the demos suggest: top-k pulls back chunks that scored well on similarity, which is a different question from whether the agent needed them. You get ten chunks, three of which matter, and you pay for ten.

Then the session ends and you do it again.

The framing I've landed on: there are two separate costs, and they get conflated.

Vector retrieval attacks volume. It does nothing about repetition, because the embedding index answers "what looks like this" and the agent still has to reconstruct the *state* — what was decided, what depends on what, what changed last Tuesday — from whatever it can read this session.

Repetition is the one that compounds across tools.

A graph behind an MCP server changes the ownership of the context. The memory isn't in the client; it's in the database, and the client is a reader.

That sounds like a small distinction and it isn't, because it means the same context is addressable from anywhere that speaks MCP. Claude writes a decision node during design. Cursor reads it two hours later while implementing, without being told. Nothing was exported, re-pasted, or summarised into a handoff doc that goes stale in a week.

The CognoDB MCP server is deliberately thin — two tools:

That's it. No pre-baked `get_user_context` endpoint, no fixed set of retrieval verbs someone had to anticipate. The agent inspects the graph's actual shape, then writes a query for the question in front of it. When you add a node type next month, no tool definitions change and no client needs updating. The schema *is* the tool surface.

Configuration is the standard block:

```
{
  "mcpServers": {
    "cognodb": {
      "command": "npx",
      "args": ["-y", "@cognodb/mcp"],
      "env": {
        "COGNODB_URI": "bolt+s://db-7f3a2c1e.databases.cognodb.cloud",
        "COGNODB_PASSWORD": "${DB_PASSWORD}"
      }
    }
  }
}
```

Drop that into Claude Desktop, Cursor, Windsurf, Cline, Zed, JetBrains, Warp or Gemini CLI and they're all reading the same graph. Run it read-only while you're getting a feel for it — the agent can explore everything and write nothing.

One honest caveat, because I've seen people hit it: **ChatGPT is not a stdio client.** Custom connectors there are remote HTTPS only, on Plus and above with Developer Mode enabled, and Business/Enterprise workspaces need an admin to approve the connector first. The graph is the same graph; the transport is the part you have to solve separately. Anyone telling you one `npx` line lights up every assistant on the market is selling something.

The other half of "no noise" is what comes back.

Similarity search returns a ranked list and you choose a cutoff. Graph traversal returns a *neighbourhood*, and the boundary is structural rather than statistical — you asked for two hops from this entity, you get two hops from this entity:

``` php
MATCH (d:Decision {id: $decision_id})
OPTIONAL MATCH (d)-[:AFFECTS]->(c:Component)
OPTIONAL MATCH (d)<-[:SUPERSEDES]-(newer:Decision)
OPTIONAL MATCH (d)-[:MADE_IN]->(s:Session)
RETURN d.summary AS decision,
       d.rationale AS why,
       collect(DISTINCT c.name) AS affects,
       collect(DISTINCT newer.summary) AS superseded_by,
       s.date AS decided_on
```

That returns a handful of rows. Not the twelve files the decision was made about — the decision, what it touches, and whether something later overrode it. If the agent needs the file, it can go read the file; it no longer has to read twelve of them hoping one explains the situation.

The `SUPERSEDES` edge is the part I'd underline. It's the thing a pile of retrieved text is worst at: an agent reading two conflicting notes has no way to know which one won. An edge says so directly, and the path it came back on is the explanation you show a human when they ask why the agent did that.

Our own measurement on this is about a **98.7% reduction in tokens at 3,700 entities**, comparing a bounded traversal against loading the equivalent corpus. Treat that as directionally true rather than a promise — the ratio moves with how dense your graph is and how deep you traverse, and a five-hop query on a hairball graph will happily hand you back the whole dataset. Bounded means bounded because *you* bounded it.

Latency matters here more than it looks like it should: two-hop traversals land around 0.27 ms, p95 0.51 ms. When retrieval is that cheap the agent can afford several small, specific queries per turn instead of one enormous speculative fetch — which is the actual mechanism by which the noise goes away.

It isn't graph versus vectors, and I'd rather say that plainly than pretend otherwise.

If your question is "find me things that read like this" — semantic search over documents, fuzzy dedup, recommendations from unstructured text — embeddings are the right tool and a graph is a worse one. Graphs earn their place when the relationships are themselves the data you're querying: dependencies, provenance, ownership, sequence, supersession. Most real agent memory is a mix, and the useful version is a graph holding the structure with vector or BM25 lookup for the entry point.

The other honest limit: someone has to decide what a node is. A vector store will accept whatever you throw at it. A graph makes you commit to a model up front, and a bad model is worse than no model. Start with three node types you're sure about and let it grow.

Full disclosure: I work on CognoDB. None of the reasoning above is specific to it — you can build exactly this on any Bolt-speaking graph with an MCP server in front.

What made me stop prototyping this on a local cluster was the setup tax. The free `c0` instance is enough to hold a real project's decision graph, it's Bolt 5.x so existing Neo4j drivers work unchanged, and the MCP server is the same `npx` line above. Point two clients at it and watch the second one already know things.

The unsolved part, for me, is **pruning**. A decision graph grows monotonically and a six-month-old superseded decision is noise, but deleting it destroys the provenance chain that made the graph worth having. Time-scoped edges? A `status` property and every query filtering on it? Both feel wrong in different ways.

If you've run shared agent memory across more than one tool for a while — how are you keeping it from silting up?

More in this series under #cognodb.
