@memoryrelay/mcp-server 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,33 @@
17
17
 
18
18
  ---
19
19
 
20
+ ## Why MemoryRelay?
21
+
22
+ | Feature | MemoryRelay | Mem0 | Basic MCP Memory |
23
+ |---------|------------|------|-----------------|
24
+ | Semantic Search | Yes | Yes | No |
25
+ | Work Sessions | Yes | No | No |
26
+ | Architectural Decisions (ADRs) | Yes | No | No |
27
+ | Reusable Patterns | Yes | No | No |
28
+ | Project Orchestration | Yes | No | No |
29
+ | Knowledge Graph (Entities) | Yes | Limited | No |
30
+ | Multi-Agent Support | Yes | Yes | No |
31
+ | V2 Async Storage (60-600x faster) | Yes | No | No |
32
+ | Tools | 44 | ~10 | 3-5 |
33
+
34
+ MemoryRelay is purpose-built for engineering teams managing complex, long-running projects — not general-purpose Q&A memory.
35
+
36
+ ---
37
+
38
+ ## Use Cases
39
+
40
+ - **Tech Lead** managing 3+ projects — track architectural decisions across repos, build pattern libraries, understand cross-project dependencies
41
+ - **DevOps Engineer** — record infrastructure decisions with rationale, store runbooks as reusable patterns, track deployment configurations
42
+ - **Solo Developer** — build a persistent knowledge base that grows across sessions, link related concepts through entities
43
+ - **Coding Agent** — auto-capture important context, adopt proven patterns, maintain session continuity across conversations
44
+
45
+ ---
46
+
20
47
  ## 📦 Installation
21
48
 
22
49
  ### Using npx (recommended)
@@ -141,6 +168,7 @@ Try asking:
141
168
  | `MEMORYRELAY_TIMEOUT` | No | `30000` | Request timeout in milliseconds |
142
169
  | `MEMORYRELAY_LOG_LEVEL` | No | `info` | Logging level (`debug`, `info`, `warn`, `error`) |
143
170
  | `MEMORYRELAY_TOOLS` | No | `all` | Comma-separated tool groups to enable (see below) |
171
+ | `OPENCLAW_AGENT_NAME` | No | - | Agent name when running under OpenClaw (used for agent ID detection) |
144
172
 
145
173
  ### Tool Groups
146
174
 
@@ -148,7 +176,8 @@ Control which tools are exposed via the `MEMORYRELAY_TOOLS` environment variable
148
176
 
149
177
  | Group | Tools | Description |
150
178
  |-------|-------|-------------|
151
- | `core` | 16 tools | Memory CRUD, entities, agents, health |
179
+ | `core` | 18 tools | Memory CRUD, entities, agents, health, aliases (forget/recall) |
180
+ | `v2` | 3 tools | Async memory storage, status polling, context building |
152
181
  | `sessions` | 4 tools | Session lifecycle (start, end, recall, list) |
153
182
  | `decisions` | 4 tools | Decision recording and checking |
154
183
  | `patterns` | 4 tools | Pattern library (create, search, adopt, suggest) |
@@ -172,7 +201,7 @@ The server automatically detects your agent ID from:
172
201
 
173
202
  ## 🛠️ Available Tools
174
203
 
175
- The MCP server provides 39 tools organized into groups:
204
+ The MCP server provides 44 tools organized into groups:
176
205
 
177
206
  ### Memory Management Tools
178
207
 
@@ -405,7 +434,7 @@ npm run type-check
405
434
  mcp-server/
406
435
  ├── src/
407
436
  │ ├── index.ts # Entry point with CLI routing
408
- │ ├── server.ts # MCP server implementation (39 tools)
437
+ │ ├── server.ts # MCP server implementation (44 tools)
409
438
  │ ├── client.ts # MemoryRelay API client
410
439
  │ ├── config.ts # Configuration loader + tool groups
411
440
  │ ├── logger.ts # Security-hardened logging
@@ -15,6 +15,8 @@ var TOOL_GROUPS = {
15
15
  "memory_get",
16
16
  "memory_update",
17
17
  "memory_delete",
18
+ "memory_forget",
19
+ "memory_recall",
18
20
  "entity_create",
19
21
  "entity_link",
20
22
  "entity_list",
@@ -26,6 +28,7 @@ var TOOL_GROUPS = {
26
28
  "agent_get",
27
29
  "memory_health"
28
30
  ],
31
+ v2: ["memory_store_async", "memory_status", "context_build"],
29
32
  sessions: ["session_start", "session_end", "session_recall", "session_list"],
30
33
  decisions: ["decision_record", "decision_list", "decision_supersede", "decision_check"],
31
34
  patterns: ["pattern_create", "pattern_search", "pattern_adopt", "pattern_suggest"],
@@ -240,10 +243,11 @@ var MemoryRelayClient = class {
240
243
  throw new Error(`Rate limited: 429 - Retry after ${waitMs}ms`);
241
244
  }
242
245
  const errorData = await response.json().catch(() => ({}));
243
- const errorMsg = `API request failed: ${response.status} ${response.statusText}` + (errorData.message ? ` - ${errorData.message}` : "");
246
+ const errorMsg = `API request failed: ${response.status} ${response.statusText}` + (errorData.detail ? ` - ${errorData.detail}` : "");
244
247
  throw new Error(maskApiKey(errorMsg, this.config.apiKey));
245
248
  }
246
- const data = await response.json();
249
+ const hasBody = response.status !== 204;
250
+ const data = hasBody ? await response.json() : null;
247
251
  this.logger.debug(`API response: ${method} ${path}`, { status: response.status });
248
252
  return data;
