@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 +32 -3
- package/dist/{chunk-P6TZEH6O.js → chunk-PFC5Z5ZD.js} +62 -5
- package/dist/chunk-PFC5Z5ZD.js.map +1 -0
- package/dist/cli/test.js +1 -1
- package/dist/index.js +254 -3
- package/dist/index.js.map +1 -1
- package/docs/OPENCLAW_GUIDE.md +64 -13
- package/docs/SECURITY.md +6 -6
- package/package.json +1 -1
- package/dist/chunk-P6TZEH6O.js.map +0 -1
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` |
|
|
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
|
|
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 (
|
|
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.
|
|
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
|
|
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-
|
|
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
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getLogger,
|
|
7
7
|
initLogger,
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
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.
|
|
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
|
}
|