carryforward
This MCP server persists facts across sessions and retrieves only what's relevant to a new task.
Record appends one fact to a project ledger with metadata: kind (constraint, correction, decision, measurement, thread), how obtained (measured, decided, told, inferred), optional ref for traceability, and optional supersede to retire an entry without deletion.
Recall scores saved entries against a one-sentence task, returning constraints and corrections in full, high-scoring entries in full, middling ones as one line, and omitting the rest — or lists everything if no task is given.
Scoring uses Jev via the Vercel AI Gateway, but if unavailable it returns everything and explains why; rules and corrections are never scored.
Storage is append-only JSONL per project (customizable via CARRYFORWARD_DIR/PROJECT), with damaged lines skipped but counted.
Library and CLI expose functions (append, readAll, recall, formatBrief) and commands (
npx carryforwardfor server,recall,path).Safety: never deletes or rewrites; only recall with a task contacts the network; constraints/corrections never leave the machine.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@carryforwardrecall what I know about the email sign-in branch"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
carryforward
What your last session knew, scored against what this one is doing.
An MCP server with two tools. record saves a fact the moment it happens. recall brings those facts back when you start a task, and uses Jev to keep only the ones that matter right now.
Nothing is summarised. Nothing is deleted.
Contents
Related MCP server: engram
The problem
Your session ends, or it gets compacted halfway through. The next one starts from a summary, and summaries lose things:
The reason behind a decision is gone, so you argue about it again.
A number you measured is gone, so you measure it again or guess.
A rule you set is gone, so it gets broken.
A guess from last week is now repeated as a fact.
Every agent you spawn has the same problem, many times a day.
What it looks like
You save facts as you go:
record constraint "never force-push, a guard that stops you is telling you something"
record decision "the hook warns instead of blocking, since blocking forces --no-verify" ref: .git/hooks/pre-push
record measurement "14 decisions cost $0.00059, 318 ms each" ref: triage.mjs refresh: not reproducible
record thread "email sign-in branch is parked until the dashboard work restarts" ref: branch dharun-dev/email-signinNext session, you start a task:
recall "add the scope check as a CI job"And you get back only what that task needs:
# Carried forward
_task: add the scope check as a CI job_
## Always
- [constraint] never force-push, a guard that stops you is telling you something
## Live for this task
- [decision · p=0.91] the hook warns instead of blocking, since blocking forces --no-verify
ref: .git/hooks/pre-push
## Also on record
- 3f9a12c0 · measurement · 14 decisions cost $0.00059, 318 ms each (p=0.41)
_1 entry not relevant to this task, omitted._The rest is still saved. A different task brings back different things.
Install
Needs Node 22 or newer.
Claude Code
claude mcp add carryforward -e AI_GATEWAY_API_KEY=$AI_GATEWAY_API_KEY -- npx -y carryforwardCursor, Claude Desktop, or any MCP client
{
"mcpServers": {
"carryforward": {
"command": "npx",
"args": ["-y", "carryforward"],
"env": { "AI_GATEWAY_API_KEY": "...", "CARRYFORWARD_PROJECT": "my-project" }
}
}
}The key is what reaches Jev. Without it everything still works, and recall just gives you the whole list and tells you it could not score.
Each project gets its own file. Claude Code starts the server inside your project, so this happens on its own. Desktop apps start it somewhere else, so set CARRYFORWARD_PROJECT per project or they all share one file.
Start every session with your rules already loaded. Add this to .claude/settings.json:
{
"hooks": {
"SessionStart": [
{ "matcher": "startup|clear|compact", "hooks": [{ "type": "command", "command": "npx -y carryforward recall --quiet" }] }
]
}
}What you can save
kind | what it is | needs a |
| a rule or a boundary you set | no |
| you corrected something the agent did | no |
| a choice, and why you made it | yes |
| a number you will rely on later | yes |
| work that is parked, blocked, or with someone else | yes |
Constraints and corrections come back every time, in full. Jev never sees them, because a model should not get a vote on a rule you set.
The other three must point at something real: the command, the pull request, the commit, the file, the link. This keeps the list short, lets anyone check a claim instead of trusting it, and lets an entry fade away once the thing it points at is closed.
Each entry also saves where it came from: measured, decided, told, or inferred. So a guess never gets repeated later as a fact.
Write the what before the why. Entries are matched to a task by their words. One that only explains a reason will not be found by the task it belongs to. There is a real example of this below.
Why Jev
Jev is an evaluation model from TypeSafe. You give it some context and a typed question, and it answers with a probability. It writes no text at all.
That sounds like a limit until you notice this job never needs anything written. It only needs sorting. So the usual worry about a language model rewriting your notes or inventing a detail does not apply here, because Jev cannot write into your file even in principle. It reads and it ranks. Your words stay your words.
For each entry that is not a constraint or a correction, recall asks Jev one yes or no question:
Is this still live for the task starting now? Would not knowing it cause wrong or repeated work?
The probability that comes back decides what you see. Above 0.60 you get the full entry. Between 0.30 and 0.60 you get one line. Below that it is left out. Those numbers are exported constants, not hidden.
It is also cheap and quick. Jev costs $0.042 per million input tokens with no charge for output, so a list of a hundred entries is scored in four requests for well under a tenth of a cent, in about a second. carryforward reaches it through the Vercel AI Gateway, which is the route with published zero data retention terms.
Prefer a different scorer? Write an Asker with one ask(state, questions) method and pass it to recall(entries, task, asker).
A real run
Nine entries from a real project, three different tasks, 1.4 seconds each.
entry | "resume the email sign-in work" | "rerun the tests, which failures are real" | "add the scope check as a CI job" |
email sign-in branch is parked | 0.81 | 0.15 | 0.12 |
105 Windows test failures are environmental | 0.27 | 0.81 | 0.21 |
the hook warns instead of blocking | 0.20 | 0.15 | 0.41 |
PR #1903, written as "kept separate from #1860 because..." | 0.22 | 0.12 | 0.17 |
the same fact, rewritten as "proposes the scope check as a CI check" | 0.70 |
When an entry clearly fits the task it scores around 0.8, and nothing else comes close.
The last two rows are the same fact written twice. The first version only explains why the pull request exists and never says what it does, so Jev never connected it to the task. Rewritten to say what it is, it went from left out to shown in full. That is where the "what before why" advice comes from.
Nine entries and three tasks is a hint, not proof. There is no accuracy claim here until there is a proper test behind it.
What the eval suite found
evals/ holds a claude plugin eval suite, and the first thing it found was a hole in the design.
The payoff case gives the agent a real task, a branch rejected as behind main, and asks for the git commands. Nothing in the prompt mentions force-pushing. A rule recorded in an earlier session says never to force-push here, so the only route to the right answer is recalling it. The no-plugin arm is expected to fail. That gap is the measurement.
With the tools available and the skill installed, the agent called recall zero times out of four runs. Not blocked, not erroring, just never reached for. It answered a git question it already knew the answer to, and proposed a force push.
That is worth stating plainly: an MCP tool sitting there is not enough. An agent will not check a memory server before answering something it believes it knows, and a skill telling it to does not reliably change that. This is why the SessionStart hook exists and why it matters more than the tools do. The hook injects your rules unconditionally, at startup and again right after compaction, instead of depending on the model choosing to look.
Run it yourself. MCP tools are gated, so they need an operator grant on the command line, not just the case's allowed_tools:
claude plugin eval . --trust-plugin --ablation with-without \
--allow-tools mcp__carryforward__recall mcp__carryforward__recordMocks in evals/mocks/ stand in for the server, so runs are deterministic and never spend anything on scoring.
Things it will never do
It never deletes anything. Sorting happens when you read, not when you write. The file only grows.
It never scores your rules. Constraints and corrections always come back whole.
It never quietly gives you less. No key, no task, scorer down, rate limited: you get everything, plus a line saying why it could not sort.
It never sends anything when you save. Only
recallwith a task talks to the network, and your constraints and corrections are never part of that.
Where things are kept
One JSON line per entry, in ~/.carryforward/<project>.jsonl. Change the folder with CARRYFORWARD_DIR and the name with CARRYFORWARD_PROJECT.
Nothing is ever rewritten. To retire an entry you add a new one naming the old one, and the old line stays where it is. So a decision you reversed is still visible as reversed.
If a line ever gets damaged, by a crash or a full disk, it is skipped and counted, never removed, and recall tells you it happened.
Library and CLI
import { append, active, readAll, recall, formatBrief, gatewayAsker, ledgerPath } from "carryforward";
const path = ledgerPath();
append(path, { kind: "constraint", obtained: "told", text: "never force-push" });
const brief = await recall(active(readAll(path)), "add the scope check to CI", gatewayAsker());
console.log(formatBrief(brief));npx carryforward # run the MCP server
npx carryforward recall # print what is saved, one line each
npx carryforward recall "..." # print what matters for this task
npx carryforward path # show where this project's file livesHelp and contributing
Questions and bugs go in issues. Pull requests are welcome, and CONTRIBUTING.md explains the few rules that keep this small.
Two things are deliberately not built yet. Sorting as you write, a second small use of Jev that labels things as they happen so you do not have to decide what to save. And a proper accuracy test, comparing what was given to an agent against what it actually used. No accuracy number goes in this file before that exists.
npm install
npm run checkTests use a fake scorer and never touch the network.
License
MIT
Available Tools
2 toolsrecallRecall for a taskA
Bring forward what earlier sessions recorded, scored against the task you are about to do. Call it once, at the start of a task, with the task in one sentence. Constraints and corrections come back in full every time. Everything else is scored for whether not knowing it would cause wrong or repeated work: high scores come back in full, middling ones as one line, the rest are omitted. Without a task it lists everything one line each. If scoring is unavailable it returns everything and says so — it never returns less than the ledger holds without telling you.
| Name | Required | Description | Default |
|---|---|---|---|
| task | No | the task about to start, in one sentence |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and does so thoroughly. It discloses what comes back in full, what is truncated to one line, what is omitted, what happens without a task, and how it behaves if scoring is unavailable. This is unusually explicit about output and failure behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: it explains purpose, invocation, ranking behavior, the no-task mode, and the fallback guarantee. It is front-loaded with the core function and does not waste words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must explain return behavior, and it does so completely: full returns, one-line returns, omissions, no-task listing, and degradation when scoring is unavailable. For a one-parameter recall tool, nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema already documents the 'task' parameter, the description adds crucial semantics: the task should be one sentence, it is optional, and omitting it changes the result entirely to a one-line-per-item listing. This goes well beyond the schema's description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('bring forward what earlier sessions recorded') and the resource it operates on, and it is clearly the read-side counterpart to the sibling tool 'record'. It goes beyond the name and title by explaining the scoring behavior, so an agent cannot mistake it for a generic recall tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage instructions: 'Call it once, at the start of a task, with the task in one sentence' and also explains the no-task variant. It does not explicitly say when not to use it or contrast it with the sibling 'record' tool, so it falls just short of full alternative-route guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
recordRecord to the ledgerA
Append one fact to this project's ledger, at the moment it is produced. Call it right after: the user states a rule or a boundary (constraint); the user corrects something you did (correction); you choose between alternatives (decision — say why in text, and ref where it is recorded); a tool result yields a figure you will rely on (measurement — ref the command, refresh says whether it reproduces); work is parked, blocked, or handed to someone (thread — ref the branch, PR, or issue). Decisions, measurements and threads must carry a ref. Write text so it says WHAT the thing is before WHY: recall scores entries against a future task by their text, and an entry that only explains a reason will not be found by the task it belongs to. To retire an earlier entry pass its id in supersedes; the old entry stays on disk, marked replaced. Nothing here is ever deleted.
| Name | Required | Description | Default |
|---|---|---|---|
| ref | No | what it points at: a command, PR, issue, commit, file path or URL. Required for decision, measurement, thread | |
| kind | Yes | constraint | correction | decision | measurement | thread | |
| text | Yes | the claim, one or two sentences; say what it is, then why | |
| refresh | No | how to re-derive it, or the words 'not reproducible' | |
| obtained | Yes | how the claim was obtained: measured | decided | told | inferred | |
| supersedes | No | id of an earlier entry this one replaces |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the full burden. It does: it discloses persistence ('Nothing here is ever deleted'), the replacement behavior ('the old entry stays on disk, marked replaced'), and the rationale for text formatting to support future recall. These are non-obvious behaviors an agent needs to know.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than typical but every sentence serves a purpose: it front-loads the core action, then provides the when-list, then per-field semantics and lifecycle rules. It is structured as a compact reference, not padded prose. Slight verbosity is justified by the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter tool with two enums and no output schema, the description covers all necessary aspects: when to invoke, what each field means, required ref conditions, text formatting rules, supersede semantics, and the permanence guarantee. It also implicitly contrasts with recall. Nothing an agent needs to call correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so every parameter already has a description. The description adds value beyond that by explaining the semantic role of each field in context: e.g., 'ref' is required for specific kinds, 'text' should lead with WHAT for recallability, 'refresh' indicates reproducibility, and 'supersedes' links to an earlier entry. This enriches the schema without redundancy.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb-resource pair ('Append one fact to this project's ledger') and specifies the exact timing ('at the moment it is produced'). It distinguishes itself from the sibling tool recall by framing this as the write operation ('recall scores entries against a future task'), so an agent can tell them apart without inspecting schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description enumerates specific trigger conditions ('Call it right after: the user states a rule or a boundary... the user corrects something... you choose between alternatives...') and gives explicit formatting guidance ('say what it is, then why') plus rules for required refs per kind. It also explains when to use supersedes to retire an entry. This is explicit and actionable.
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.
2 tool updates
v0.1.1- First observed
recall - First observed
record
TDQS
Scored across 2 tools
Record and recall have completely distinct purposes: one appends to the ledger, the other queries it. There is no overlap or ambiguity between them.
Both tools use the same pattern of a single imperative verb describing the operation. Record and recall are symmetric, predictable, and easy to distinguish.
Two tools perfectly cover the core write/read lifecycle of the ledger. Additional tools would add bloat without expanding the server's stated purpose.
The surface supports recording facts, superseding old entries, task-scored recall, and full listing without a task. Deletion is intentionally excluded by design, so no necessary operation is missing.
Maintenance
Related MCP Connectors
Durable, shareable and governed project memory with smart triage and explicit project composition.
Per-project memory for AI agents: decisions, attempts, tasks, ranked recall. Paid per call via x402.
Persistent memory layer that saves and recalls your project context and preferences.
Project memory for coding agents: requirements, decisions, code graph and delivery telemetry.
Related MCP Servers
- AlicenseBqualityBmaintenanceEnables coding agents to incrementally index project text and code, persist decisions and constraints with clear sources, and assemble focused project context across sessions via MCP.34316 npm5MIT
- AlicenseNot gradedqualityAmaintenanceProvides persistent project memory for AI coding agents, enabling context retention across sessions via event logging, briefing generation, and querying.MIT
- AlicenseNot gradedqualityAmaintenanceEnables MCP agents to maintain durable, evidence-aware project knowledge, retrieve precise excerpts on demand, and track decisions, conflicts, and revisions across sessions.1Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to persist and retrieve cause-effect project memories across sessions using a local ledger and hosted ranking tools, with corrections always prioritized and unreliable answers suppressed.695 npmMIT