Back to Projects
opencode-mem0-selfhost
CompletedTypeScriptNode.jsDocker

opencode-mem0-selfhost

Self-hosted Mem0 REST backend for OpenCode — persistent AI memory for coding agents

Timeline

Jun 2026

Role

Solo

Team

Solo

Status
Completed

Technology Stack

TypeScript
Node.js
Docker

Key Challenges

  • Reverse-engineering the Mem0 server API by reading Python source — no official REST docs existed for the self-host endpoints
  • Mapping the mem0ai SDK call patterns to plain fetch calls without losing the upstream plugin behaviour
  • Handling the app_id mismatch: the self-host REST API has no top-level app_id field, only metadata
  • Keeping the fork in sync with upstream changes via an automated weekly GitHub Actions workflow

Key Learnings

  • Self-host REST APIs often have undocumented constraints — reading the server source is the only reliable spec
  • A thin REST client can replace a heavy SDK dependency entirely, cutting bundle size and attack surface
  • OIDC trusted publishing on npm eliminates the need for long-lived npm tokens in CI
  • Designing for opt-in telemetry by default respects self-host users privacy expectations

The Problem

AI coding assistants are powerful, but they suffer from amnesia. Every new session starts from scratch — no memory of past decisions, conventions, or lessons learned. The official Mem0 OpenCode plugin solves this, but it's hardcoded to call Mem0's hosted platform. If you run your own infrastructure, there's no documented way to point it at a self-hosted Mem0 server.

What I Built

opencode-mem0-selfhost is a community-maintained fork that swaps the mem0ai SDK for a thin REST client — about 480 lines of TypeScript that talk directly to your self-hosted Mem0 server. Everything else from the upstream plugin is preserved: the 9 memory tools, 7 lifecycle hooks, 9 slash skills, the scope model, and the auto-dream consolidation scheduler.

The result is a drop-in replacement. You change one line in your opencode.json:

{
  "plugin": ["opencode-mem0-selfhost"]
}

Restart OpenCode, and your coding agent now has persistent memory backed by your own server. No data leaves your infrastructure.

How It Works

The plugin intercepts OpenCode's lifecycle hooks to inject relevant memories into context before each task, and to capture new memories after tool executions. The memory operations — add, search, get, update, delete — are all plain fetch calls against the Mem0 REST API.

Architecture at a glance

OpenCode session
    │
    ├── chat.message hook ──→ injects relevant memories into context
    ├── tool.execute.after ──→ captures new memories
    ├── /mem0-* slash skills ──→ manual memory operations
    │
    └── SelfHostMemoryClient (selfhost-client.ts)
            │
            └── fetch → MEM0_HOST/memories, /search, /entities

The SelfHostMemoryClient handles the translation layer: it maps the plugin's identity model (user_id, app_id) to what the self-host REST API expects, manages X-API-Key authentication, and does client-side filtering for fields the server can't filter on (like app_id, which lives in metadata).

Key Design Decisions

Why a thin REST client instead of the SDK?

The mem0ai SDK is designed for the hosted platform. It handles auth, retries, and response parsing for a different API surface. Stripping it out and replacing it with raw fetch calls meant:

  • Zero external dependencies beyond what OpenCode already provides
  • Full control over request/response shapes, error messages, and auth headers
  • Smaller bundle — the final ESM bundle is 0.49 MB with no SDK bloat

Opt-in telemetry by default

The upstream plugin sends anonymous usage events to PostHog by default. Self-host users tend to be more privacy-conscious — they're running their own infrastructure for a reason. This fork flips the default: MEM0_TELEMETRY is false unless you explicitly set it to true.

Automated upstream sync

Mem0 moves fast. To avoid falling behind, a GitHub Actions workflow runs weekly, compares the upstream mem0ai/mem0@main HEAD against a pinned SHA in .upstream-sha, and opens a PR with the diff if anything changed. Porting is manual (since the SDK-to-REST translation layer differs), but the diff is typically a 10–30 minute job per release.

What's Included

ComponentCountNotes
Memory tools9add_memory, search_memories, get_memories, get_memory, update_memory, delete_memory, delete_all_memories, delete_entities, list_entities
Lifecycle hooks7config, chat.message, tool.execute.before/after, chat.messages.transform, session.compacting, shell.env
Slash skills9/mem0-remember, /mem0-tour, /mem0-search, /mem0-status, /mem0-scope, /mem0-dream, /mem0-forget, /mem0-pin, /mem0-context-loader
Unit tests63All passing with bun test

Self-Host Caveats Learned the Hard Way

A few things that don't show up in the API docs but bite you in practice:

  • Port depends on how you run the server. Docker Compose publishes 8000 as 8888. Raw docker run and uvicorn listen on 8000. The plugin defaults to 8888 (the Compose path).
  • No /health endpoint. The server has no health check route. The plugin's health() method pings /openapi.json instead.
  • No rerank parameter. The Python SDK's search() accepts rerank: bool, but the REST server's SearchRequest schema filters it out. The skills were updated to drop rerank: true.
  • delete_all_memories is a list-then-delete loop. The server's bulk DELETE /memories endpoint is admin-only. The plugin fetches matching IDs and deletes them one at a time, so it works with a regular per-user API key.

Try It

If you're running a self-hosted Mem0 server and use OpenCode, give it a spin:

# In your opencode.json
{
  "plugin": ["opencode-mem0-selfhost"]
}

Set MEM0_API_KEY and MEM0_HOST as environment variables, restart OpenCode, and run /mem0-status to verify the connection. The plugin auto-installs from npm — no build step required.

Timeline

Forked from the upstream Mem0 OpenCode plugin and developed actively in late June 2026. The fork replaced the mem0ai SDK with a thin REST client, added a test suite, and set up an automated workflow to keep the fork in sync with upstream changes.

  • Jun 2026 — Forked from the upstream Mem0 OpenCode plugin, replaced the mem0ai SDK with a thin REST client (~480 lines of TypeScript), added 63 unit tests, published to npm
  • Jun 2026 — Automated upstream sync workflow added; project actively maintained

Design & Developed by Sudip Ghosh
© 2026. All rights reserved.