thrift-memory

by yohadh

Not rated
GitHub

Description

Thrift Memory is a cost-first MCP memory server for coding agents that stop reloading large MEMORY.md, AGENTS.md, and project context files every session. It recalls only task-relevant memory under a hard token budget and returns a savings receipt: baselineTokens vs…

About

Thrift Memory is a cost-first MCP memory server for coding agents that stop reloading large MEMORY.md, AGENTS.md, and project context files every session. It recalls only task-relevant memory under a hard token budget and returns a savings receipt: baselineTokens vs injectedTokens vs savedTokens.

Details

Author
yohadh
Categories
Developer Tools, AI

Setup

Install thrift-memory in your MCP client (Claude Desktop, Cursor, Windsurf, and others).

Repository: https://github.com/yohadh/thrift-memory

Follow the installation instructions in the repository README, then restart your MCP client.

The MCP memory server that proves how many tokens you saved.(npm:thrift-memory)

Not affiliated withApache Thrift, the RPC framework. This project is always referred to asThrift Memory— an MCP memory layer for coding agents.

Thrift Memory is acost-first MCP memory server for coding agents that stop reloading largeMEMORY.md,AGENTS.md, and project context files every session.It recalls only task-relevant memory under ahard token budgetand returns a savings receipt:baselineTokens vs injectedTokens vs savedTokens.

savedTokens = baselineTokens - injectedTokens

If your coding agent re-loads the same large context file at every session start, that reload is pure, repeated token cost. Thrift Memory caps it and — uniquely — logs a receipt on every recall so you can see the token usage you avoided, not just trust that you avoided it.

Budgeted recall, in one line:Thrift Memory recalls only task-relevant memory under a hard token budget and logs a receipt showing baselineTokens vs injectedTokens vs savedTokens.

Status: early0.0.x. APIs are useful but still allowed to change beforev0.1.

- MCP manages memory recall and token receipts.
- thrift-proxymanages live request trimming and rate-limit retries.

The right comparison for Thrift Memory isnotrecall-quality / knowledge-graph layers likeMem0,Zep, orGraphiti— those optimize howsmartrecall is. Thrift Memory competes with the growing set ofMCP memory servers for coding agents, and it differs from all of them on one axis:cost visibility.

Every recall returns a savings receipt —baselineTokens,injectedTokens,savedTokens— so you can see how many tokens you avoided. No other server in this category positions itself aroundprovingthe saving.

The closest competitor,@provos/memory-mcp-server, also recalls under a token budget — but it does not surfacewhat the budget saved you. Thrift Memory's differentiator is not "I do memory"; it is "I do memory with acost accounting." ThesavedTokens = baselineTokens - injectedTokensreceipt is the thing no one else in this category leads with.

Honest summary: if you need the smartest possible recall, use a knowledge-graph layer like Mem0 or Zep. If your coding agents keep re-paying to reload largeMEMORY.md/AGENTS.md/ project context files at every session start and you want tomeasure and cap that costwith no extra infrastructure, that gap is what Thrift Memory fills. The two are not mutually exclusive — Thrift Memory can sit in front of a heavier store as the budget/metering layer.

For the full head-to-head — including how Thrift Memory differs fromMem0,Zep, andGraphition the cost-vs-recall-quality axis — seedocs/COMPARISON.md. Common questions are answered indocs/FAQ.md. For a narrative walkthrough of the whole memory field — recall-quality layers vs. the cost-first MCP memory servers — readthe Mem0 vs Zep vs Graphiti blog post.

remember(scope, text, agentId?, sessionId?, tags?) Store a memory in org, agent, or session scope. recall(agentId, tokenBudget, task?, tags?) Return relevant memories under a hard token budget. Also returns { injectedTokens, baselineTokens, savedTokens }. search_memory(agentId, task?, tags?, limit?) Browse matching memories without applying a small recall budget.

See Your Own Waste (10 seconds, nothing installed)

Before adopting anything, measure what your agents already reload every session:

It scans the current repo for agent memory / instruction files —CLAUDE.md,CLAUDE.local.md,MEMORY.md,AGENTS.md,GEMINI.md,.cursorrules,.cursor/rules/,.windsurfrules,.clinerules,.github/copilot-instructions.md, plus your user-global~/.claude/CLAUDE.md— and prints the bill:

