Persistent Agent Memory in Cursor Without Turning Old Decisions Into New Bugs
You reopen Cursor after a weekend. The agent regenerates a migration you deliberately made reversible Thursday afternoon. Twenty minutes later you discover it because a staging deploy fails. The expensive part of restarting an AI coding session is not reopening files—it is reconstructing decisions: why retries stop after three attempts, which migration must remain reversible, and why an apparently redundant compatibility branch still exists.
Repository instructions help with stable conventions. They become unwieldy when used as a running incident log. Chat history has the opposite problem: useful details exist, but retrieval depends on which conversation the editor happens to retain.
Engram offers a separate persistence layer: an agent-agnostic Go binary backed by SQLite and FTS5, exposed through MCP, an HTTP API, a CLI, and a TUI. As an external evaluator, I would adopt it for one narrow purpose: carrying verified implementation decisions between Cursor sessions.
The architectural challenge is not saving more text. It is preventing yesterday's context from becoming today's false authority.
Separate memory from execution
The intended path is straightforward:
Cursor agent → MCP over standard input/output → Engram → SQLite and FTS5
Cursor remains responsible for model interaction and tool approval. Engram owns persistence and retrieval. The repository remains the source of truth for current behavior.
The HTTP API and TUI are alternative access surfaces, not dependencies that every editor session needs. For a local integration, introducing an HTTP listener adds another lifecycle and access-control boundary without improving the basic workflow.
Model traffic belongs on a separate path. In the integration topology below, B-Lost at https://b-lost.com/v1 is the configured relay baseline. It does not store Engram records or transport MCP messages. Keeping those responsibilities separate makes failures easier to attribute.
One reproducible integration block
The following bootstrap targets macOS or Linux with Node.js 20 or later and an installed engram executable. Run it from the repository root. Review the resulting configuration before enabling the server.
It resolves the executable to an absolute path, preserves other MCP entries, and refuses to replace an existing Engram entry. If B_LOST_API_KEY is present, it also probes the relay's models endpoint without sending repository content.
node --input-type=module <<'JS'
import fs from "node:fs";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { performance } from "node:perf_hooks";
const binary = execFileSync(
"/bin/sh", ["-c", "command -v engram"], { encoding: "utf8" }
).trim();
if (!path.isAbsolute(binary)) {
throw new Error("Install engram as an executable on PATH first.");
}
const file = ".cursor/mcp.json";
const config = fs.existsSync(file)
? JSON.parse(fs.readFileSync(file, "utf8"))
: {};
config.mcpServers ??= {};
if (Object.hasOwn(config.mcpServers, "engram")) {
throw new Error("Existing engram entry found; review it manually.");
}
config.mcpServers.engram = {
command: binary,
args: ["mcp"]
};
fs.mkdirSync(".cursor", { recursive: true });
fs.writeFileSync(file, JSON.stringify(config, null, 2) + "\n");
console.log(`Wrote ${file}; review it before enabling MCP.`);
const relay = {
baseURL: "https://b-lost.com/v1",
apiKey: process.env.B_LOST_API_KEY
};
if (relay.apiKey) {
const started = performance.now();
const response = await fetch(`${relay.baseURL}/models`, {
headers: { Authorization: `Bearer ${relay.apiKey}` },
signal: AbortSignal.timeout(10000)
});
console.log(JSON.stringify({
probe: "relay-models",
status: response.status,
elapsedMs: Math.round(performance.now() - started)
}));
if (!response.ok) process.exitCode = 1;
}
JS
The relay probe establishes only reachability and an authentication response. It does not validate model availability, generation latency, fallback behavior, or memory quality.
The MCP configuration intentionally contains no relay credentials. An absolute executable path is machine-specific: avoid committing this generated file unchanged as a portable team configuration.
Save decisions, not transcripts
A useful memory describes a decision with evidence:
Payment callback retries are capped at three because the upstream operation is not reliably idempotent. Confirmed in
src/payments/callback.tsand its retry test. Revisit after upstream idempotency support ships.
That record has an actionable claim, a code location, and an invalidation condition. A transcript fragment such as "we fixed retries" has none of those properties.
My operating rule would be to retrieve memory before investigating a related subsystem, then verify every consequential claim against current code. Save a new record only after a test, review, or explicit developer decision establishes it.
Do not save credentials, customer payloads, or raw debugging dumps. Local storage does not make sensitive data harmless: retrieved text can subsequently enter model context and leave the machine through the configured model provider.
Diagnose the boundary that actually failed
Toy demonstrations stop after the editor displays a connected server. Production debugging starts there.
If Cursor cannot launch Engram, inspect the executable path and editor environment first. A command that works in an interactive shell may depend on startup files the editor never loads.
If the process starts but tools do not appear, investigate MCP initialization and transport compatibility. Standard-output logging can corrupt a standard-input/output protocol; diagnostics belong on standard error.
If retrieval succeeds but produces irrelevant records, the transport is probably healthy. Inspect record wording and project attribution instead. FTS5 provides lexical retrieval, not a guarantee that "login callback" will find a record describing "authentication redirect."
Measure the actual pain point with paired fresh sessions on the same repository revision. Record time to the first correct patch, repeated clarification questions, retrieved text volume, and stale-memory corrections. Keep the model and relay configuration unchanged.
A faster answer that requires undoing an obsolete decision is not an improvement.
The trade-off
Engram separates durable context from an editor's conversation lifecycle. That is useful, but it creates a small knowledge store that needs ownership, deletion, and revision.
The integration earns its place when verified decisions survive session resets without displacing code inspection. If maintaining those records takes longer than rediscovering them, store fewer records—not longer conversations.
Here is the dilemma I have not resolved: how much stale context should you tolerate before a memory system becomes net negative? I do not have a clean answer yet.
Top comments (0)