249
253
  } catch (error) {
@@ -270,7 +274,7 @@ var MemoryRelayClient = class {
270
274
  /**
271
275
  * Store a new memory
272
276
  */
273
- async storeMemory(content, metadata, deduplicate, dedupThreshold, project, importance, tier) {
277
+ async storeMemory(content, metadata, deduplicate, dedupThreshold, project, importance, tier, sessionId, autoExtractEntities) {
274
278
  this.validateContentSize(content);
275
279
  const body = {
276
280
  content,
@@ -292,6 +296,12 @@ var MemoryRelayClient = class {
292
296
  if (tier) {
293
297
  body.tier = tier;
294
298
  }
299
+ if (sessionId) {
300
+ body.session_id = sessionId;
301
+ }
302
+ if (autoExtractEntities !== void 0) {
303
+ body.auto_extract_entities = autoExtractEntities;
304
+ }
295
305
  return this.request("POST", "/v1/memories", body);
296
306
  }
297
307
  /**
@@ -304,7 +314,7 @@ var MemoryRelayClient = class {
304
314
  async searchMemories(query, limit = 10, threshold = 0.5, agentId, includeConfidential = false, includeArchived = false, compress = false, maxContextTokens, project, tier, minImportance) {
305
315
  this.validateContentSize(query);
306
316
  const effectiveAgentId = agentId === null ? void 0 : agentId ?? this.config.agentId;
307
- const body = { query, limit, threshold };
317
+ const body = { query, limit, min_score: threshold };
308
318
  if (effectiveAgentId) {
309
319
  body.agent_id = effectiveAgentId;
310
320
  }
@@ -765,6 +775,53 @@ var MemoryRelayClient = class {
765
775
  body
766
776
  );
767
777
  }
778
+ // ── V2 Async API (60-600x faster) ──
779
+ /**
780
+ * Store a memory asynchronously (V2 API).
781
+ * Returns immediately with 202 Accepted and a job ID.
782
+ * Use getMemoryStatus() to poll for completion.
783
+ */
784
+ async storeMemoryAsync(content, metadata, project, importance, tier, webhookUrl) {
785
+ this.validateContentSize(content);
786
+ const body = {
787
+ content,
788
+ agent_id: this.config.agentId
789
+ };
790
+ if (metadata) body.metadata = metadata;
791
+ if (project) body.project = project;
792
+ if (importance !== void 0) body.importance = importance;
793
+ if (tier) body.tier = tier;
794
+ if (webhookUrl) body.webhook_url = webhookUrl;
795
+ return this.request(
796
+ "POST",
797
+ "/v2/memories",
798
+ body
799
+ );
800
+ }
801
+ /**
802
+ * Get memory processing status (V2 API).
803
+ * Use after storeMemoryAsync() to check when embedding is ready.
804
+ */
805
+ async getMemoryStatus(memoryId) {
806
+ return this.request("GET", `/v2/memories/${memoryId}/status`);
807
+ }
808
+ /**
809
+ * Build a ranked context bundle from memories (V2 API).
810
+ * Supports optional AI summarization with custom LLM URL.
811
+ */
812
+ async buildContextV2(query, options) {
813
+ const body = { query };
814
+ if (options?.agentId !== void 0) body.agent_id = options.agentId;
815
+ if (options?.maxMemories) body.max_memories = options.maxMemories;
816
+ if (options?.maxTokens) body.max_tokens = options.maxTokens;
817
+ if (options?.aiEnhanced) body.ai_enhanced = true;
818
+ if (options?.rankingVersion) body.ranking_version = options.rankingVersion;
819
+ if (options?.searchMode) body.search_mode = options.searchMode;
820
+ if (options?.llmApiUrl) body.llm_api_url = options.llmApiUrl;
821
+ if (options?.llmModel) body.llm_model = options.llmModel;
822
+ if (options?.excludeMemoryIds) body.exclude_memory_ids = options.excludeMemoryIds;
823
+ return this.request("POST", "/v2/context/build", body);
824
+ }
768
825
  /**
769
826
  * Health check - verify API connectivity
770
827
  */
@@ -793,4 +850,4 @@ export {
793
850
  getLogger,
794
851
  MemoryRelayClient
795
852
  };
796
- //# sourceMappingURL=chunk-P6TZEH6O.js.map
853
+ //# sourceMappingURL=chunk-PFC5Z5ZD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/logger.ts","../src/client.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Configuration schema with security validation\n */\nexport const configSchema = z.object({\n apiKey: z.string()\n .startsWith('mem_', { message: 'API key must start with \"mem_\"' })\n .min(20, { message: 'API key appears to be invalid (too short)' }),\n apiUrl: z.string()\n .url({ message: 'API URL must be a valid URL' })\n .default('https://api.memoryrelay.net'),\n agentId: z.string()\n .optional()\n .describe('Agent identifier - auto-detected if not provided'),\n timeout: z.number()\n .positive({ message: 'Timeout must be positive' })\n .default(30000),\n logLevel: z.enum(['debug', 'info', 'warn', 'error'])\n .default('info'),\n});\n\nexport type Config = z.infer<typeof configSchema>;\n\n/**\n * Tool groups that can be enabled/disabled via MEMORYRELAY_TOOLS env var.\n * Default (when unset or 'all'): all groups enabled.\n */\nexport const TOOL_GROUPS: Record<string, string[]> = {\n core: [\n 'memory_store', 'memory_search', 'memory_list', 'memory_get',\n 'memory_update', 'memory_delete', 'memory_forget', 'memory_recall',\n 'entity_create', 'entity_link',\n 'entity_list', 'entity_graph', 'memory_batch_store', 'memory_context',\n 'agent_list', 'agent_create', 'agent_get', 'memory_health',\n ],\n v2: ['memory_store_async', 'memory_status', 'context_build'],\n sessions: ['session_start', 'session_end', 'session_recall', 'session_list'],\n decisions: ['decision_record', 'decision_list', 'decision_supersede', 'decision_check'],\n patterns: ['pattern_create', 'pattern_search', 'pattern_adopt', 'pattern_suggest'],\n projects: ['project_register', 'project_list', 'project_info'],\n relationships: [\n 'project_add_relationship', 'project_dependencies', 'project_dependents',\n 'project_related', 'project_impact', 'project_shared_patterns',\n ],\n context: ['project_context', 'memory_promote'],\n};\n\n/**\n * Parse MEMORYRELAY_TOOLS env var and return set of enabled tool names.\n * Returns null if all tools should be enabled (default behavior).\n */\nexport function getEnabledTools(): Set<string> | null {\n const raw = process.env.MEMORYRELAY_TOOLS;\n if (!raw || raw.trim().toLowerCase() === 'all') {\n return null; // all tools enabled\n }\n\n const groups = raw.split(',').map(g => g.trim().toLowerCase());\n const enabled = new Set<string>();\n for (const group of groups) {\n const tools = TOOL_GROUPS[group];\n if (tools) {\n for (const tool of tools) {\n enabled.add(tool);\n }\n }\n }\n return enabled;\n}\n\n/**\n * Load and validate configuration from environment variables\n */\nexport function loadConfig(): Config {\n try {\n const config = configSchema.parse({\n apiKey: process.env.MEMORYRELAY_API_KEY,\n apiUrl: process.env.MEMORYRELAY_API_URL,\n agentId: process.env.MEMORYRELAY_AGENT_ID,\n timeout: process.env.MEMORYRELAY_TIMEOUT \n ? parseInt(process.env.MEMORYRELAY_TIMEOUT, 10) \n : undefined,\n logLevel: process.env.MEMORYRELAY_LOG_LEVEL,\n });\n\n return config;\n } catch (error) {\n if (error instanceof z.ZodError) {\n const issues = error.issues.map(issue => \n ` - ${issue.path.join('.')}: ${issue.message}`\n ).join('\\n');\n \n throw new Error(\n `Configuration validation failed:\\n${issues}\\n\\n` +\n 'Please check your environment variables:\\n' +\n ' - MEMORYRELAY_API_KEY (required, starts with \"mem_\")\\n' +\n ' - MEMORYRELAY_API_URL (optional, default: https://api.memoryrelay.net)\\n' +\n ' - MEMORYRELAY_AGENT_ID (optional, auto-detected)\\n' +\n ' - MEMORYRELAY_TIMEOUT (optional, default: 30000)\\n' +\n ' - MEMORYRELAY_LOG_LEVEL (optional, default: info)',\n { cause: error }\n );\n }\n throw error;\n }\n}\n\n/**\n * Get or generate agent ID.\n *\n * Priority order:\n * 1. MEMORYRELAY_AGENT_ID (explicit configuration)\n * 2. OPENCLAW_AGENT_NAME (OpenClaw auto-detection)\n * 3. Auto-generated from username + hostname\n *\n * When no explicit ID is provided we build a human-readable identifier\n * from the current user and hostname so that memories are easier to\n * attribute in the dashboard (e.g. \"sparc-DESKTOP\" instead of \"agent-a1b2c3d4\").\n */\nexport function getAgentId(config: Config): string {\n if (config.agentId) {\n return config.agentId;\n }\n\n // OpenClaw auto-detection: use agent name from OpenClaw environment\n const openclawAgent = process.env.OPENCLAW_AGENT_NAME;\n if (openclawAgent) {\n return openclawAgent.slice(0, 32);\n }\n\n const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME || 'unknown';\n const user = process.env.USER || process.env.USERNAME || '';\n return user\n ? `${user}-${hostname}`.slice(0, 32)\n : `mcp-${hostname}`.slice(0, 32);\n}\n","/**\n * Security-hardened logger for MCP server\n * \n * - All output to stderr (stdout reserved for MCP protocol)\n * - Automatic masking of API keys (anything starting with \"mem_\")\n * - No internal paths in error messages\n */\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\nexport class Logger {\n private minLevel: number;\n\n constructor(level: LogLevel = 'info') {\n this.minLevel = LOG_LEVELS[level];\n }\n\n /**\n * Mask sensitive data in log messages\n * - API keys starting with \"mem_\" are masked\n * - Internal paths are sanitized\n */\n private sanitize(message: string): string {\n let sanitized = message;\n\n // Mask API keys (mem_xxx -> mem_****)\n sanitized = sanitized.replace(/mem_[a-zA-Z0-9_-]+/g, 'mem_****');\n\n // Remove internal paths (anything that looks like a file path)\n sanitized = sanitized.replace(/\\/[a-zA-Z0-9_\\-./]+\\.(ts|js|json)/g, '<file>');\n sanitized = sanitized.replace(/at\\s+[^\\s]+\\s+\\([^)]+\\)/g, 'at <location>');\n\n return sanitized;\n }\n\n /**\n * Format log message with timestamp and level\n */\n private format(level: LogLevel, message: string, data?: unknown): string {\n const timestamp = new Date().toISOString();\n const sanitizedMessage = this.sanitize(message);\n \n let output = `[${timestamp}] [${level.toUpperCase()}] ${sanitizedMessage}`;\n \n if (data !== undefined) {\n const sanitizedData = this.sanitize(JSON.stringify(data, null, 2));\n output += `\\n${sanitizedData}`;\n }\n \n return output;\n }\n\n debug(message: string, data?: unknown): void {\n if (this.minLevel <= LOG_LEVELS.debug) {\n console.error(this.format('debug', message, data));\n }\n }\n\n info(message: string, data?: unknown): void {\n if (this.minLevel <= LOG_LEVELS.info) {\n console.error(this.format('info', message, data));\n }\n }\n\n warn(message: string, data?: unknown): void {\n if (this.minLevel <= LOG_LEVELS.warn) {\n console.error(this.format('warn', message, data));\n }\n }\n\n error(message: string, data?: unknown): void {\n if (this.minLevel <= LOG_LEVELS.error) {\n console.error(this.format('error', message, data));\n }\n }\n}\n\n// Export singleton instance\nlet logger: Logger;\n\nexport function initLogger(level: LogLevel = 'info'): Logger {\n logger = new Logger(level);\n return logger;\n}\n\nexport function getLogger(): Logger {\n if (!logger) {\n logger = new Logger();\n }\n return logger;\n}\n","/**\n * MemoryRelay API client with retry logic and error handling\n */\n\nimport type {\n Memory,\n Entity,\n Agent,\n Session,\n SessionDetail,\n Decision,\n DecisionCheckResult,\n Project,\n Pattern,\n PatternSearchResult,\n SearchResult,\n ListResponse,\n ClientConfig,\n EntityType,\n BatchMemoryItem,\n BatchStoreResponse,\n} from './types.js';\nimport { getLogger } from './logger.js';\n\n// Retry configuration\nconst MAX_RETRIES = 3;\nconst INITIAL_DELAY_MS = 1000;\nconst MAX_CONTENT_SIZE = 50 * 1024; // 50KB\n\n/**\n * Exponential backoff retry wrapper\n */\nasync function withRetry<T>(\n fn: () => Promise<T>,\n retries: number = MAX_RETRIES\n): Promise<T> {\n let lastError: Error | undefined;\n \n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n \n // Don't retry on certain errors\n if (\n lastError.message.includes('401') ||\n lastError.message.includes('403') ||\n lastError.message.includes('404') ||\n lastError.message.includes('400')\n ) {\n throw lastError;\n }\n \n // On last attempt, throw the error\n if (attempt === retries) {\n throw lastError;\n }\n \n // Exponential backoff with jitter\n const delay = INITIAL_DELAY_MS * Math.pow(2, attempt);\n const jitter = Math.random() * 0.3 * delay;\n await new Promise(resolve => setTimeout(resolve, delay + jitter));\n }\n }\n \n throw lastError || new Error('Retry failed');\n}\n\n/**\n * Mask API key in error messages for security\n */\nfunction maskApiKey(message: string, apiKey: string): string {\n if (!apiKey) return message;\n const maskedKey = apiKey.substring(0, 8) + '***';\n return message.replace(new RegExp(apiKey, 'g'), maskedKey);\n}\n\nexport class MemoryRelayClient {\n private config: ClientConfig;\n private logger = getLogger();\n\n constructor(config: ClientConfig) {\n this.config = config;\n this.logger.info('MemoryRelay client initialized', {\n apiUrl: config.apiUrl,\n agentId: config.agentId,\n });\n }\n\n /**\n * Make authenticated HTTP request to MemoryRelay API with retry logic\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n return withRetry(async () => {\n const url = `${this.config.apiUrl}${path}`;\n \n this.logger.debug(`API request: ${method} ${path}`);\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.config.timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'User-Agent': '@memoryrelay/mcp-server',\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Handle rate limiting with retry\n if (response.status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : 5000;\n this.logger.warn(`Rate limited, waiting ${waitMs}ms`);\n await new Promise(resolve => setTimeout(resolve, waitMs));\n throw new Error(`Rate limited: 429 - Retry after ${waitMs}ms`);\n }\n\n const errorData = await response.json().catch(() => ({})) as { detail?: string };\n const errorMsg = `API request failed: ${response.status} ${response.statusText}` +\n (errorData.detail ? ` - ${errorData.detail}` : '');\n \n // Mask API key in error message\n throw new Error(maskApiKey(errorMsg, this.config.apiKey));\n }\n\n const hasBody = response.status !== 204;\n const data = hasBody ? await response.json() : null;\n this.logger.debug(`API response: ${method} ${path}`, { status: response.status });\n\n return data as T;\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n throw new Error(`Request timeout after ${this.config.timeout}ms`, { cause: error });\n }\n // Mask API key in all error messages\n error.message = maskApiKey(error.message, this.config.apiKey);\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n });\n }\n\n /**\n * Validate content size\n */\n private validateContentSize(content: string): void {\n if (content.length > MAX_CONTENT_SIZE) {\n throw new Error(`Content exceeds maximum size of ${MAX_CONTENT_SIZE} bytes`);\n }\n }\n\n /**\n * Store a new memory\n */\n async storeMemory(\n content: string,\n metadata?: Record<string, string>,\n deduplicate?: boolean,\n dedupThreshold?: number,\n project?: string,\n importance?: number,\n tier?: string,\n sessionId?: string,\n autoExtractEntities?: boolean\n ): Promise<Memory> {\n this.validateContentSize(content);\n\n const body: Record<string, unknown> = {\n content,\n metadata,\n agent_id: this.config.agentId,\n };\n if (deduplicate) {\n body.deduplicate = true;\n }\n if (dedupThreshold !== undefined) {\n body.dedup_threshold = dedupThreshold;\n }\n if (project) {\n body.project = project;\n }\n if (importance !== undefined) {\n body.importance = importance;\n }\n if (tier) {\n body.tier = tier;\n }\n if (sessionId) {\n body.session_id = sessionId;\n }\n if (autoExtractEntities !== undefined) {\n body.auto_extract_entities = autoExtractEntities;\n }\n\n return this.request<Memory>('POST', '/v1/memories', body);\n }\n\n /**\n * Search memories using semantic search\n * @param agentId - Optional agent ID override. If omitted, uses config agentId. Pass null for cross-agent search.\n * @param includeConfidential - Include confidential memories in results\n * @param includeArchived - Include archived memories in results\n * @param project - Optional project slug to filter by\n */\n async searchMemories(\n query: string,\n limit: number = 10,\n threshold: number = 0.5,\n agentId?: string | null,\n includeConfidential: boolean = false,\n includeArchived: boolean = false,\n compress: boolean = false,\n maxContextTokens?: number,\n project?: string,\n tier?: string,\n minImportance?: number\n ): Promise<SearchResult[]> {\n this.validateContentSize(query);\n\n // If agentId is explicitly null, omit it (cross-agent search).\n // If agentId is undefined, use the default from config.\n const effectiveAgentId = agentId === null ? undefined : (agentId ?? this.config.agentId);\n\n const body: Record<string, unknown> = { query, limit, min_score: threshold };\n if (effectiveAgentId) {\n body.agent_id = effectiveAgentId;\n }\n if (includeConfidential) {\n body.include_confidential = true;\n }\n if (includeArchived) {\n body.include_archived = true;\n }\n if (compress) {\n body.compress = true;\n }\n if (maxContextTokens !== undefined) {\n body.max_context_tokens = maxContextTokens;\n }\n if (project) {\n body.project = project;\n }\n if (tier) {\n body.tier = tier;\n }\n if (minImportance !== undefined) {\n body.min_importance = minImportance;\n }\n\n const response = await this.request<{ data: SearchResult[] }>(\n 'POST',\n '/v1/memories/search',\n body\n );\n return response.data;\n }\n\n /**\n * List recent memories with pagination\n */\n async listMemories(limit: number = 20, offset: number = 0): Promise<ListResponse<Memory>> {\n return this.request<ListResponse<Memory>>(\n 'GET',\n `/v1/memories?limit=${limit}&offset=${offset}`\n );\n }\n\n /**\n * Get a specific memory by ID\n */\n async getMemory(id: string): Promise<Memory> {\n return this.request<Memory>('GET', `/v1/memories/${id}`);\n }\n\n /**\n * Update an existing memory\n */\n async updateMemory(\n id: string,\n content: string,\n metadata?: Record<string, string>\n ): Promise<Memory> {\n this.validateContentSize(content);\n \n return this.request<Memory>('PATCH', `/v1/memories/${id}`, {\n content,\n metadata,\n });\n }\n\n /**\n * Delete a memory\n */\n async deleteMemory(id: string): Promise<void> {\n await this.request<void>('DELETE', `/v1/memories/${id}`);\n }\n\n /**\n * Create a named entity\n */\n async createEntity(\n name: string,\n type: EntityType,\n metadata?: Record<string, string>\n ): Promise<Entity> {\n this.validateContentSize(name);\n \n return this.request<Entity>('POST', '/v1/entities', {\n name,\n type,\n metadata,\n });\n }\n\n /**\n * Link an entity to a memory\n */\n async linkEntity(\n entityId: string,\n memoryId: string,\n relationship: string = 'mentioned_in'\n ): Promise<void> {\n await this.request<void>('POST', '/v1/entities/links', {\n entity_id: entityId,\n memory_id: memoryId,\n relationship,\n });\n }\n\n /**\n * Get an entity by ID\n */\n async getEntity(id: string): Promise<Entity> {\n return this.request<Entity>('GET', `/v1/entities/${id}`);\n }\n\n /**\n * List entities with pagination\n */\n async listEntities(limit: number = 20, offset: number = 0): Promise<ListResponse<Entity>> {\n return this.request<ListResponse<Entity>>(\n 'GET',\n `/v1/entities?limit=${limit}&offset=${offset}`\n );\n }\n\n /**\n * Get entity neighborhood (ego-centric subgraph).\n * Returns the entity's 1-hop or 2-hop neighbors and relationships.\n */\n async getEntityNeighborhood(\n entityId: string,\n depth: number = 1,\n maxNeighbors: number = 50\n ): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n 'GET',\n `/v1/entities/${entityId}/neighborhood?depth=${depth}&max_neighbors=${maxNeighbors}`\n );\n }\n\n /**\n * Delete an entity\n */\n async deleteEntity(id: string): Promise<void> {\n await this.request<void>('DELETE', `/v1/entities/${id}`);\n }\n\n /**\n * List agents with pagination\n */\n async listAgents(limit: number = 20, offset: number = 0): Promise<ListResponse<Agent>> {\n return this.request<ListResponse<Agent>>(\n 'GET',\n `/v1/agents?limit=${limit}&offset=${offset}`\n );\n }\n\n /**\n * Create a new agent\n */\n async createAgent(\n name: string,\n description?: string,\n metadata?: Record<string, string>\n ): Promise<Agent> {\n const body: Record<string, unknown> = { name };\n if (description) body.description = description;\n if (metadata) body.metadata = metadata;\n\n return this.request<Agent>('POST', '/v1/agents', body);\n }\n\n /**\n * Get an agent by ID\n */\n async getAgent(id: string): Promise<Agent> {\n return this.request<Agent>('GET', `/v1/agents/${id}`);\n }\n\n /**\n * Batch store multiple memories in a single API call.\n * Uses the /v1/memories/batch endpoint.\n */\n async batchStoreMemories(\n items: BatchMemoryItem[]\n ): Promise<BatchStoreResponse> {\n if (items.length === 0) {\n return { success: true, total: 0, succeeded: 0, failed: 0, skipped: 0, results: [] };\n }\n if (items.length > 100) {\n throw new Error('Batch size exceeds maximum of 100 memories');\n }\n\n // Attach default agent_id to items that don't have one\n const memories = items.map(item => ({\n content: item.content,\n metadata: item.metadata || {},\n agent_id: item.agent_id || this.config.agentId,\n }));\n\n return this.request<BatchStoreResponse>('POST', '/v1/memories/batch', { memories });\n }\n\n /**\n * Build a context string from search results.\n * Searches for relevant memories, formats them, and returns\n * a single string ready for prompt injection.\n */\n async buildContext(\n query: string,\n limit: number = 10,\n threshold: number = 0.5,\n maxTokens?: number\n ): Promise<{ context: string; memories_used: number; total_chars: number }> {\n const results = await this.searchMemories(\n query,\n limit,\n threshold,\n undefined, // use default agent\n false, // no confidential\n false, // no archived\n !!maxTokens, // compress if token budget provided\n maxTokens\n );\n\n if (results.length === 0) {\n return { context: '', memories_used: 0, total_chars: 0 };\n }\n\n const lines: string[] = [];\n for (const result of results) {\n const score = (result.score * 100).toFixed(0);\n lines.push(`[${score}%] ${result.memory.content}`);\n }\n\n const context = lines.join('\\n\\n');\n\n // Rough token budget enforcement (1 token ≈ 4 chars)\n let finalContext = context;\n if (maxTokens) {\n const charBudget = maxTokens * 4;\n if (finalContext.length > charBudget) {\n finalContext = finalContext.slice(0, charBudget) + '\\n\\n[...truncated]';\n }\n }\n\n return {\n context: finalContext,\n memories_used: results.length,\n total_chars: finalContext.length,\n };\n }\n\n /**\n * Start a new session\n */\n async startSession(\n title?: string,\n project?: string,\n metadata?: Record<string, string>\n ): Promise<Session> {\n const body: Record<string, unknown> = {};\n if (this.config.agentId) body.agent_id = this.config.agentId;\n if (title) body.title = title;\n if (project) body.project = project;\n if (metadata) body.metadata = metadata;\n return this.request<Session>('POST', '/v1/sessions', body);\n }\n\n /**\n * End an active session\n */\n async endSession(\n sessionId: string,\n summary?: string,\n ): Promise<Session> {\n const body: Record<string, unknown> = {};\n if (summary) body.summary = summary;\n return this.request<Session>('PUT', `/v1/sessions/${sessionId}/end`, body);\n }\n\n /**\n * Get a session by ID with its memories\n */\n async getSession(sessionId: string): Promise<SessionDetail> {\n return this.request<SessionDetail>(\n 'GET',\n `/v1/sessions/${sessionId}?include_memories=true`\n );\n }\n\n /**\n * List sessions with optional filters\n */\n async listSessions(\n limit: number = 20,\n agentId?: string,\n project?: string,\n status?: string,\n ): Promise<ListResponse<Session>> {\n const params = new URLSearchParams();\n params.set('limit', String(limit));\n const effectiveAgentId = agentId ?? this.config.agentId;\n if (effectiveAgentId) params.set('agent_id', effectiveAgentId);\n if (project) params.set('project', project);\n if (status) params.set('status', status);\n return this.request<ListResponse<Session>>(\n 'GET',\n `/v1/sessions?${params.toString()}`\n );\n }\n\n /**\n * Record a new decision\n */\n async recordDecision(\n title: string,\n rationale: string,\n alternatives?: string,\n project?: string,\n tags?: string[],\n status?: string,\n metadata?: Record<string, string>\n ): Promise<Decision> {\n const body: Record<string, unknown> = { title, rationale };\n if (this.config.agentId) body.agent_id = this.config.agentId;\n if (alternatives) body.alternatives = alternatives;\n if (project) body.project_slug = project;\n if (tags) body.tags = tags;\n if (status) body.status = status;\n if (metadata) body.metadata = metadata;\n return this.request<Decision>('POST', '/v1/decisions', body);\n }\n\n /**\n * List decisions with optional filters\n */\n async listDecisions(\n limit?: number,\n project?: string,\n status?: string,\n tags?: string,\n ): Promise<ListResponse<Decision>> {\n const params = new URLSearchParams();\n if (limit) params.set('limit', String(limit));\n if (project) params.set('project', project);\n if (status) params.set('status', status);\n if (tags) params.set('tags', tags);\n return this.request<ListResponse<Decision>>(\n 'GET',\n `/v1/decisions?${params.toString()}`\n );\n }\n\n /**\n * Supersede a decision with a new one\n */\n async supersedeDecision(\n decisionId: string,\n title: string,\n rationale: string,\n alternatives?: string,\n tags?: string[],\n ): Promise<Decision> {\n const body: Record<string, unknown> = { title, rationale };\n if (alternatives) body.alternatives = alternatives;\n if (tags) body.tags = tags;\n return this.request<Decision>(\n 'POST',\n `/v1/decisions/${decisionId}/supersede`,\n body\n );\n }\n\n /**\n * Check for existing decisions about a topic (semantic search)\n */\n async checkDecisions(\n query: string,\n project?: string,\n limit?: number,\n threshold?: number,\n includeSuperseded?: boolean,\n ): Promise<{ data: DecisionCheckResult[]; query: string; total: number }> {\n const params = new URLSearchParams();\n params.set('query', query);\n if (limit) params.set('limit', String(limit));\n if (threshold !== undefined) params.set('threshold', String(threshold));\n if (project) params.set('project', project);\n if (includeSuperseded) params.set('include_superseded', 'true');\n return this.request<{ data: DecisionCheckResult[]; query: string; total: number }>(\n 'GET',\n `/v1/decisions/check?${params.toString()}`\n );\n }\n\n /**\n * Register a new project\n */\n async createProject(\n slug: string,\n name: string,\n description?: string,\n stack?: Record<string, unknown>,\n repo_url?: string,\n metadata?: Record<string, unknown>\n ): Promise<Project> {\n const body: Record<string, unknown> = { slug, name };\n if (description) body.description = description;\n if (stack) body.stack = stack;\n if (repo_url) body.repo_url = repo_url;\n if (metadata) body.metadata = metadata;\n return this.request<Project>('POST', '/v1/projects', body);\n }\n\n /**\n * List projects with optional pagination\n */\n async listProjects(\n limit: number = 20,\n cursor?: string\n ): Promise<ListResponse<Project>> {\n const params = new URLSearchParams();\n params.set('limit', String(limit));\n if (cursor) params.set('cursor', cursor);\n return this.request<ListResponse<Project>>(\n 'GET',\n `/v1/projects?${params.toString()}`\n );\n }\n\n /**\n * Get a project by slug\n */\n async getProject(slug: string): Promise<Project> {\n return this.request<Project>('GET', `/v1/projects/${slug}`);\n }\n\n /**\n * Create a reusable pattern\n */\n async createPattern(\n title: string,\n description: string,\n category?: string,\n example_code?: string,\n scope?: string,\n tags?: string[],\n source_project?: string,\n metadata?: Record<string, unknown>\n ): Promise<Pattern> {\n const body: Record<string, unknown> = { title, description };\n if (category) body.category = category;\n if (example_code) body.example_code = example_code;\n if (scope) body.scope = scope;\n if (tags) body.tags = tags;\n if (source_project) body.source_project = source_project;\n if (metadata) body.metadata = metadata;\n return this.request<Pattern>('POST', '/v1/patterns', body);\n }\n\n /**\n * Search patterns using semantic search\n */\n async searchPatterns(\n query: string,\n category?: string,\n project?: string,\n limit?: number,\n threshold?: number,\n ): Promise<{ data: PatternSearchResult[]; query: string; total: number }> {\n const params = new URLSearchParams();\n params.set('query', query);\n if (category) params.set('category', category);\n if (project) params.set('project', project);\n if (limit) params.set('limit', String(limit));\n if (threshold !== undefined) params.set('threshold', String(threshold));\n return this.request<{ data: PatternSearchResult[]; query: string; total: number }>(\n 'GET',\n `/v1/patterns/search?${params.toString()}`\n );\n }\n\n /**\n * Adopt a pattern for a project\n */\n async adoptPattern(\n patternId: string,\n project: string,\n ): Promise<Pattern> {\n return this.request<Pattern>(\n 'POST',\n `/v1/patterns/${patternId}/adopt`,\n { project }\n );\n }\n\n /**\n * Suggest patterns for a project\n */\n async suggestPatterns(\n project: string,\n limit?: number,\n ): Promise<{ data: Pattern[]; project: string; total: number }> {\n const params = new URLSearchParams();\n params.set('project', project);\n if (limit) params.set('limit', String(limit));\n return this.request<{ data: Pattern[]; project: string; total: number }>(\n 'GET',\n `/v1/patterns/suggest?${params.toString()}`\n );\n }\n\n // ── Project Relationships (Issue #186) ──\n\n /**\n * Add a relationship between two projects\n */\n async addProjectRelationship(\n sourceSlug: string,\n targetProject: string,\n relationshipType: string,\n metadata?: Record<string, unknown>,\n ): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = {\n target_project: targetProject,\n relationship_type: relationshipType,\n };\n if (metadata) body.metadata = metadata;\n return this.request<Record<string, unknown>>(\n 'POST',\n `/v1/projects/${sourceSlug}/relationships`,\n body,\n );\n }\n\n /**\n * Get what this project depends on\n */\n async getProjectDependencies(slug: string): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n 'GET',\n `/v1/projects/${slug}/dependencies`,\n );\n }\n\n /**\n * Get what depends on this project\n */\n async getProjectDependents(slug: string): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n 'GET',\n `/v1/projects/${slug}/dependents`,\n );\n }\n\n /**\n * Get all related projects\n */\n async getProjectRelated(slug: string): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n 'GET',\n `/v1/projects/${slug}/related`,\n );\n }\n\n /**\n * Run impact analysis for a project change\n */\n async projectImpactAnalysis(\n project: string,\n changeDescription: string,\n ): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n 'POST',\n '/v1/projects/impact-analysis',\n { project, change_description: changeDescription },\n );\n }\n\n /**\n * Find patterns shared between two projects\n */\n async getSharedPatterns(\n slugA: string,\n slugB: string,\n ): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n 'GET',\n `/v1/projects/shared-patterns?a=${encodeURIComponent(slugA)}&b=${encodeURIComponent(slugB)}`,\n );\n }\n\n /**\n * Get full project context (hot memories, decisions, patterns, formatted text)\n */\n async getProjectContext(slug: string): Promise<Record<string, unknown>> {\n return this.request<Record<string, unknown>>(\n 'GET',\n `/v1/projects/${encodeURIComponent(slug)}/context`,\n );\n }\n\n /**\n * Promote/demote a memory by updating its importance and tier\n */\n async promoteMemory(\n memoryId: string,\n importance: number,\n tier?: string\n ): Promise<Memory> {\n const body: Record<string, unknown> = { importance };\n if (tier) {\n body.tier = tier;\n }\n return this.request<Memory>(\n 'PUT',\n `/v1/memories/${encodeURIComponent(memoryId)}/importance`,\n body,\n );\n }\n\n // ── V2 Async API (60-600x faster) ──\n\n /**\n * Store a memory asynchronously (V2 API).\n * Returns immediately with 202 Accepted and a job ID.\n * Use getMemoryStatus() to poll for completion.\n */\n async storeMemoryAsync(\n content: string,\n metadata?: Record<string, string>,\n project?: string,\n importance?: number,\n tier?: string,\n webhookUrl?: string\n ): Promise<{ id: string; status: string; job_id: string; estimated_completion_seconds: number }> {\n this.validateContentSize(content);\n\n const body: Record<string, unknown> = {\n content,\n agent_id: this.config.agentId,\n };\n if (metadata) body.metadata = metadata;\n if (project) body.project = project;\n if (importance !== undefined) body.importance = importance;\n if (tier) body.tier = tier;\n if (webhookUrl) body.webhook_url = webhookUrl;\n\n return this.request<{ id: string; status: string; job_id: string; estimated_completion_seconds: number }>(\n 'POST',\n '/v2/memories',\n body\n );\n }\n\n /**\n * Get memory processing status (V2 API).\n * Use after storeMemoryAsync() to check when embedding is ready.\n */\n async getMemoryStatus(memoryId: string): Promise<{\n id: string;\n status: 'pending' | 'processing' | 'ready' | 'failed';\n created_at: string;\n updated_at: string;\n error?: string;\n }> {\n return this.request<{\n id: string;\n status: 'pending' | 'processing' | 'ready' | 'failed';\n created_at: string;\n updated_at: string;\n error?: string;\n }>('GET', `/v2/memories/${memoryId}/status`);\n }\n\n /**\n * Build a ranked context bundle from memories (V2 API).\n * Supports optional AI summarization with custom LLM URL.\n */\n async buildContextV2(\n query: string,\n options?: {\n agentId?: string | null;\n maxMemories?: number;\n maxTokens?: number;\n aiEnhanced?: boolean;\n rankingVersion?: string;\n searchMode?: 'semantic' | 'hybrid' | 'keyword';\n llmApiUrl?: string;\n llmModel?: string;\n excludeMemoryIds?: string[];\n }\n ): Promise<{\n context: Array<{\n memory_id: string;\n content: string;\n score: number;\n memory_type?: string;\n }>;\n summary?: string;\n token_count: number;\n ranking_version: string;\n ai_enhanced: boolean;\n latency_ms: number;\n }> {\n const body: Record<string, unknown> = { query };\n if (options?.agentId !== undefined) body.agent_id = options.agentId;\n if (options?.maxMemories) body.max_memories = options.maxMemories;\n if (options?.maxTokens) body.max_tokens = options.maxTokens;\n if (options?.aiEnhanced) body.ai_enhanced = true;\n if (options?.rankingVersion) body.ranking_version = options.rankingVersion;\n if (options?.searchMode) body.search_mode = options.searchMode;\n if (options?.llmApiUrl) body.llm_api_url = options.llmApiUrl;\n if (options?.llmModel) body.llm_model = options.llmModel;\n if (options?.excludeMemoryIds) body.exclude_memory_ids = options.excludeMemoryIds;\n\n return this.request<{\n context: Array<{\n memory_id: string;\n content: string;\n score: number;\n memory_type?: string;\n }>;\n summary?: string;\n token_count: number;\n ranking_version: string;\n ai_enhanced: boolean;\n latency_ms: number;\n }>('POST', '/v2/context/build', body);\n }\n\n /**\n * Health check - verify API connectivity\n */\n async healthCheck(): Promise<{ status: string; message: string }> {\n try {\n // Simple GET request to check API is reachable\n await this.request<{ status: string }>('GET', '/v1/health');\n return {\n status: 'healthy',\n message: 'API connection successful',\n };\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : 'Unknown error';\n return {\n status: 'unhealthy',\n message: `API connection failed: ${errorMsg}`,\n };\n }\n }\n}\n"],"mappings":"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","names":[]}
package/dist/cli/test.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  MemoryRelayClient,
3
3
  getAgentId,
4
4
  loadConfig
5
- } from "../chunk-P6TZEH6O.js";
5
+ } from "../chunk-PFC5Z5ZD.js";
6
6
 
7
7
  // src/cli/test.ts
8
8
  function stderr(msg) {
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  getLogger,
7
7
  initLogger,
8
8
  loadConfig
9
- } from "./chunk-P6TZEH6O.js";
9
+ } from "./chunk-PFC5Z5ZD.js";
10
10
 
11
11
  // src/server.ts
12
12
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -38,7 +38,7 @@ var MemoryRelayMCPServer = class {
38
38
  this.server = new Server(
39
39
  {
40
40
  name: "@memoryrelay/mcp-server",
41
- version: "0.3.0"
41
+ version: "0.4.1"
42
42
  },
43
43
  {
44
44
  capabilities: {
@@ -114,6 +114,15 @@ var MemoryRelayMCPServer = class {
114
114
  type: "string",
115
115
  description: 'Memory tier override: "hot" (always in context), "warm" (default), "cold" (archived). Auto-computed from importance if omitted.',
116
116
  enum: ["hot", "warm", "cold"]
117
+ },
118
+ session_id: {
119
+ type: "string",
120
+ description: "Session ID to associate the memory with. Links the memory to an active session."
121
+ },
122
+ auto_extract_entities: {
123
+ type: "boolean",
124
+ description: "Automatically extract entities (people, places, orgs, etc.) from the memory content.",
125
+ default: false
117
126
  }
118
127
  },
119
128
  required: ["content"]
@@ -1022,6 +1031,165 @@ var MemoryRelayMCPServer = class {
1022
1031
  },
1023
1032
  required: ["memory_id", "importance"]
1024
1033
  }
1034
+ },
1035
+ // ── V2 Async API Tools (60-600x faster) ──
1036
+ {
1037
+ name: "memory_store_async",
1038
+ description: "Store a memory asynchronously using V2 API. Returns immediately (<50ms) with 202 Accepted and a job ID. Background workers generate the embedding. Use memory_status to poll for completion. Prefer this over memory_store for high-throughput or latency-sensitive applications.",
1039
+ inputSchema: {
1040
+ type: "object",
1041
+ properties: {
1042
+ content: {
1043
+ type: "string",
1044
+ description: "The memory content to store. Be specific and include relevant context."
1045
+ },
1046
+ metadata: {
1047
+ type: "object",
1048
+ description: "Optional key-value metadata to attach to the memory",
1049
+ additionalProperties: { type: "string" }
1050
+ },
1051
+ project: {
1052
+ type: "string",
1053
+ description: "Project slug to associate the memory with",
1054
+ maxLength: 100
1055
+ },
1056
+ importance: {
1057
+ type: "number",
1058
+ description: "Memory importance (0.0-1.0). Values >= 0.8 promote to hot tier.",
1059
+ minimum: 0,
1060
+ maximum: 1
1061
+ },
1062
+ tier: {
1063
+ type: "string",
1064
+ description: 'Memory tier override: "hot", "warm", or "cold"',
1065
+ enum: ["hot", "warm", "cold"]
1066
+ },
1067
+ webhook_url: {
1068
+ type: "string",
1069
+ description: "Optional webhook URL to receive completion notification"
1070
+ }
1071
+ },
1072
+ required: ["content"]
1073
+ }
1074
+ },
1075
+ {
1076
+ name: "memory_status",
1077
+ description: "Check the processing status of a memory created via memory_store_async. Poll this endpoint to determine when embedding generation is complete. Status values: pending (waiting for worker), processing (generating embedding), ready (searchable), failed (error occurred).",
1078
+ inputSchema: {
1079
+ type: "object",
1080
+ properties: {
1081
+ memory_id: {
1082
+ type: "string",
1083
+ description: "Memory ID (UUID) to check status for"
1084
+ }
1085
+ },
1086
+ required: ["memory_id"]
1087
+ }
1088
+ },
1089
+ {
1090
+ name: "context_build",
1091
+ description: "Build a ranked context bundle from memories using V2 API with optional AI summarization. Searches for relevant memories, ranks them by composite score, and optionally generates an AI summary. Useful for building token-efficient context windows for LLM prompts. Supports custom LLM endpoints (Ollama, llama.cpp, vLLM, etc.).",
1092
+ inputSchema: {
1093
+ type: "object",
1094
+ properties: {
1095
+ query: {
1096
+ type: "string",
1097
+ description: "Context query describing what information is needed"
1098
+ },
1099
+ max_memories: {
1100
+ type: "number",
1101
+ description: "Maximum number of memories to include (1-100, default 20)",
1102
+ minimum: 1,
1103
+ maximum: 100,
1104
+ default: 20
1105
+ },
1106
+ max_tokens: {
1107
+ type: "number",
1108
+ description: "Token budget for context window (enforces truncation)",
1109
+ minimum: 100,
1110
+ maximum: 128e3
1111
+ },
1112
+ ai_enhanced: {
1113
+ type: "boolean",
1114
+ description: "Enable AI summarization of context (uses LLM)",
1115
+ default: false
1116
+ },
1117
+ search_mode: {
1118
+ type: "string",
1119
+ description: 'Search mode: "semantic", "hybrid", or "keyword"',
1120
+ enum: ["semantic", "hybrid", "keyword"],
1121
+ default: "hybrid"
1122
+ },
1123
+ llm_api_url: {
1124
+ type: "string",
1125
+ description: "Custom LLM API URL for summarization (OpenAI-compatible). Use for local LLMs: Ollama (http://localhost:11434/v1), llama.cpp, vLLM, LM Studio, etc."
1126
+ },
1127
+ llm_model: {
1128
+ type: "string",
1129
+ description: 'Model name for custom LLM (e.g., "mistral", "llama3", "gemma")'
1130
+ },
1131
+ exclude_memory_ids: {
1132
+ type: "array",
1133
+ items: { type: "string" },
1134
+ description: "Memory IDs to exclude (for multi-turn context building)"
1135
+ }
1136
+ },
1137
+ required: ["query"]
1138
+ }
1139
+ },
1140
+ // ── Tool Name Aliases (backward compatibility with OpenClaw plugin) ──
1141
+ {
1142
+ name: "memory_forget",
1143
+ description: "Delete a memory by ID, or search by query to find candidates. Alias for memory_delete. Provide memoryId for direct deletion, or query to search first.",
1144
+ inputSchema: {
1145
+ type: "object",
1146
+ properties: {
1147
+ memoryId: {
1148
+ type: "string",
1149
+ description: "Memory ID to delete"
1150
+ },
1151
+ query: {
1152
+ type: "string",
1153
+ description: "Search query to find memory"
1154
+ }
1155
+ }
1156
+ }
1157
+ },
1158
+ {
1159
+ name: "memory_recall",
1160
+ description: "Search memories using natural language. Alias for memory_search with default project scoping. Returns the most relevant memories based on semantic similarity.",
1161
+ inputSchema: {
1162
+ type: "object",
1163
+ properties: {
1164
+ query: {
1165
+ type: "string",
1166
+ description: "Natural language search query"
1167
+ },
1168
+ limit: {
1169
+ type: "number",
1170
+ description: "Maximum results (1-50). Default 5.",
1171
+ minimum: 1,
1172
+ maximum: 50,
1173
+ default: 5
1174
+ },
1175
+ threshold: {
1176
+ type: "number",
1177
+ description: "Minimum similarity threshold (0-1). Default 0.3.",
1178
+ minimum: 0,
1179
+ maximum: 1,
1180
+ default: 0.3
1181
+ },
1182
+ project: {
1183
+ type: "string",
1184
+ description: "Filter by project slug"
1185
+ },
1186
+ max_tokens: {
1187
+ type: "number",
1188
+ description: "Token budget for context window"
1189
+ }
1190
+ },
1191
+ required: ["query"]
1192
+ }
1025
1193
  }
1026
1194
  ];
1027
1195
  return {
@@ -1041,7 +1209,9 @@ var MemoryRelayMCPServer = class {
1041
1209
  args.dedup_threshold,
1042
1210
  args.project,
1043
1211
  args.importance,
1044
- args.tier
1212
+ args.tier,
1213
+ args.session_id,
1214
+ args.auto_extract_entities
1045
1215
  );
1046
1216
  return {
1047
1217
  content: [
@@ -1626,6 +1796,87 @@ var MemoryRelayMCPServer = class {
1626
1796
  content: [{ type: "text", text: JSON.stringify(updated, null, 2) }]
1627
1797
  };
1628
1798
  }
1799
+ // ── V2 Async API Tool Handlers ──
1800
+ case "memory_store_async": {
1801
+ const response = await this.client.storeMemoryAsync(
1802
+ args.content,
1803
+ args.metadata,
1804
+ args.project,
1805
+ args.importance,
1806
+ args.tier,
1807
+ args.webhook_url
1808
+ );
1809
+ return {
1810
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
1811
+ };
1812
+ }
1813
+ case "memory_status": {
1814
+ const id = args.memory_id;
1815
+ validateUuid(id, "memory_id");
1816
+ const status = await this.client.getMemoryStatus(id);
1817
+ return {
1818
+ content: [{ type: "text", text: JSON.stringify(status, null, 2) }]
1819
+ };
1820
+ }
1821
+ case "context_build": {
1822
+ const result = await this.client.buildContextV2(
1823
+ args.query,
1824
+ {
1825
+ agentId: args.agent_id,
1826
+ maxMemories: args.max_memories,
1827
+ maxTokens: args.max_tokens,
1828
+ aiEnhanced: args.ai_enhanced,
1829
+ rankingVersion: args.ranking_version,
1830
+ searchMode: args.search_mode,
1831
+ llmApiUrl: args.llm_api_url,
1832
+ llmModel: args.llm_model,
1833
+ excludeMemoryIds: args.exclude_memory_ids
1834
+ }
1835
+ );
1836
+ return {
1837
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1838
+ };
1839
+ }
1840
+ // ── Tool Name Aliases (backward compatibility) ──
1841
+ case "memory_forget": {
1842
+ const id = args.memoryId;
1843
+ const query = args.query;
1844
+ if (id) {
1845
+ await this.client.deleteMemory(id);
1846
+ return {
1847
+ content: [{ type: "text", text: `Memory ${id.slice(0, 8)}... deleted.` }]
1848
+ };
1849
+ } else if (query) {
1850
+ const results = await this.client.searchMemories(query, 1, 0.9);
1851
+ if (results.length === 1) {
1852
+ await this.client.deleteMemory(results[0].memory.id);
1853
+ return {
1854
+ content: [{ type: "text", text: `Memory ${results[0].memory.id.slice(0, 8)}... deleted (matched query).` }]
1855
+ };
1856
+ }
1857
+ return {
1858
+ content: [{ type: "text", text: `Found ${results.length} matches. Provide memoryId to delete.` }]
1859
+ };
1860
+ }
1861
+ throw new Error("memoryId or query is required");
1862
+ }
1863
+ case "memory_recall": {
1864
+ const results = await this.client.searchMemories(
1865
+ args.query,
1866
+ args.limit ?? 5,
1867
+ args.threshold ?? 0.3,
1868
+ void 0,
1869
+ // use default agent
1870
+ false,
1871
+ false,
1872
+ false,
1873
+ args.max_tokens,
1874
+ args.project
1875
+ );
1876
+ return {
1877
+ content: [{ type: "text", text: JSON.stringify({ memories: results, total: results.length }, null, 2) }]
1878
+ };
1879
+ }
1629
1880
  default:
1630
1881
  throw new Error(`Unknown tool: ${name}`);
1631
1882
  }