Thrift Memory audit — D:\myrepo File Tokens CLAUDE.md 3,000 .cursor/rules/api.mdc 900 AGENTS.md 800 .github/copilot-instructions.md 300 TOTAL reloaded per session 5,000 At 10 sessions/day (--sessions): ~50,000 tokens/day, ~1,500,000/month ≈ $22.50/month at $15/M input tokens (an assumption — adjust: --price-per-mtok) With recall capped at 2,000 tokens/session (--budget): projected saving ~60%

Every number is computed from your files with the same estimator the meter uses — nothing is phoned home, nothing is installed. Flags:--path=,--sessions=,--budget=,--price-per-mtok=.

Option A — Claude Code plugin (one command, automatic memory)

If you use Claude Code, install the whole thing — MCP server, a memory-aware agent, and/thrift-recall//thrift-remembercommands — in one step:

/plugin marketplace add YohadH/thrift-memory /plugin install thrift-memory@thrift

That registers thethriftMCP server automatically (vianpx thrift-memory), sorecall/remember/search_memoryare available with no config editing. Seeplugins/thrift-memory/for what the plugin bundles.

Automatic memory (plugin v0.2.0):the plugin ships aSessionStarthook that runsthrift-memory session-contextand injects a budgeted memory slice (default 1,500 tokens) directly into context at every sessionstart,resume,/clear, andpost-compaction. Your durable memories survive context loss with zero tool calls — and each auto-injection is metered (agentsession-start), so the dashboard shows what the automatic path costs and saves too. An empty store injects nothing.

{ "mcpServers": { "thrift": { "command": "npx", "args": ["thrift-memory"] } } }
npx thrift-memory \ --store-path=~/.thrift/memories.jsonl \ --meter-path=~/.thrift/meter.jsonl \ --default-budget=2000

By default, the MCP server also scans the current working directory for existing agent context files:MEMORY.md,AGENTS.md,CLAUDE.md,GEMINI.md,.cursorrules,.windsurfrules,.clinerules,.cursor/rules/.md|.mdc,.windsurf/rules/.md|.mdc,.github/copilot-instructions.md, and agent-specific folders matchingmemory/<agentId>/.md.

Those files are treated asread-only recall sources.remember()still writes new durable memories to the JSONL store at--store-path, so the runtime model is:

MEMORY.md / AGENTS.md / rules files / memory/<agentId>/.md + ~/.thrift/memories.jsonl read-only sources + writable overlay

Files undermemory/<agentId>/.mdare loaded as agent-scoped memories, somemory/takshi/crm.mdis visible toagentId: "takshi", whilememory/qa-manager/smoke.mdis visible toagentId: "qa-manager". Shared folders such asmemory/reports,memory/feed, andmemory/adviceare not treated as agent IDs.

File edits are picked up on the next recall/search. To scan a different project root, pass--file-root=/path/to/repoor setTHRIFT_FILE_ROOT. To disable file-backed recall and use only JSONL memories, pass--file-memory=falseor setTHRIFT_FILE_MEMORY=0.

No agent required — prove theremember → recall → receiptloop with the library. Save asdemo.mjsafternpm install thrift-memory, thennode demo.mjs:

import { JsonlStore, ScopedRetriever } from "thrift-memory"; const store = new JsonlStore({ path: "./demo.jsonl" }); const now = Date.now(); // 1. remember — store a few org memories (cheap, no LLM enrichment) store.add({ scope: "org", text: "All money values are stored as integer cents, never floats." }, now); store.add({ scope: "org", text: "We deploy only on green CI; no Friday-evening releases." }, now); store.add({ scope: "org", text: "Postgres is the system of record; Redis is cache-only." }, now); // 2. recall — load only what the task needs, under a hard token budget const r = new ScopedRetriever().recall(store, { agentId: "dev", task: "how should I store money values?", tokenBudget: 40, }); // 3. receipt for (const m of r.memories) console.log("•", m.text); console.log(injected ${r.injectedTokens} / baseline ${r.baselineTokens} (saved ${r.savedTokens}));
• All money values are stored as integer cents, never floats. injected 15 / baseline 43 (saved 28)

Only the relevant memory is injected — the deploy-cadence and Postgres notes are dropped because they don't match the task, not merely because of the budget (recallapplies a relevance floor). That gap,baseline - injected, is exactly what you stop paying for on every run. Relevance here is lexical overlap, so phrase the task with words your memories actually use; an empty result means nothing in scope was relevant — which is the honest answer, not noise to pad the budget.

