OpenCode plugin for persistent memory using Graphiti MCP server.
Your agent remembers what you tell it - across sessions, across projects.
Step 1: Add the plugin to your OpenCode config (~/.config/opencode/opencode.jsonc or project-level .opencode/opencode.jsonc):
Step 2: Set environment variables:
export GRAPHITI_URL="http://your-graphiti-server:8000/mcp"
export GRAPHITI_USER_ID="your-user-id"Or use explicit group ID:
export GRAPHITI_URL="http://your-graphiti-server:8000/mcp"
export GRAPHITI_GROUP_ID="your-group-id"Step 3: Restart OpenCode. The plugin is auto-installed from npm.
Alternatively, run the installer to automate Step 1:
# Using npx (npm)
npx @ceris/opencode-graphiti@latest install
# Using bunx (Bun)
bunx @ceris/opencode-graphiti@latest installOr install globally and run directly:
npm install -g @ceris/opencode-graphiti
opencode-graphiti installOr let your agent do it - paste this into OpenCode:
Install @ceris/opencode-graphiti by following https://raw.githubusercontent.com/artemfomin/opencode-graphiti/main/README.md
Instructions for LLM Agents (click to expand)
If you're an LLM agent helping set up @ceris/opencode-graphiti, follow these steps:
Read ~/.config/opencode/opencode.jsonc (or .opencode/opencode.jsonc in the project root) and add @ceris/opencode-graphiti to the plugin array:
{
"plugin": [
"@ceris/opencode-graphiti"
// ... other plugins
]
}If the file doesn't exist, create it. OpenCode will auto-install the package from npm on restart.
If manual config editing fails, try the CLI installer as a fallback:
npx @ceris/opencode-graphiti@latest install --no-tui
# or: bunx @ceris/opencode-graphiti@latest install --no-tuiSet required environment variables:
export GRAPHITI_URL="http://your-graphiti-server:8000/mcp"
export GRAPHITI_USER_ID="your-user-id"Or create ~/.config/opencode/graphiti.jsonc:
{
"graphitiUrl": "http://localhost:8000/mcp",
"userId": "your-user-id"
}Tell the user to restart OpenCode and run:
opencode -cThey should see graphiti in the tools list. If not, check:
- Is
GRAPHITI_URLand (GRAPHITI_USER_IDorGRAPHITI_GROUP_ID) set? - Is the plugin in
opencode.jsonc? - Check logs:
tail ~/.opencode-graphiti.log
Run /graphiti-init to have the agent explore and memorize the codebase.
On first message, the agent receives (invisible to user):
- User profile (cross-project preferences)
- Project memories (all project knowledge)
- Relevant memories (semantic search)
Example of what the agent sees:
[GRAPHITI]
User Profile:
- Prefers concise responses
- Expert in TypeScript
Project Knowledge:
- Uses Bun, not Node.js
- Build: bun run build
Relevant Memories:
- Build fails if .env.local missing
The agent uses this context automatically - no manual prompting needed.
Say "remember", "save this", "don't forget" etc. and the agent auto-saves to memory.
You: "Remember that this project uses bun"
Agent: [saves to project memory]
Add custom triggers via keywordPatterns config.
Run /graphiti-init to explore and memorize your codebase structure, patterns, and conventions.
When context hits 80% capacity:
- Triggers OpenCode's summarization
- Injects project memories into summary context
- Saves session summary as a memory
This preserves conversation context across compaction events.
API key is <private>sk-abc123</private>
Content in <private> tags is never stored.
The graphiti tool is available to the agent:
| Mode | Args | Description |
|---|---|---|
add |
content, type?, scope? |
Store memory |
search |
query, scope? |
Search memories |
profile |
query? |
View user profile |
list |
scope?, limit? |
List memories |
forget |
memoryId, scope? |
Delete memory |
help |
- | Show help text |
Examples:
// Add a project memory
graphiti({ mode: "add", content: "This project uses Bun", type: "project-config" })
// Search memories
graphiti({ mode: "search", query: "build command" })
// List memories
graphiti({ mode: "list", limit: 10 })Scopes: user (cross-project), project (default)
Types: project-config, architecture, error-solution, preference, learned-pattern, conversation
| Scope | Namespace | Persists |
|---|---|---|
| User | {userId} or {groupId}_profile |
All projects |
| Project | {groupId}_{hash} |
This project |
The project namespace uses a hash derived from the git remote URL (origin) combined with the relative path from the git root. This ensures:
- The same project cloned on different machines produces identical namespaces
- Different subdirectories in a monorepo get distinct namespaces
- SSH and HTTPS remotes for the same repo produce the same hash
When no git remote is available (e.g., non-git directories), the plugin falls back to hashing the local directory path.
Create ~/.config/opencode/graphiti.jsonc:
{
// Graphiti MCP server URL (required)
"graphitiUrl": "http://localhost:8000/mcp",
// User identifier for automatic namespacing (optional*)
"userId": "john",
// Base group ID for namespacing (optional*)
// Auto-derived as {userId}_{projectName} if userId set but groupId not
"groupId": "myteam",
// Profile namespace (optional, default: "{userId}" or "{groupId}_profile")
"profileGroupId": "myteam_profile",
// Max memories injected per request (default: 5)
"maxMemories": 5,
// Max project memories listed (default: 10)
"maxProjectMemories": 10,
// Max profile facts injected (default: 5)
"maxProfileItems": 5,
// Include user profile in context (default: true)
"injectProfile": true,
// Extra keyword patterns for memory detection (regex)
"keywordPatterns": ["log\\s+this", "write\\s+down"],
// Context usage ratio that triggers compaction (default: 0.80)
"compactionThreshold": 0.80
}Required fields: graphitiUrl, (userId or groupId)
Either userId or groupId must be set. When userId is set without an explicit groupId, the groupId is automatically derived as {userId}_{projectName}.
Environment variables (take precedence over config file):
GRAPHITI_URL- MCP server URLGRAPHITI_USER_ID- User identifier for automatic namespacingGRAPHITI_GROUP_ID- Base group ID (or auto-derived from userId)
When userId is set but groupId is not explicitly provided, the plugin automatically derives groupId:
groupId = {userId}_{projectName}
Where projectName is extracted from package.json name field (or directory name as fallback), sanitized to remove special characters.
Example:
userId:"john"- Project name:
"my-app" - Derived
groupId:"john_my-app"
This allows you to configure userId once globally, and each project gets its own namespace automatically.
Priority order for groupId resolution:
GRAPHITI_GROUP_IDenvironment variable (highest)- Local config
groupId(.opencode/graphiti.jsonc) - Global config
groupId(~/.config/opencode/graphiti.jsonc) - Auto-derived from
userId+ project name (if userId is set)
Create .opencode/graphiti.jsonc in your project root to override settings:
{
"groupId": "project-specific-id",
"maxProjectMemories": 20
}Local config merges with global config. Environment variables take highest precedence.
- Package renamed:
opencode-graphiti→@ceris/opencode-graphiti. Update youropencode.jsonc:{ "plugin": ["@ceris/opencode-graphiti"] } - Project hash changed: The project namespace hash now uses the git remote URL instead of the local directory path. Existing memories stored under old hashes will not be automatically migrated. Run
/graphiti-initto rebuild your project's memory.
If you're using Oh My OpenCode, disable its built-in auto-compact hook to let graphiti handle context compaction:
Add to ~/.config/opencode/oh-my-opencode.json:
{
"disabled_hooks": ["anthropic-context-window-limit-recovery"]
}bun install
bun run build
bun run typecheck
bun testLocal install:
{
"plugin": ["file:///path/to/@ceris/opencode-graphiti"]
}tail -f ~/.opencode-graphiti.logMIT
{ "plugin": ["@ceris/opencode-graphiti"] }