403 lines
14 KiB
Markdown
403 lines
14 KiB
Markdown
# Waggle Memory Plugin — Claude Code Agent Onboarding Brief
|
|
|
|
**Date:** 2026-04-13
|
|
**Author:** Marko Markovic + Claude (Waggle OS session)
|
|
**Purpose:** Everything a new Claude Code agent needs to build the Waggle Memory plugin for Claude Code, Claude.ai, Cowork, OpenClaw, and compatible systems.
|
|
|
|
---
|
|
|
|
## 1. What You're Building
|
|
|
|
A **persistent memory MCP server** that gives Claude Code (and any MCP-compatible AI system) the ability to:
|
|
|
|
1. **Remember** across conversations — save decisions, preferences, facts, project context
|
|
2. **Recall** with semantic search — keyword + vector hybrid search over all memories
|
|
3. **Harvest** — import conversation history from ChatGPT, Gemini, Perplexity, Cursor, Copilot exports
|
|
4. **Knowledge Graph** — automatically extract and track entities (people, projects, concepts) and their relationships
|
|
5. **Cross-workspace** — organize memories into workspaces and search across them
|
|
|
|
This is NOT a toy memory system. It's battle-tested production code from Waggle OS (a Tauri desktop app with 150+ API routes, 60+ agent tools, 305 test files) being extracted into a standalone MCP server.
|
|
|
|
---
|
|
|
|
## 2. Source Code Available
|
|
|
|
The full Waggle OS codebase has been copied to your working directory. The relevant packages:
|
|
|
|
```
|
|
packages/core/src/ ← THE MEMORY ENGINE (this is what you're wrapping)
|
|
├── mind/
|
|
│ ├── db.ts MindDB — SQLite wrapper (WAL mode, sqlite-vec loaded)
|
|
│ ├── schema.ts Full schema: memory_frames, FTS5, vec table, entities, relations
|
|
│ ├── frames.ts FrameStore — I/P/B frame CRUD, dedup, compact
|
|
│ ├── search.ts HybridSearch — FTS5 keyword + sqlite-vec k-NN + RRF fusion
|
|
│ ├── knowledge.ts KnowledgeGraph — entity/relation CRUD, traversal, validation
|
|
│ ├── identity.ts IdentityLayer — user identity persistence
|
|
│ ├── awareness.ts AwarenessLayer — active task/context tracking
|
|
│ ├── sessions.ts SessionStore — conversation session tracking
|
|
│ ├── scoring.ts Relevance scoring (temporal, popularity, importance, contextual)
|
|
│ ├── embeddings.ts Embedder interface
|
|
│ ├── inprocess-embedder.ts Zero-config embedder (Xenova/all-MiniLM-L6-v2, 384→1024 dims)
|
|
│ ├── ollama-embedder.ts Ollama embedder (nomic-embed-text)
|
|
│ ├── api-embedder.ts OpenAI/Voyage embedder
|
|
│ ├── embedding-provider.ts Provider chain: InProcess → Ollama → API → Mock
|
|
│ ├── entity-normalizer.ts Dedup entity names
|
|
│ ├── ontology.ts Entity type validation
|
|
│ └── reconcile.ts FTS/vec index repair
|
|
├── harvest/
|
|
│ ├── pipeline.ts 4-pass harvest: Classify → Extract → Synthesize → Dedup
|
|
│ ├── chatgpt-adapter.ts ChatGPT export parser
|
|
│ ├── claude-adapter.ts Claude export parser
|
|
│ ├── claude-code-adapter.ts Claude Code session scanner
|
|
│ ├── gemini-adapter.ts Gemini export parser
|
|
│ ├── universal-adapter.ts Generic JSON/JSONL parser
|
|
│ ├── source-store.ts Track registered sources + auto-sync
|
|
│ ├── dedup.ts SHA-256 dedup within 500-frame window
|
|
│ └── types.ts ImportSourceType, UniversalImportItem, etc.
|
|
├── multi-mind.ts MultiMind (personal + workspace search)
|
|
├── multi-mind-cache.ts LRU cache of open MindDB handles (max 20)
|
|
├── workspace-config.ts Workspace CRUD (id, name, directory, config)
|
|
├── config.ts WaggleConfig (settings persistence)
|
|
├── team-sync.ts Push/pull frames to team server
|
|
└── file-store.ts FileStore (Local, Linked, S3)
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Architecture of the Memory System
|
|
|
|
### Frame Types (the fundamental unit)
|
|
- **I-Frame (Identity):** Base fact — "User prefers TypeScript over JavaScript"
|
|
- **P-Frame (Procedural):** Update to an I-Frame — "Also prefers functional style"
|
|
- **B-Frame (Bridging):** Links between frames
|
|
|
|
### Importance Levels
|
|
`critical | important | normal | temporary | deprecated`
|
|
|
|
### Source Provenance
|
|
`user_stated | tool_verified | agent_inferred | import | system | team_sync`
|
|
|
|
### Search Pipeline
|
|
1. **FTS5 keyword search** — stop-word filtered, OR-based
|
|
2. **sqlite-vec k-NN search** — 1024-dim embeddings
|
|
3. **RRF Fusion** (K=60) — combines keyword + vector results
|
|
4. **Relevance scoring** — 4 profiles: balanced, recent, important, connected
|
|
|
|
### Embedding Chain (probed in order)
|
|
1. **InProcess** — Xenova/all-MiniLM-L6-v2, 384→1024 dims, zero config, ~23MB download
|
|
2. **Ollama** — nomic-embed-text (needs local Ollama)
|
|
3. **Voyage/OpenAI** — API-based (needs keys)
|
|
4. **Mock** — deterministic fallback (always works)
|
|
|
|
### Knowledge Graph
|
|
- Entities: `{ type, name, properties, validFrom, validTo }`
|
|
- Relations: `{ sourceId, targetId, type, confidence, properties }`
|
|
- Traversal: BFS with depth limit
|
|
- Types: person, project, concept, organization, technology, tool, location, event
|
|
|
|
---
|
|
|
|
## 4. What the MCP Server Should Expose
|
|
|
|
### Tools (MCP tool definitions)
|
|
|
|
```
|
|
save_memory(content, importance?, workspace?)
|
|
→ Creates an I-Frame in the target workspace mind
|
|
→ Auto-indexes in FTS5 + vector
|
|
→ Returns { id, content, importance, timestamp }
|
|
|
|
recall_memory(query, limit?, workspace?, scope?)
|
|
→ HybridSearch across personal + workspace minds
|
|
→ scope: 'current' | 'personal' | 'all' | 'global'
|
|
→ Returns ranked results with scores
|
|
|
|
search_entities(query, type?, limit?)
|
|
→ Search the knowledge graph for entities
|
|
→ Returns matching entities with relations
|
|
|
|
save_entity(type, name, properties?)
|
|
→ Upsert an entity in the knowledge graph
|
|
→ Auto-dedup by normalized name
|
|
|
|
get_identity()
|
|
→ Returns the user's identity profile from IdentityLayer
|
|
|
|
set_identity(updates)
|
|
→ Update identity (name, role, preferences, etc.)
|
|
|
|
get_awareness()
|
|
→ Returns current awareness items (active tasks, context)
|
|
|
|
set_awareness(key, value, priority?, ttl?)
|
|
→ Set an awareness item (active context)
|
|
|
|
list_workspaces()
|
|
→ Returns all workspaces with stats
|
|
|
|
harvest_import(source, data_or_path)
|
|
→ Import conversations from external sources
|
|
→ source: 'chatgpt' | 'claude' | 'claude-code' | 'gemini' | 'universal'
|
|
```
|
|
|
|
### Resources (MCP resource definitions)
|
|
|
|
```
|
|
memory://personal/stats → frame count, entity count, last session
|
|
memory://workspace/{id} → workspace info + memory stats
|
|
memory://identity → current identity profile
|
|
memory://awareness → current awareness items
|
|
```
|
|
|
|
---
|
|
|
|
## 5. How to Build It
|
|
|
|
### Step 1: Create the MCP Server
|
|
|
|
The MCP server uses `@modelcontextprotocol/sdk` (the official MCP SDK). It communicates over stdio (for Claude Code) or HTTP/SSE (for web clients).
|
|
|
|
```typescript
|
|
// Structure
|
|
waggle-memory-mcp/
|
|
├── package.json
|
|
├── tsconfig.json
|
|
├── src/
|
|
│ ├── index.ts ← Entry point, MCP server setup
|
|
│ ├── tools/
|
|
│ │ ├── memory.ts ← save_memory, recall_memory
|
|
│ │ ├── knowledge.ts ← search_entities, save_entity
|
|
│ │ ├── identity.ts ← get/set identity
|
|
│ │ ├── awareness.ts ← get/set awareness
|
|
│ │ ├── workspace.ts ← list_workspaces
|
|
│ │ └── harvest.ts ← harvest_import
|
|
│ ├── resources/
|
|
│ │ └── memory.ts ← MCP resource handlers
|
|
│ └── core/
|
|
│ └── setup.ts ← MindDB init, embedding provider, workspace manager
|
|
```
|
|
|
|
### Step 2: Depend on @waggle/core
|
|
|
|
The MCP server should import directly from the Waggle core package:
|
|
|
|
```typescript
|
|
import {
|
|
MindDB, FrameStore, HybridSearch, KnowledgeGraph,
|
|
IdentityLayer, AwarenessLayer, SessionStore,
|
|
MultiMind, MultiMindCache, WorkspaceManager,
|
|
createEmbeddingProvider,
|
|
HarvestSourceStore, ChatGPTAdapter, ClaudeAdapter,
|
|
ClaudeCodeAdapter, GeminiAdapter, UniversalAdapter,
|
|
} from '@waggle/core';
|
|
```
|
|
|
|
### Step 3: Data Storage
|
|
|
|
Default data directory: `~/.waggle/` (same as Waggle OS — the plugin IS the same memory)
|
|
|
|
```
|
|
~/.waggle/
|
|
├── config.json ← settings
|
|
├── personal.mind ← personal SQLite mind
|
|
├── workspaces/
|
|
│ ├── {id}/
|
|
│ │ ├── workspace.json ← workspace config
|
|
│ │ ├── workspace.mind ← workspace SQLite mind
|
|
│ │ └── files/ ← workspace files
|
|
```
|
|
|
|
**CRITICAL:** If the user also runs Waggle OS desktop, the MCP server shares the SAME data directory. This is BY DESIGN — memories saved in Claude Code appear in Waggle OS and vice versa. The SQLite WAL mode supports concurrent readers.
|
|
|
|
### Step 4: Registration in Claude Code
|
|
|
|
The user adds to `~/.claude/claude_desktop_config.json`:
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"waggle-memory": {
|
|
"command": "npx",
|
|
"args": ["waggle-memory-mcp"],
|
|
"env": {
|
|
"WAGGLE_DATA_DIR": "~/.waggle"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Or for Claude Code CLI in `~/.claude/settings.json` under `mcpServers`.
|
|
|
|
---
|
|
|
|
## 6. Key Design Decisions
|
|
|
|
### Shared data directory
|
|
The MCP server and Waggle OS desktop share `~/.waggle/`. This means:
|
|
- Memory saved in Claude Code → visible in Waggle OS
|
|
- Memory saved in Waggle OS → available to Claude Code
|
|
- The user has ONE memory, not two
|
|
|
|
### Embedding provider
|
|
The InProcess embedder works offline with zero config. It downloads a 23MB ONNX model on first run. This should be the default — no API keys needed.
|
|
|
|
### Workspace management
|
|
- The MCP server can create/list/switch workspaces
|
|
- Each workspace gets its own `.mind` SQLite file
|
|
- A "current workspace" can be set via a tool or env var
|
|
- Default: personal mind (no workspace)
|
|
|
|
### Concurrent access safety
|
|
- SQLite WAL mode allows concurrent readers
|
|
- The MCP server should open MindDB handles and keep them open (not open/close per request)
|
|
- Use MultiMindCache for LRU management of workspace handles
|
|
|
|
---
|
|
|
|
## 7. Compatibility Targets
|
|
|
|
### Primary: Claude Code
|
|
- MCP server over stdio
|
|
- `~/.claude/settings.json` registration
|
|
|
|
### Secondary: Claude.ai / Claude Desktop
|
|
- MCP server over stdio
|
|
- `~/.claude/claude_desktop_config.json` registration
|
|
|
|
### Tertiary: Compatible systems
|
|
- Any MCP-compatible host: OpenClaw, Hermes Agent, Cowork, etc.
|
|
- Same stdio protocol
|
|
- The MCP SDK handles the transport
|
|
|
|
---
|
|
|
|
## 8. What NOT to Build
|
|
|
|
- **No UI** — this is a headless MCP server. Waggle OS desktop IS the UI.
|
|
- **No LLM integration** — the host (Claude Code) provides the LLM. The MCP server is tools-only.
|
|
- **No auth** — local-only server. Auth is handled by the host.
|
|
- **No HTTP server** — stdio transport only (Claude Code standard). HTTP/SSE can be added later.
|
|
- **No team sync** — the MCP server works on the local mind only. Team features need Waggle OS.
|
|
|
|
---
|
|
|
|
## 9. SQLite Schema (from packages/core/src/mind/schema.ts)
|
|
|
|
The schema auto-creates when MindDB is instantiated. Key tables:
|
|
|
|
```sql
|
|
-- Memory frames (the fundamental unit)
|
|
memory_frames (
|
|
id INTEGER PRIMARY KEY,
|
|
gop_id TEXT NOT NULL REFERENCES sessions(gop_id),
|
|
frame_type TEXT NOT NULL CHECK (frame_type IN ('I','P','B')),
|
|
base_frame_id INTEGER REFERENCES memory_frames(id),
|
|
t INTEGER NOT NULL DEFAULT 0,
|
|
content TEXT NOT NULL,
|
|
importance TEXT NOT NULL DEFAULT 'normal',
|
|
source TEXT DEFAULT 'user_stated',
|
|
access_count INTEGER NOT NULL DEFAULT 0,
|
|
last_accessed TEXT,
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
content_hash TEXT
|
|
)
|
|
|
|
-- Full-text search index
|
|
memory_frames_fts USING fts5(content, content='memory_frames', content_rowid='id')
|
|
|
|
-- Vector embeddings (sqlite-vec)
|
|
memory_embeddings USING vec0(embedding float[1024])
|
|
|
|
-- Knowledge graph
|
|
knowledge_entities (id, type, name, properties JSON, valid_from, valid_to, created_at)
|
|
knowledge_relations (id, source_id, target_id, type, confidence, properties JSON, valid_from, valid_to)
|
|
|
|
-- Sessions
|
|
sessions (gop_id TEXT PK, title TEXT, summary TEXT, status TEXT, created_at, closed_at)
|
|
|
|
-- Identity
|
|
identity (key TEXT PK, value TEXT, updated_at TEXT)
|
|
|
|
-- Awareness
|
|
awareness_items (key TEXT PK, value TEXT, priority INTEGER, expires_at TEXT, created_at)
|
|
|
|
-- Harvest tracking
|
|
harvest_sources (source TEXT UNIQUE, display_name, source_path, last_synced_at, items_imported, frames_created, auto_sync, sync_interval_hours)
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Testing Checklist
|
|
|
|
Before shipping:
|
|
- [ ] `save_memory` creates an I-Frame retrievable by `recall_memory`
|
|
- [ ] `recall_memory` returns relevant results with semantic search (not just exact match)
|
|
- [ ] FTS5 index stays in sync with frames
|
|
- [ ] Vector index stays in sync with frames
|
|
- [ ] Knowledge graph entities extractable from saved memories
|
|
- [ ] Harvest import works for at least: ChatGPT JSON, Claude JSON, Claude Code local scan
|
|
- [ ] Multiple workspaces can be created and searched independently
|
|
- [ ] `scope: 'global'` searches all workspaces
|
|
- [ ] InProcess embedder works with zero config
|
|
- [ ] Concurrent access doesn't corrupt (two MCP instances)
|
|
- [ ] Shared data dir with Waggle OS desktop works (read memories saved by the other)
|
|
|
|
---
|
|
|
|
## 11. Package Dependencies
|
|
|
|
From Waggle OS (copy or depend directly):
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"@modelcontextprotocol/sdk": "latest",
|
|
"better-sqlite3": "^11.0.0",
|
|
"sqlite-vec": "^0.1.0",
|
|
"@xenova/transformers": "^2.17.0",
|
|
"glob": "^10.0.0"
|
|
}
|
|
}
|
|
```
|
|
|
|
If depending on `@waggle/core` directly (recommended):
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"@modelcontextprotocol/sdk": "latest",
|
|
"@waggle/core": "workspace:*"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Quick Start for the Building Agent
|
|
|
|
```bash
|
|
# 1. Read the core memory engine
|
|
cat packages/core/src/mind/db.ts # MindDB constructor + schema
|
|
cat packages/core/src/mind/frames.ts # FrameStore API
|
|
cat packages/core/src/mind/search.ts # HybridSearch API
|
|
cat packages/core/src/mind/knowledge.ts # KnowledgeGraph API
|
|
|
|
# 2. Read the embedding provider
|
|
cat packages/core/src/mind/embedding-provider.ts # Provider chain
|
|
|
|
# 3. Read the harvest pipeline
|
|
cat packages/core/src/harvest/pipeline.ts
|
|
cat packages/core/src/harvest/types.ts
|
|
|
|
# 4. Read the workspace manager
|
|
cat packages/core/src/workspace-config.ts
|
|
|
|
# 5. Check the MCP runtime pattern (how Waggle already runs MCP servers)
|
|
cat packages/agent/src/mcp/mcp-runtime.ts
|
|
```
|
|
|
|
The entire memory engine is in `packages/core/`. You're wrapping it in an MCP server. Don't reinvent — import and expose.
|
|
|
|
---
|
|
|
|
*Generated from Waggle OS codebase audit, session 2026-04-12/13. 26 commits shipped this session.*
|