The optional dashboard is local. It shows whether Thrift is really saving tokens across real agent runs, and (as of 0.0.3) exposes a small write surface for owner controls — pin/disable a memory, set per-agent budgets, mute an agent, and a fleet-wide kill-switch — over localPOST/DELETEendpoints. The same controls are available from thethrift-panelCLI.

npx thrift-panel serve \ --store-path=~/.thrift/memories.jsonl \ --meter-path=~/.thrift/meter.jsonl \ --control-path=~/.thrift/control.json \ --port=8585
npx thrift-panel summary --store-path=~/.thrift/memories.jsonl --meter-path=~/.thrift/meter.jsonl npx thrift-panel agents --store-path=~/.thrift/memories.jsonl --meter-path=~/.thrift/meter.jsonl npx thrift-panel memories --store-path=~/.thrift/memories.jsonl --scope=org

Everyrecallwrites a receipt toTHRIFT_METER_PATHwhen a meter path is configured:

{"at":1760000000000,"agentId":"dev","injectedTokens":420,"baselineTokens":2100,"savedTokens":1680}

- Seed memories from your own markdown files or useremember.
- Let real agents callrecallduring normal work.
- Reviewthrift-panel summaryandthrift-panel agents.
- Validate quality separately by comparing task outcomes with full memory vs Thrift recall.

For a credible public report, publish both token reduction and quality evidence. For example: "saved 72% of memory tokens across 200 real recalls, with 19/20 paired tasks producing the same outcome."

Safe token saver — budget-pressure signals

Cutting tokens is only safe if the agent can tell "I got everything relevant" apart from "I got a fraction of it." So everyrecallresult also reports how muchrelevantmemory the budget forced it to leave behind:

{ "injectedTokens": 492, "baselineTokens": 14000, "savedTokens": 13508, "relevantTokens": 2100, "skippedForBudget": 12, "skippedTokensForBudget": 1608, "hasMoreRelevantMemory": true, "budgetPressure": "high" }

These countonlymemory that passed the relevance filter, sohasMoreRelevantMemorynever fires on noise the recall correctly dropped. The intended loop isprogressive recall, done by the agent (not the end user): start with a small budget, and ifbudgetPressureishigh, do one more focused recall before acting — never exceeding a total task budget. That is what turns Thrift from a tokensaverinto asafetoken saver: you never silently act on a starved slice. The bundled Claude Code plugin'smemory-keeperagent and/thrift-recallcommand already follow this loop.

Account for the MCP overhead.Registering any MCP server adds its tool-schema load to each agent's context (often several thousand tokens). The honest figure isnet:savings = recall reduction − MCP schema/tool-call overhead. On a context-heavy agent that reloads broad memory every run, recall usually wins by a wide margin — but confirm it with the meter on your own workload before going fleet-wide, rather than assuming. The receipts exist precisely so you don't have to guess.

This repo includes a small synthetic fixture so users can verify the measurement pipeline without any private data:

- benchmark/fixtures/memories.jsonl
- benchmark/fixtures/meter.jsonl

Seedocs/case-study.mdfor a sanitized example of how to interpret the numbers.

The plugin'sUserPromptSubmithook runsthrift-memory context-watchon every prompt. It tracks context usage against the model's window and, when usage crosses a step boundary, injects an instruction telling the agent to save durable facts viarememberand suggests running/compact— so decisions survive compaction instead of being silently dropped.

Step size is clamped between a floor and a ceiling so it neither fires too often on small windows nor too rarely on huge ones:

step = clamp(stepPct% × window, minStepTokens, maxStepPct% × window)

The save → compact → reload loop:context-watchprompts a save before a step boundary is crossed,PreCompactprints compaction guidance as a safety net, and the pre-existingSessionStarthook reloads a budgeted memory slice immediately after — closing the loop so no durable fact is lost to compaction.

Delta saves, not re-saves:each crossing now tags its guidance with a session-specific marker,session:<sessionId>, so the agent isn't just told to "save facts" blind every time. The injected instruction has the agent callsearch_memoryfor that tag first to see what it already stored this session, then save only genuinely new facts, tagging them the same way. That keeps later crossings in the same session from re-remembering the same fact over and over, and stops the agent from wrongly assuming something was already saved.

Opt outby removing theUserPromptSubmit(and optionallyPreCompact) entries fromplugins/thrift-memory/hooks/hooks.json.

Measured savings:node benchmark/context-watch.mjsshows ~72.5% fewer tokens reloaded across simulated windows (37,744 baseline vs. 10,367 injected, saving 27,377 tokens) — seeSynthetic Benchmarkabove for methodology.

