·

Memory MCP With OpenCode

Set up Memory MCP in OpenCode so your AI agent can persist and recall context across sessions right from your editor.

OpenCode's MCP configuration shape differs enough from Claude Code and Cursor that copy-pasting a mcpServers block from one of those tools straight into opencode.json will fail silently or throw a schema error, depending on version. This topic covers the OpenCode-specific config, a realistic session pattern, and the rough edges you'll hit that the other clients don't have.


Installing and Connecting Memory MCP to OpenCode

OpenCode reads MCP server definitions from opencode.json (project-level) or ~/.config/opencode/opencode.json (global), under an mcp key — not mcpServers. Each entry needs an explicit type ("local" for a subprocess server, "remote" for an HTTP/SSE endpoint) and the command as an array rather than separate command/args fields:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "memory": {
      "type": "local",
      "command": ["npx", "-y", "@modelcontextprotocol/server-memory"],
      "environment": {
        "MEMORY_FILE_PATH": "/Users/dat/agent-memory/english-grammar-platform.json"
      },
      "enabled": true
    }
  }
}

Put this at the project root as opencode.json if you want it scoped to one repo, or in the global config if you want the same memory server available everywhere (with the caveats about cross-project pollution covered in this module's first topic — a global mcp entry doesn't give you a global memory file unless you also point every project at the same MEMORY_FILE_PATH, which you generally shouldn't).

Start OpenCode and confirm the server loaded:

opencode
/mcp

If it shows disconnected, the most common cause isn't a config typo — it's npx failing to resolve the package because OpenCode launched from a shell without your usual PATH (common when OpenCode is launched from a GUI app launcher rather than a terminal). Confirm by running the exact command array manually:

npx -y @modelcontextprotocol/server-memory

If that hangs correctly but OpenCode still reports disconnected, check enabled: true is actually present — omitting it defaults to disabled in some OpenCode config schema versions, which is an easy miss when copying a snippet from documentation that assumes the field is optional.

Tips
- OpenCode's config key is mcp, not mcpServers, and command is an array — porting a Claude Code or Cursor config verbatim will not work.
- Test npx -y @modelcontextprotocol/server-memory manually in the same shell context OpenCode launches from if the server shows disconnected.
- Explicitly set "enabled": true — don't rely on it defaulting the way you'd expect from other MCP clients.


Storing and Retrieving Session Context in OpenCode

Once connected, the interaction pattern is identical in substance to any other MCP client — the tool names and JSON shapes are server-defined, not client-defined, so create_entities, search_nodes, and the rest behave the same everywhere. What differs is how naturally OpenCode's session model nudges you toward using them.

OpenCode sessions are more explicitly file-and-directory scoped than Claude Code's — each session tracks a working directory, and it's common to run several concurrent OpenCode sessions across different branches or worktrees of the same repo. That makes memory even more valuable here than in a single-session tool: two OpenCode sessions on different feature branches, both pointed at the same MEMORY_FILE_PATH, share facts about the codebase in real time.

Session A (working on feature/rate-limit-v2):

  Prompt: Store a memory entity for the new rate limiter design —
  entityType "decision", name "rate-limit-v2", noting we're moving
  to a sliding-window-log algorithm this time, with a fix for the
  burst-rejection issue via a grace-window parameter.
Session B (working on feature/billing-refactor, separate worktree):

  Prompt: Before I touch anything calling the rate limiter's
  public interface, check memory for related changes in flight.

  → search_nodes("rate limit")
  → finds "rate-limit-v2" decision entity from Session A, created
    20 minutes ago

That cross-session, near-real-time visibility is the concrete payoff of a shared JSON file over per-session in-memory state — Session B finds out about an in-progress interface change on another branch without anyone sending a Slack message. The risk is the mirror image of the benefit: two sessions writing observations to the same file concurrently can race. The server re-reads and rewrites the whole file per call, so a genuinely simultaneous write from two sessions can lose one of the two updates. In practice this only bites during rapid parallel work; stagger memory writes when you know two sessions are active at once, or dedicate specific entities to specific work streams.

{
  "type": "entity",
  "name": "rate-limit-v2",
  "entityType": "decision",
  "observations": [
    "Moving to sliding-window-log algorithm",
    "Adds a grace-window parameter to fix the 2026 burst-rejection issue",
    "In progress on feature/rate-limit-v2 as of 2026-08-21"
  ]
}

Tips
- Point every concurrent OpenCode worktree/session at the same MEMORY_FILE_PATH when they're working on the same repo — this is where shared memory pays off fastest.
- Be aware of the read-modify-write race on the JSON file under truly simultaneous writes from two sessions; it's rare but not impossible.
- Tag time-sensitive observations with a date and status ("in progress as of...") since the graph has no built-in staleness tracking.


Practical Example: Resuming a Multi-Day Refactor Without Re-Briefing the Agent

Take a realistic case: migrating the platform's topic-fixture pipeline from synchronous markdown conversion to an async job queue, spread across three days of OpenCode sessions with gaps in between.

Day 1, end of session:

Prompt: Summarize today's refactor progress and store it as memory.
Entity: entityType "decision", name "fixture-pipeline-async-migration".
Include: what's done, what's not, and the one design question still open.
{
  "type": "entity",
  "name": "fixture-pipeline-async-migration",
  "entityType": "decision",
  "observations": [
    "Goal: move convert_topic_bodies.py from sync CLI script to a queued job",
    "Day 1: extracted the markdown->HTML conversion logic into content/fixtures/converter.py, unit tested in isolation",
    "Not yet done: the queue backend choice — evaluating RQ vs a simple DB-polled table, leaning DB-polled to avoid a new infra dependency",
    "Open question: does the queue need to survive a Django app restart mid-conversion, or is at-most-once fine for this workload?"
  ]
}

Day 3, new OpenCode session, no manual briefing:

Prompt: Resume the fixture pipeline async migration. Check memory
first for where we left off.

→ open_nodes(["fixture-pipeline-async-migration"])
→ returns the full entity above

Agent: Picking up from Day 1 — converter.py extraction is done and
tested. The open question was queue backend choice. Since you leaned
toward DB-polled to avoid new infra, I'll scaffold that unless you've
decided otherwise since then.

The agent restated the open decision instead of silently picking one, which is exactly the behavior you want — memory should surface unresolved questions, not paper over them with a confident-sounding guess. Compare this to the no-memory alternative: either you re-type the Day 1 summary by hand on Day 3, or you skip it and the agent starts proposing a Redis-backed queue because that's the generically "correct" answer, ignoring the infra constraint you'd already ruled it out for.

Tips
- Explicitly store open questions and undecided trade-offs, not just completed work — that's what prevents an agent from silently re-deciding something you'd already ruled on.
- Use open_nodes with the exact entity name when resuming known work — it's a direct fetch, cheaper than a search_nodes guess.
- End every multi-day task's session with an explicit "what's done / what's not / what's open" memory write — this is the single habit with the highest payoff for resumability.


Known Limitations for Memory MCP in OpenCode

Be upfront about where this breaks down in OpenCode specifically, beyond the generic limitations of the memory server itself (no semantic search, no encryption, unbounded read_graph growth — covered in this module's first topic):

  • No first-class memory UI. OpenCode's TUI shows connected MCP servers and lets you inspect tool calls in the session log, but there's no dedicated pane to browse the knowledge graph the way you might browse files in the file tree. Inspecting the graph directly means reading the JSON file yourself or asking the agent to read_graph and summarize.
  • Config schema volatility. OpenCode's opencode.json schema has changed shape across versions (the mcp key structure, the environment vs env field naming) more than Claude Code's or Cursor's have. Pin your OpenCode version or double-check the $schema URL's current shape before assuming a config snippet from six months ago still validates.
  • Concurrent-session write races, covered above — sharper here than in single-session tools because running multiple OpenCode sessions in parallel worktrees is a normal, encouraged workflow, not an edge case.
  • No automatic project-vs-global scoping enforcement. Nothing stops a global opencode.json memory config from silently shadowing a project-level one, or vice versa, depending on which config OpenCode resolves first for a given launch directory — verify with /mcp in-session rather than assuming your project config took effect.

None of these are severe enough to avoid the integration — they're the kind of thing you learn once and route around, not blockers.

Tips
- Don't expect a graph browser UI in OpenCode — plan on read_graph + agent summary, or a small external script, if you need to eyeball the whole memory file.
- Re-check your opencode.json against the current $schema after any OpenCode upgrade — the mcp block's shape isn't guaranteed stable across versions.
- Confirm which config (project or global) actually took effect via /mcp rather than assuming — scoping precedence isn't always obvious from the files alone.


Tips

Tips
- OpenCode's mcp config key, array-form command, and environment field are meaningfully different from Claude Code/Cursor's mcpServers shape — don't copy-paste configs across tools.
- Shared memory across concurrent OpenCode worktree sessions is this client's strongest use case for memory MCP — lean into it deliberately.
- Store open questions and unresolved trade-offs explicitly, not just completed work, so resumed sessions don't silently re-decide settled matters.
- Watch for config schema drift across OpenCode versions and re-validate after upgrades.