Version: 7.0.0 (December 2025)
Target Audience: Developers building claude-mem integrations (VSCode extensions, IDE plugins, CLI tools)
Quick Reference
Worker Base URL http://localhost:37777
Override with CLAUDE_MEM_WORKER_PORT
Health Check GET /api/health
Returns { "status": "ok" }
Queue Observation POST /api/sessions/observations
Pass claudeSessionId + tool data
SSE Stream GET /stream
Real-time events for UI updates
Most Common Operations
// Health check
GET / api / health
// Create/get session and queue observation
POST / api / sessions / observations
Body : { claudeSessionId , tool_name , tool_input , tool_response , cwd }
// Queue summary
POST / api / sessions / summarize
Body : { claudeSessionId , last_user_message , last_assistant_message }
// Complete session
POST / api / sessions / complete
Body : { claudeSessionId }
// Search observations
GET / api / search ? query = authentication & type = observations & format = index & limit = 20
// Get recent context for project
GET / api / context / recent ? project = my - project & limit = 3
Environment Variables
CLAUDE_MEM_MODEL = claude-sonnet-4-5 # Model for observations/summaries
CLAUDE_MEM_CONTEXT_OBSERVATIONS = 50 # Observations injected at SessionStart
CLAUDE_MEM_WORKER_PORT = 37777 # Worker service port
CLAUDE_MEM_PYTHON_VERSION = 3.13 # Python version for chroma-mcp
Build Commands (Local Development)
npm run build # Compile TypeScript (hooks + worker)
npm run sync-marketplace # Copy to ~/.claude/plugins
npm run worker:restart # Restart worker
npm run worker:logs # View worker logs
npm run worker:status # Check worker status
Worker Architecture
Request Flow
Platform Hook/Extension
→ HTTP Request to Worker (localhost:37777)
→ Route Handler (SessionRoutes/DataRoutes/SearchRoutes/etc.)
→ Domain Service (SessionManager/SearchManager/DatabaseManager)
→ Database (SQLite3 + Chroma vector DB)
→ SSE Broadcast (real-time UI updates)
Domain Services
DatabaseManager SQLite connection management, initialization
SessionManager Event-driven session lifecycle, message queues
SearchManager Search orchestration (FTS5 + Chroma)
SSEBroadcaster Server-Sent Events for real-time updates
SDKAgent Claude Agent SDK for generating observations/summaries
PaginationHelper Query pagination utilities
SettingsManager User settings CRUD
FormattingService Result formatting (index vs full)
TimelineService Unified timeline generation
Route Organization
Health check endpoint
Viewer UI (React app)
SSE stream for real-time updates
Session lifecycle (init, observations, summarize, complete)
Privacy checks and tag stripping
Auto-start SDK agent generators
Data retrieval (observations, summaries, prompts, stats)
Pagination support
Processing status
All search operations
Unified search API
Timeline context
Semantic shortcuts
User settings
MCP toggle
Git branch switching
API Reference
Session Lifecycle (SessionRoutes)
Create/Get Session + Queue Observation
POST /api/sessions/observations
Response
POST /api/sessions/observations
Content-Type : application/json
{
"claudeSessionId" : "abc123" ,
"tool_name" : "Bash" ,
"tool_input" : { "command" : "ls" },
"tool_response" : { "stdout" : "..." },
"cwd" : "/path/to/project"
}
Privacy Check: Skips if the user prompt was entirely wrapped in <private> tags.
Tag Stripping: Removes <private> and <claude-mem-context> tags before storage.
Auto-Start: Ensures SDK agent generator is running to process the queue.
Queue Summary
POST /api/sessions/summarize
Response
POST /api/sessions/summarize
Content-Type : application/json
{
"claudeSessionId" : "abc123" ,
"last_user_message" : "User's message" ,
"last_assistant_message" : "Assistant's response"
}
Complete Session
POST /api/sessions/complete
Response
POST /api/sessions/complete
Content-Type : application/json
{
"claudeSessionId" : "abc123"
}
Effect: Stops SDK agent, marks session complete, broadcasts status change.
Legacy Endpoints (Still Supported)
Initialize Session
Queue Observations
Queue Summary
Complete Session
POST /sessions/:sessionDbId/init
Body : { userPrompt, promptNumber }
POST /sessions/:sessionDbId/observations
Body : { tool_name, tool_input, tool_response, prompt_number, cwd }
POST /sessions/:sessionDbId/summarize
Body : { last_user_message, last_assistant_message }
POST /sessions/:sessionDbId/complete
New integrations should use /api/sessions/* endpoints with claudeSessionId.
Data Retrieval (DataRoutes)
Get Paginated Data
Observations
Summaries
User Prompts
GET /api/observations?offset=0&limit=20&project=my-project
GET /api/summaries?offset=0&limit=20&project=my-project
GET /api/prompts?offset=0&limit=20&project=my-project
{
"items" : [ ... ],
"hasMore" : boolean ,
"offset" : number ,
"limit" : number
}
Get by ID
Observation
Session
Prompt
Get Database Stats
{
"worker" : {
"version" : "7.0.0" ,
"uptime" : 12345 ,
"activeSessions" : 2 ,
"sseClients" : 1 ,
"port" : 37777
},
"database" : {
"path" : "~/.claude-mem/claude-mem.db" ,
"size" : 1048576 ,
"observations" : 500 ,
"sessions" : 50 ,
"summaries" : 25
}
}
Get Projects List
{
"projects" : [ "claude-mem" , "other-project" ]
}
Get Processing Status
GET /api/processing-status
{
"isProcessing" : boolean ,
"queueDepth" : number
}
Search Operations (SearchRoutes)
Unified Search
GET /api/search?query=authentication&type=observations&format=index&limit=20
Search query text (optional, omit for filter-only)
"observations" | "sessions" | "prompts"
Filter by observation type: discovery, decision, bugfix, feature, refactor
Filter by concepts (comma-separated)
Filter by file paths (comma-separated)
ISO timestamp (filter start)
ISO timestamp (filter end)
{
"observations" : [ ... ],
"sessions" : [ ... ],
"prompts" : [ ... ]
}
Format Options:
index: Minimal fields for list display (id, title, preview)
full: Complete entity with all fields
Unified Timeline
GET /api/timeline?anchor=123&depth_before=10&depth_after=10&project=my-project
Anchor point (observation ID, "S123" for session, or ISO timestamp)
[
{ "type" : "observation" , "id" : 120 , "created_at_epoch" : 1700000000000 },
{ "type" : "session" , "id" : 5 , "created_at_epoch" : 1700000001000 },
{ "type" : "observation" , "id" : 123 , "created_at_epoch" : 1700000002000 }
]
Semantic Shortcuts
Decisions GET /api/decisions?format=index&limit=20
Changes GET /api/changes?format=index&limit=20
How It Works GET /api/how-it-works?format=index&limit=20
Search by Concept
GET /api/search/by-concept?concept=discovery&format=index&limit=10&project=my-project
Search by File Path
GET /api/search/by-file?filePath=src/services/worker-service.ts&format=index&limit=10
Search by Type
GET /api/search/by-type?type=bugfix&format=index&limit=10
Get Recent Context
GET /api/context/recent?project=my-project&limit=3
{
"summaries" : [ ... ],
"observations" : [ ... ]
}
Context Preview (for Settings UI)
GET /api/context/preview?project=my-project
Returns plain text with ANSI colors for terminal display.
Context Injection (for Hooks)
GET /api/context/inject?project=my-project&colors=true
Returns a pre-formatted context string ready for display or system prompt injection.
Settings & Configuration (SettingsRoutes)
Get/Update User Settings
GET
GET Response
POST
POST Response
MCP Server Status/Toggle
GET Status
GET Response
POST Toggle
POST Response
Git Branch Operations
Get Status
Switch Branch
Update Branch
{
"current" : "main" ,
"remote" : "origin/main" ,
"ahead" : 0 ,
"behind" : 0
}
POST /api/branch/switch
Body : { "branch": "feature/new-feature" }
{ "success" : true , "updated" : boolean }
Viewer & Real-Time Updates (ViewerRoutes)
Health Check
Viewer UI
Returns the HTML shell for the React viewer app.
SSE Stream
Server-Sent Events stream Event Types:
processing_status: { type, isProcessing, queueDepth }
session_started: { type, sessionDbId, project }
observation_queued: { type, sessionDbId }
summarize_queued: { type }
observation_created: { type, observation }
summary_created: { type, summary }
new_prompt: { type, id, claude_session_id, project, prompt_number, prompt_text, created_at_epoch }
Data Models
Active Session (In-Memory)
interface ActiveSession {
sessionDbId : number ; // Database ID (numeric)
claudeSessionId : string ; // Claude session identifier (string)
sdkSessionId : string | null ; // SDK session ID
project : string ; // Project name
userPrompt : string ; // Current user prompt text
pendingMessages : PendingMessage []; // Queue of pending operations
abortController : AbortController ; // For cancellation
generatorPromise : Promise < void > | null ; // SDK agent promise
lastPromptNumber : number ; // Last processed prompt number
startTime : number ; // Session start timestamp
cumulativeInputTokens : number ; // Total input tokens
cumulativeOutputTokens : number ; // Total output tokens
}
interface PendingMessage {
type : 'observation' | 'summarize' ;
tool_name ?: string ;
tool_input ?: any ;
tool_response ?: any ;
prompt_number ?: number ;
cwd ?: string ;
last_user_message ?: string ;
last_assistant_message ?: string ;
}
Database Entities
SDK Session
Observation
Session Summary
User Prompt
interface SDKSessionRow {
id : number ;
claude_session_id : string ;
sdk_session_id : string ;
project : string ;
user_prompt : string ;
created_at_epoch : number ;
completed_at_epoch ?: number ;
}
interface ObservationRow {
id : number ;
sdk_session_id : string ;
title : string ;
subtitle ?: string ;
summary : string ;
facts : string ; // JSON array of fact strings
concepts : string ; // JSON array of concept strings
files_touched : string ; // JSON array of file paths
obs_type : string ; // discovery, decision, bugfix, feature, refactor
project : string ;
created_at_epoch : number ;
prompt_number : number ;
}
interface SessionSummaryRow {
id : number ;
sdk_session_id : string ;
summary_text : string ;
facts : string ; // JSON array
concepts : string ; // JSON array
files_touched : string ; // JSON array
project : string ;
created_at_epoch : number ;
}
interface UserPromptRow {
id : number ;
claude_session_id : string ;
sdk_session_id : string ;
project : string ;
prompt_number : number ;
prompt_text : string ;
created_at_epoch : number ;
}
Search Results
interface ObservationSearchResult {
id : number ;
title : string ;
subtitle ?: string ;
summary : string ;
facts : string []; // Parsed from JSON
concepts : string []; // Parsed from JSON
files_touched : string []; // Parsed from JSON
obs_type : string ;
project : string ;
created_at_epoch : number ;
prompt_number : number ;
rank ?: number ; // FTS5 rank score
}
interface SessionSummarySearchResult {
id : number ;
summary_text : string ;
facts : string [];
concepts : string [];
files_touched : string [];
project : string ;
created_at_epoch : number ;
rank ?: number ;
}
interface UserPromptSearchResult {
id : number ;
claude_session_id : string ;
project : string ;
prompt_number : number ;
prompt_text : string ;
created_at_epoch : number ;
rank ?: number ;
}
Timeline Item
interface TimelineItem {
type : 'observation' | 'session' | 'prompt' ;
id : number ;
created_at_epoch : number ;
// Entity-specific fields based on type
}
Integration Patterns
Mapping Claude Code Hooks to Worker API
SessionStart Hook
Not needed for the new API — sessions are auto-created on the first observation.
UserPromptSubmit Hook
No API call needed — the user prompt is captured by the first observation in the prompt.
PostToolUse Hook
async function onPostToolUse ( context : HookContext ) {
const { session_id , tool_name , tool_input , tool_result , cwd } = context ;
const response = await fetch ( 'http://localhost:37777/api/sessions/observations' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
claudeSessionId: session_id ,
tool_name ,
tool_input ,
tool_response: tool_result ,
cwd
})
});
const result = await response . json ();
// result.status === 'queued' | 'skipped'
}
Summary Hook
async function onSummary ( context : HookContext ) {
const { session_id , last_user_message , last_assistant_message } = context ;
await fetch ( 'http://localhost:37777/api/sessions/summarize' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
claudeSessionId: session_id ,
last_user_message ,
last_assistant_message
})
});
}
SessionEnd Hook
async function onSessionEnd ( context : HookContext ) {
const { session_id } = context ;
await fetch ( 'http://localhost:37777/api/sessions/complete' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
claudeSessionId: session_id
})
});
}
VSCode Extension Integration
import * as vscode from 'vscode' ;
const searchTool = vscode . lm . registerTool ( 'claude-mem-search' , {
description: 'Search persistent memory for observations, sessions, and prompts' ,
inputSchema: {
type: 'object' ,
properties: {
query: { type: 'string' , description: 'Search query text' },
type: {
type: 'string' ,
enum: [ 'observations' , 'sessions' , 'prompts' ],
description: 'Type of results to return'
},
limit: { type: 'number' , description: 'Maximum number of results' , default: 10 }
},
required: [ 'query' ]
},
invoke : async ( options , token ) => {
const { query , type , limit = 10 } = options . input ;
try {
const response = await fetch (
`http://localhost:37777/api/search?query= ${ encodeURIComponent ( query ) } &format=index&limit= ${ limit } `
);
if ( ! response . ok ) {
throw new Error ( `Search failed: ${ response . statusText } ` );
}
const results = await response . json ();
return new vscode . LanguageModelToolResult ([
new vscode . LanguageModelTextPart ( JSON . stringify ( results , null , 2 ))
]);
} catch ( error ) {
return new vscode . LanguageModelToolResult ([
new vscode . LanguageModelTextPart ( `Error: ${ error . message } ` )
]);
}
}
});
Chat Participant Implementation
const participant = vscode . chat . createChatParticipant ( 'claude-mem' , async ( request , context , stream , token ) => {
const claudeSessionId = context . session . id ;
stream . markdown ( `Searching memory for: ${ request . prompt } \n\n ` );
const response = await fetch (
`http://localhost:37777/api/search?query= ${ encodeURIComponent ( request . prompt ) } &format=index&limit=5`
);
const results = await response . json ();
if ( results . observations ?. length > 0 ) {
stream . markdown ( '**Found observations:** \n ' );
for ( const obs of results . observations ) {
stream . markdown ( `- ${ obs . title } ( ${ obs . project } ) \n ` );
}
}
return { metadata: { command: 'search' } };
});
package.json (VSCode Extension)
{
"name" : "claude-mem-vscode" ,
"displayName" : "Claude-Mem" ,
"version" : "1.0.0" ,
"engines" : { "vscode" : "^1.95.0" },
"activationEvents" : [ "onStartupFinished" ],
"main" : "./dist/extension.js" ,
"contributes" : {
"chatParticipants" : [
{
"id" : "claude-mem" ,
"name" : "memory" ,
"description" : "Search your persistent memory"
}
],
"languageModelTools" : [
{
"name" : "claude-mem-search" ,
"displayName" : "Search Memory" ,
"description" : "Search persistent memory for observations, sessions, and prompts"
}
]
},
"scripts" : {
"build" : "node build.js" ,
"watch" : "node build.js --watch" ,
"package" : "vsce package"
},
"devDependencies" : {
"@types/vscode" : "^1.95.0" ,
"esbuild" : "^0.19.0" ,
"typescript" : "^5.3.0"
}
}
Error Handling & Resilience
Connection Failures
async function callWorkerWithFallback < T >(
endpoint : string ,
options ?: RequestInit
) : Promise < T | null > {
try {
const response = await fetch ( `http://localhost:37777 ${ endpoint } ` , {
... options ,
signal: AbortSignal . timeout ( 5000 ) // 5s timeout
});
if ( ! response . ok ) {
throw new Error ( `HTTP ${ response . status } : ${ response . statusText } ` );
}
return await response . json ();
} catch ( error ) {
console . error ( `Worker unavailable ( ${ endpoint } ):` , error );
return null ; // Graceful degradation
}
}
Retry Logic with Exponential Backoff
async function retryWithBackoff < T >(
fn : () => Promise < T >,
maxRetries = 3 ,
baseDelay = 100
) : Promise < T > {
for ( let attempt = 0 ; attempt < maxRetries ; attempt ++ ) {
try {
return await fn ();
} catch ( error ) {
if ( attempt === maxRetries - 1 ) throw error ;
const delay = baseDelay * Math . pow ( 2 , attempt );
await new Promise ( resolve => setTimeout ( resolve , delay ));
}
}
throw new Error ( 'Max retries exceeded' );
}
Worker Health Check
async function isWorkerHealthy () : Promise < boolean > {
try {
const response = await fetch ( 'http://localhost:37777/api/health' , {
signal: AbortSignal . timeout ( 2000 )
});
return response . ok ;
} catch {
return false ;
}
}
Privacy Tag Handling
The worker automatically strips privacy tags before storage:
<private>content</private> — User-level privacy control
<claude-mem-context>content</claude-mem-context> — System-level tag (prevents recursive storage)
Privacy Check: Observations/summaries are skipped if the entire user prompt was wrapped in <private> tags.
Custom Error Classes
class WorkerUnavailableError extends Error {
constructor () {
super ( 'Claude-mem worker is not running or unreachable' );
this . name = 'WorkerUnavailableError' ;
}
}
class WorkerTimeoutError extends Error {
constructor ( endpoint : string ) {
super ( `Worker request timed out: ${ endpoint } ` );
this . name = 'WorkerTimeoutError' ;
}
}
SSE Stream Error Handling
function connectToSSE ( onEvent : ( event : any ) => void ) {
const eventSource = new EventSource ( 'http://localhost:37777/stream' );
eventSource . onmessage = ( event ) => {
try {
const data = JSON . parse ( event . data );
onEvent ( data );
} catch ( error ) {
console . error ( 'SSE parse error:' , error );
}
};
eventSource . onerror = ( error ) => {
console . error ( 'SSE connection error:' , error );
eventSource . close ();
// Reconnect after 5 seconds
setTimeout (() => connectToSSE ( onEvent ), 5000 );
};
return eventSource ;
}
Development Workflow
Local Testing Loop
Terminal 2: Check worker status
npm run worker:status
npm run worker:logs
Terminal 3: Test API manually
curl http://localhost:37777/api/health
curl "http://localhost:37777/api/search?query=test&limit=5"
VSCode: Launch extension host
Press F5 to launch the extension host.
Complete WorkerClient Implementation
export class WorkerClient {
private baseUrl : string ;
constructor ( port : number = 37777 ) {
this . baseUrl = `http://localhost: ${ port } ` ;
}
async isHealthy () : Promise < boolean > {
try {
const response = await fetch ( ` ${ this . baseUrl } /api/health` , {
signal: AbortSignal . timeout ( 2000 )
});
return response . ok ;
} catch {
return false ;
}
}
async queueObservation ( data : {
claudeSessionId : string ;
tool_name : string ;
tool_input : any ;
tool_response : any ;
cwd ?: string ;
}) : Promise <{ status : string ; reason ?: string }> {
const response = await fetch ( ` ${ this . baseUrl } /api/sessions/observations` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( data ),
signal: AbortSignal . timeout ( 5000 )
});
if ( ! response . ok ) {
throw new Error ( `Failed to queue observation: ${ response . statusText } ` );
}
return await response . json ();
}
async queueSummarize ( data : {
claudeSessionId : string ;
last_user_message ?: string ;
last_assistant_message ?: string ;
}) : Promise <{ status : string ; reason ?: string }> {
const response = await fetch ( ` ${ this . baseUrl } /api/sessions/summarize` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( data ),
signal: AbortSignal . timeout ( 5000 )
});
if ( ! response . ok ) {
throw new Error ( `Failed to queue summary: ${ response . statusText } ` );
}
return await response . json ();
}
async completeSession ( claudeSessionId : string ) : Promise < void > {
const response = await fetch ( ` ${ this . baseUrl } /api/sessions/complete` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({ claudeSessionId }),
signal: AbortSignal . timeout ( 5000 )
});
if ( ! response . ok ) {
throw new Error ( `Failed to complete session: ${ response . statusText } ` );
}
}
async search ( params : {
query ?: string ;
type ?: 'observations' | 'sessions' | 'prompts' ;
format ?: 'index' | 'full' ;
limit ?: number ;
project ?: string ;
}) : Promise < any > {
const queryString = new URLSearchParams (
Object . entries ( params )
. filter (([ _ , v ]) => v !== undefined )
. map (([ k , v ]) => [ k , String ( v )])
). toString ();
const response = await fetch (
` ${ this . baseUrl } /api/search? ${ queryString } ` ,
{ signal: AbortSignal . timeout ( 10000 ) }
);
if ( ! response . ok ) {
throw new Error ( `Search failed: ${ response . statusText } ` );
}
return await response . json ();
}
connectSSE ( onEvent : ( event : any ) => void ) : EventSource {
const eventSource = new EventSource ( ` ${ this . baseUrl } /stream` );
eventSource . onmessage = ( event ) => {
try {
const data = JSON . parse ( event . data );
onEvent ( data );
} catch ( error ) {
console . error ( 'SSE parse error:' , error );
}
};
eventSource . onerror = ( error ) => {
console . error ( 'SSE connection error:' , error );
};
return eventSource ;
}
}
Testing Strategy
Manual Testing Checklist
Phase 1: Connection & Health
Phase 2: Session Lifecycle
Phase 3: Search & Retrieval
Phase 4: Real-Time Updates
Critical Implementation Notes
sessionDbId vs claudeSessionId Use claudeSessionId (string) for new API endpoints, not sessionDbId (number).
sessionDbId — Numeric database ID (legacy endpoints only)
claudeSessionId — String identifier from the Claude platform (new endpoints)
JSON String Fields Fields like facts, concepts, and files_touched are stored as JSON strings and require parsing: const observation = await client . getObservationById ( 123 );
const facts = JSON . parse ( observation . facts ); // string[]
const concepts = JSON . parse ( observation . concepts ); // string[]
Timestamps All created_at_epoch fields are in milliseconds , not seconds: const date = new Date ( observation . created_at_epoch ); // ✅ Correct
const date = new Date ( observation . created_at_epoch * 1000 ); // ❌ Wrong
Asynchronous Processing Workers process observations and summaries asynchronously. Results appear in the database 1–2 seconds after queuing. Use SSE events for real-time notifications.
Always wrap sensitive content in <private> tags to prevent storage: const userMessage = '<private>API key: sk-1234567890</private>' ;
// This observation will be skipped (entire prompt is private)
Additional Resources
Documentation Complete claude-mem documentation
GitHub Source code and issue tracker
Worker Service Worker architecture details
Database Schema Database structure and queries