Unit tests.npm test— 157 tests across 12 files, including a dedicatedtest/contextWatch.test.tsthat covers the clamp table (1M→200k, 200k→80k, 128k→64k, 32k→16k step sizes), the step-crossing state machine (first crossing fires, same step doesn't re-fire, the next step fires again, per-session isolation), transcript-tail parsing (realmessage.usage, a bounded tail-read for large transcripts, fallback tofileSize / 4), model → window inference, session-ID path-traversal rejection, and malformed/missing input. All green.

Manual hook-contract run.The built CLI (node dist/mcp/bin.js context-watch) was driven directly with hook-shaped stdin JSON against a synthetic transcript: it fires with the exacthookSpecificOutputJSON on a crossing, stays silent on a repeat of the same step, fires again on the next step, and stays silent (exit 0) on garbage stdin, empty stdin, and a missing transcript path — confirming the "never break the prompt" contract holds under every failure mode, not just the happy path.

Real end-to-end run, against this feature's own development session.Rather than only a synthetic fixture,context-watchwas replayed against the actual, live Claude Code transcript that was generatedwhile building this feature*— a genuinely long session (963 KB, 410 lines, realclaude-sonnet-5/claude-fable-5usage data, no window override). Four real snapshots were cut from that transcript at increasing points in the session's actual history and fed through the CLI in chronological order, each as a fresh hook invocation:

This matches the documented "200k window → saves at ~40% and ~80%" behavior exactly, using genuine per-turn token growth instead of hand-picked numbers. The full 963 KB transcript (well above the 64 KB bounded tail-read threshold) was also run standalone and returned in well under a second (~0.3–0.6s wall time, dominated by Node process startup, not transcript parsing) — confirming the bounded tail-read keeps the hook cheap even against a large, real, long-running session.

The proxy is optional. Use it when an agent can point its LLMbase_urlat a local HTTP gateway.

Security — run it locally only.The proxy forwards your real provider API key upstream unchanged. It binds to127.0.0.1by default (enforced in code, not just docs), so it is not reachable off-host unless you deliberately opt in with--host=0.0.0.0/THRIFT_PROXY_HOST. Never expose it on a public interface or share the port. It is a single-tenant developer tool, not a hardened multi-tenant gateway. Responses are also buffered, so SSE streaming is not passed through yet.

npx thrift-proxy \ --upstream=https://api.anthropic.com \ --host=127.0.0.1 \ --port=8787 \ --budget=4000 \ --meter-path=~/.thrift/meter.jsonl

Then configure the agent's LLM base URL ashttp://localhost:8787and keep using the real provider API key.

- trims live request context under a hard token budget,
- writes the same savings receipts as the MCP surface,
- retries upstream429and503 Retry-Afterresponses,
- throttles concurrent upstream requests per provider.

thrift-proxybuffers responses in this version; streaming passthrough is a future improvement.

The import script is generic and local-only. It can import markdown files into a JSONL store:

node scripts/import-memories.mjs \ --source=./memory \ --scope=org \ --store-path=~/.thrift/memories.jsonl \ --dry-run

For agent-scoped memories, put markdown files under project directories and use--scope=agent:

memory/ checkout-service/ dev.md qa.md docs-site/ writer.md
node scripts/import-memories.mjs --source=./memory --scope=agent
import { JsonlStore, ScopedRetriever, InMemoryMeter, ThriftMcpServer } from "thrift-memory"; const server = new ThriftMcpServer({ store: new JsonlStore({ path: "./memories.jsonl" }), retriever: new ScopedRetriever(), meter: new InMemoryMeter(), defaultTokenBudget: 2000, }); await server.runStdio();
npm install npm run typecheck npm run build npm test

This is a web browser that enables your coding agent, such as Claude Code, to visit websites on your behalf and assist you in identifying bugs or creating UI test cases.

Provides perfect memory for AI-assisted development by capturing project context snapshots, enabling natural language search, evolution tracking, and code intelligence.

Your AI development companion. An MCP server that gives your AI persistent memory, structured specifications, and adaptive change management — so it builds what you actually want.

A persistent development partner that prevents context drift and maintains project memory across all development sessions.

Cross-agent memory bridge with knowledge graph, workspace sync, and auto-memory hooks. Supports Windsurf, Cursor, Claude Code, Codex, and VS Code Copilot.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.