@memoryrelay/mcp-server 0.1.9 → 0.3.0
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 +68 -134
- package/dist/chunk-P6TZEH6O.js +796 -0
- package/dist/chunk-P6TZEH6O.js.map +1 -0
- package/dist/cli/setup.d.ts +12 -0
- package/dist/cli/setup.js +243 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/test.d.ts +10 -0
- package/dist/cli/test.js +80 -0
- package/dist/cli/test.js.map +1 -0
- package/dist/index.js +1439 -649
- package/dist/index.js.map +1 -1
- package/docs/OPENCLAW_GUIDE.md +264 -0
- package/docs/SECURITY.md +1 -1
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -1,356 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
logLevel: z.enum(["debug", "info", "warn", "error"]).default("info")
|
|
11
|
-
});
|
|
12
|
-
function loadConfig() {
|
|
13
|
-
try {
|
|
14
|
-
const config = configSchema.parse({
|
|
15
|
-
apiKey: process.env.MEMORYRELAY_API_KEY,
|
|
16
|
-
apiUrl: process.env.MEMORYRELAY_API_URL,
|
|
17
|
-
agentId: process.env.MEMORYRELAY_AGENT_ID,
|
|
18
|
-
timeout: process.env.MEMORYRELAY_TIMEOUT ? parseInt(process.env.MEMORYRELAY_TIMEOUT, 10) : void 0,
|
|
19
|
-
logLevel: process.env.MEMORYRELAY_LOG_LEVEL
|
|
20
|
-
});
|
|
21
|
-
return config;
|
|
22
|
-
} catch (error) {
|
|
23
|
-
if (error instanceof z.ZodError) {
|
|
24
|
-
const issues = error.issues.map(
|
|
25
|
-
(issue) => ` - ${issue.path.join(".")}: ${issue.message}`
|
|
26
|
-
).join("\n");
|
|
27
|
-
throw new Error(
|
|
28
|
-
`Configuration validation failed:
|
|
29
|
-
${issues}
|
|
30
|
-
|
|
31
|
-
Please check your environment variables:
|
|
32
|
-
- MEMORYRELAY_API_KEY (required, starts with "mem_")
|
|
33
|
-
- MEMORYRELAY_API_URL (optional, default: https://api.memoryrelay.net)
|
|
34
|
-
- MEMORYRELAY_AGENT_ID (optional, auto-detected)
|
|
35
|
-
- MEMORYRELAY_TIMEOUT (optional, default: 30000)
|
|
36
|
-
- MEMORYRELAY_LOG_LEVEL (optional, default: info)`
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
throw error;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function getAgentId(config) {
|
|
43
|
-
if (config.agentId) {
|
|
44
|
-
return config.agentId;
|
|
45
|
-
}
|
|
46
|
-
if (process.env.OPENCLAW_AGENT_NAME) {
|
|
47
|
-
return process.env.OPENCLAW_AGENT_NAME;
|
|
48
|
-
}
|
|
49
|
-
const hostname = process.env.HOSTNAME || "unknown";
|
|
50
|
-
return `agent-${hostname.slice(0, 8)}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// src/logger.ts
|
|
54
|
-
var LOG_LEVELS = {
|
|
55
|
-
debug: 0,
|
|
56
|
-
info: 1,
|
|
57
|
-
warn: 2,
|
|
58
|
-
error: 3
|
|
59
|
-
};
|
|
60
|
-
var Logger = class {
|
|
61
|
-
minLevel;
|
|
62
|
-
constructor(level = "info") {
|
|
63
|
-
this.minLevel = LOG_LEVELS[level];
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Mask sensitive data in log messages
|
|
67
|
-
* - API keys starting with "mem_" are masked
|
|
68
|
-
* - Internal paths are sanitized
|
|
69
|
-
*/
|
|
70
|
-
sanitize(message) {
|
|
71
|
-
let sanitized = message;
|
|
72
|
-
sanitized = sanitized.replace(/mem_[a-zA-Z0-9_-]+/g, "mem_****");
|
|
73
|
-
sanitized = sanitized.replace(/\/[a-zA-Z0-9_\-./]+\.(ts|js|json)/g, "<file>");
|
|
74
|
-
sanitized = sanitized.replace(/at\s+[^\s]+\s+\([^)]+\)/g, "at <location>");
|
|
75
|
-
return sanitized;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Format log message with timestamp and level
|
|
79
|
-
*/
|
|
80
|
-
format(level, message, data) {
|
|
81
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
82
|
-
const sanitizedMessage = this.sanitize(message);
|
|
83
|
-
let output = `[${timestamp}] [${level.toUpperCase()}] ${sanitizedMessage}`;
|
|
84
|
-
if (data !== void 0) {
|
|
85
|
-
const sanitizedData = this.sanitize(JSON.stringify(data, null, 2));
|
|
86
|
-
output += `
|
|
87
|
-
${sanitizedData}`;
|
|
88
|
-
}
|
|
89
|
-
return output;
|
|
90
|
-
}
|
|
91
|
-
debug(message, data) {
|
|
92
|
-
if (this.minLevel <= LOG_LEVELS.debug) {
|
|
93
|
-
console.error(this.format("debug", message, data));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
info(message, data) {
|
|
97
|
-
if (this.minLevel <= LOG_LEVELS.info) {
|
|
98
|
-
console.error(this.format("info", message, data));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
warn(message, data) {
|
|
102
|
-
if (this.minLevel <= LOG_LEVELS.warn) {
|
|
103
|
-
console.error(this.format("warn", message, data));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
error(message, data) {
|
|
107
|
-
if (this.minLevel <= LOG_LEVELS.error) {
|
|
108
|
-
console.error(this.format("error", message, data));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
var logger;
|
|
113
|
-
function initLogger(level = "info") {
|
|
114
|
-
logger = new Logger(level);
|
|
115
|
-
return logger;
|
|
116
|
-
}
|
|
117
|
-
function getLogger() {
|
|
118
|
-
if (!logger) {
|
|
119
|
-
logger = new Logger();
|
|
120
|
-
}
|
|
121
|
-
return logger;
|
|
122
|
-
}
|
|
2
|
+
import {
|
|
3
|
+
MemoryRelayClient,
|
|
4
|
+
getAgentId,
|
|
5
|
+
getEnabledTools,
|
|
6
|
+
getLogger,
|
|
7
|
+
initLogger,
|
|
8
|
+
loadConfig
|
|
9
|
+
} from "./chunk-P6TZEH6O.js";
|
|
123
10
|
|
|
124
11
|
// src/server.ts
|
|
125
12
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
126
13
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
127
14
|
import {
|
|
128
15
|
CallToolRequestSchema,
|
|
129
|
-
ListToolsRequestSchema
|
|
130
|
-
ListResourcesRequestSchema,
|
|
131
|
-
ListResourceTemplatesRequestSchema,
|
|
132
|
-
ReadResourceRequestSchema,
|
|
133
|
-
ListPromptsRequestSchema,
|
|
134
|
-
GetPromptRequestSchema
|
|
16
|
+
ListToolsRequestSchema
|
|
135
17
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
136
|
-
import { z
|
|
137
|
-
|
|
138
|
-
// src/client.ts
|
|
139
|
-
var MAX_RETRIES = 3;
|
|
140
|
-
var INITIAL_DELAY_MS = 1e3;
|
|
141
|
-
var MAX_CONTENT_SIZE = 50 * 1024;
|
|
142
|
-
async function withRetry(fn, retries = MAX_RETRIES) {
|
|
143
|
-
let lastError;
|
|
144
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
145
|
-
try {
|
|
146
|
-
return await fn();
|
|
147
|
-
} catch (error) {
|
|
148
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
149
|
-
if (lastError.message.includes("401") || lastError.message.includes("403") || lastError.message.includes("404") || lastError.message.includes("400")) {
|
|
150
|
-
throw lastError;
|
|
151
|
-
}
|
|
152
|
-
if (attempt === retries) {
|
|
153
|
-
throw lastError;
|
|
154
|
-
}
|
|
155
|
-
const delay = INITIAL_DELAY_MS * Math.pow(2, attempt);
|
|
156
|
-
const jitter = Math.random() * 0.3 * delay;
|
|
157
|
-
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
throw lastError || new Error("Retry failed");
|
|
161
|
-
}
|
|
162
|
-
function maskApiKey(message, apiKey) {
|
|
163
|
-
if (!apiKey) return message;
|
|
164
|
-
const maskedKey = apiKey.substring(0, 8) + "***";
|
|
165
|
-
return message.replace(new RegExp(apiKey, "g"), maskedKey);
|
|
166
|
-
}
|
|
167
|
-
var MemoryRelayClient = class {
|
|
168
|
-
config;
|
|
169
|
-
logger = getLogger();
|
|
170
|
-
constructor(config) {
|
|
171
|
-
this.config = config;
|
|
172
|
-
this.logger.info("MemoryRelay client initialized", {
|
|
173
|
-
apiUrl: config.apiUrl,
|
|
174
|
-
agentId: config.agentId
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Make authenticated HTTP request to MemoryRelay API with retry logic
|
|
179
|
-
*/
|
|
180
|
-
async request(method, path, body) {
|
|
181
|
-
return withRetry(async () => {
|
|
182
|
-
const url = `${this.config.apiUrl}${path}`;
|
|
183
|
-
this.logger.debug(`API request: ${method} ${path}`);
|
|
184
|
-
const controller = new AbortController();
|
|
185
|
-
const timeout = setTimeout(() => controller.abort(), this.config.timeout);
|
|
186
|
-
try {
|
|
187
|
-
const response = await fetch(url, {
|
|
188
|
-
method,
|
|
189
|
-
headers: {
|
|
190
|
-
"Content-Type": "application/json",
|
|
191
|
-
"Authorization": `Bearer ${this.config.apiKey}`,
|
|
192
|
-
"User-Agent": "@memoryrelay/mcp-server"
|
|
193
|
-
},
|
|
194
|
-
body: body ? JSON.stringify(body) : void 0,
|
|
195
|
-
signal: controller.signal
|
|
196
|
-
});
|
|
197
|
-
if (!response.ok) {
|
|
198
|
-
if (response.status === 429) {
|
|
199
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
200
|
-
const waitMs = retryAfter ? parseInt(retryAfter) * 1e3 : 5e3;
|
|
201
|
-
this.logger.warn(`Rate limited, waiting ${waitMs}ms`);
|
|
202
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
203
|
-
throw new Error(`Rate limited: 429 - Retry after ${waitMs}ms`);
|
|
204
|
-
}
|
|
205
|
-
const errorData = await response.json().catch(() => ({}));
|
|
206
|
-
const errorMsg = `API request failed: ${response.status} ${response.statusText}` + (errorData.message ? ` - ${errorData.message}` : "");
|
|
207
|
-
throw new Error(maskApiKey(errorMsg, this.config.apiKey));
|
|
208
|
-
}
|
|
209
|
-
const data = await response.json();
|
|
210
|
-
this.logger.debug(`API response: ${method} ${path}`, { status: response.status });
|
|
211
|
-
return data;
|
|
212
|
-
} catch (error) {
|
|
213
|
-
if (error instanceof Error) {
|
|
214
|
-
if (error.name === "AbortError") {
|
|
215
|
-
throw new Error(`Request timeout after ${this.config.timeout}ms`);
|
|
216
|
-
}
|
|
217
|
-
error.message = maskApiKey(error.message, this.config.apiKey);
|
|
218
|
-
}
|
|
219
|
-
throw error;
|
|
220
|
-
} finally {
|
|
221
|
-
clearTimeout(timeout);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Validate content size
|
|
227
|
-
*/
|
|
228
|
-
validateContentSize(content) {
|
|
229
|
-
if (content.length > MAX_CONTENT_SIZE) {
|
|
230
|
-
throw new Error(`Content exceeds maximum size of ${MAX_CONTENT_SIZE} bytes`);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Store a new memory
|
|
235
|
-
*/
|
|
236
|
-
async storeMemory(content, metadata) {
|
|
237
|
-
this.validateContentSize(content);
|
|
238
|
-
return this.request("POST", "/v1/memories/memories", {
|
|
239
|
-
content,
|
|
240
|
-
metadata,
|
|
241
|
-
agent_id: this.config.agentId
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Search memories using semantic search
|
|
246
|
-
*/
|
|
247
|
-
async searchMemories(query, limit = 10, threshold = 0.5) {
|
|
248
|
-
this.validateContentSize(query);
|
|
249
|
-
const response = await this.request(
|
|
250
|
-
"POST",
|
|
251
|
-
"/v1/memories/memories/search",
|
|
252
|
-
{ query, limit, threshold, agent_id: this.config.agentId }
|
|
253
|
-
);
|
|
254
|
-
return response.data;
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* List recent memories with pagination
|
|
258
|
-
*/
|
|
259
|
-
async listMemories(limit = 20, offset = 0) {
|
|
260
|
-
return this.request(
|
|
261
|
-
"GET",
|
|
262
|
-
`/v1/memories/memories?limit=${limit}&offset=${offset}`
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Get a specific memory by ID
|
|
267
|
-
*/
|
|
268
|
-
async getMemory(id) {
|
|
269
|
-
return this.request("GET", `/v1/memories/memories/${id}`);
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Update an existing memory
|
|
273
|
-
*/
|
|
274
|
-
async updateMemory(id, content, metadata) {
|
|
275
|
-
this.validateContentSize(content);
|
|
276
|
-
return this.request("PATCH", `/v1/memories/memories/${id}`, {
|
|
277
|
-
content,
|
|
278
|
-
metadata
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Delete a memory
|
|
283
|
-
*/
|
|
284
|
-
async deleteMemory(id) {
|
|
285
|
-
await this.request("DELETE", `/v1/memories/memories/${id}`);
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Create a named entity
|
|
289
|
-
*/
|
|
290
|
-
async createEntity(name, type, metadata) {
|
|
291
|
-
this.validateContentSize(name);
|
|
292
|
-
return this.request("POST", "/v1/entities", {
|
|
293
|
-
name,
|
|
294
|
-
type,
|
|
295
|
-
metadata
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Link an entity to a memory
|
|
300
|
-
*/
|
|
301
|
-
async linkEntity(entityId, memoryId, relationship = "mentioned_in") {
|
|
302
|
-
await this.request("POST", "/v1/entities/links", {
|
|
303
|
-
entity_id: entityId,
|
|
304
|
-
memory_id: memoryId,
|
|
305
|
-
relationship
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Get an entity by ID
|
|
310
|
-
*/
|
|
311
|
-
async getEntity(id) {
|
|
312
|
-
return this.request("GET", `/v1/entities/${id}`);
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* List entities with pagination
|
|
316
|
-
*/
|
|
317
|
-
async listEntities(limit = 20, offset = 0) {
|
|
318
|
-
return this.request(
|
|
319
|
-
"GET",
|
|
320
|
-
`/v1/entities?limit=${limit}&offset=${offset}`
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Delete an entity
|
|
325
|
-
*/
|
|
326
|
-
async deleteEntity(id) {
|
|
327
|
-
await this.request("DELETE", `/v1/entities/${id}`);
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Health check - verify API connectivity
|
|
331
|
-
*/
|
|
332
|
-
async healthCheck() {
|
|
333
|
-
try {
|
|
334
|
-
await this.request("GET", "/v1/health");
|
|
335
|
-
return {
|
|
336
|
-
status: "healthy",
|
|
337
|
-
message: "API connection successful"
|
|
338
|
-
};
|
|
339
|
-
} catch (error) {
|
|
340
|
-
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
341
|
-
return {
|
|
342
|
-
status: "unhealthy",
|
|
343
|
-
message: `API connection failed: ${errorMsg}`
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
// src/server.ts
|
|
18
|
+
import { z } from "zod";
|
|
350
19
|
function sanitizeHtml(str) {
|
|
351
20
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'").replace(/\//g, "/");
|
|
352
21
|
}
|
|
353
|
-
var uuidSchema =
|
|
22
|
+
var uuidSchema = z.string().uuid();
|
|
354
23
|
function validateUuid(id, fieldName = "id") {
|
|
355
24
|
const result = uuidSchema.safeParse(id);
|
|
356
25
|
if (!result.success) {
|
|
@@ -361,35 +30,50 @@ var MemoryRelayMCPServer = class {
|
|
|
361
30
|
server;
|
|
362
31
|
client;
|
|
363
32
|
logger = getLogger();
|
|
364
|
-
|
|
365
|
-
|
|
33
|
+
activeSessionId = null;
|
|
34
|
+
enabledTools = null;
|
|
35
|
+
constructor(config, client) {
|
|
36
|
+
this.client = client ?? new MemoryRelayClient(config);
|
|
37
|
+
this.enabledTools = getEnabledTools();
|
|
366
38
|
this.server = new Server(
|
|
367
39
|
{
|
|
368
40
|
name: "@memoryrelay/mcp-server",
|
|
369
|
-
version: "0.
|
|
41
|
+
version: "0.3.0"
|
|
370
42
|
},
|
|
371
43
|
{
|
|
372
44
|
capabilities: {
|
|
373
|
-
tools: {}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
45
|
+
tools: {}
|
|
46
|
+
},
|
|
47
|
+
instructions: [
|
|
48
|
+
"Recommended workflow:",
|
|
49
|
+
"1. Call project_context(project) to load hot-tier memories for context",
|
|
50
|
+
"2. Call session_start(project, goal) to begin tracking your work",
|
|
51
|
+
"3. Call decision_check(project, topic) before making architectural choices",
|
|
52
|
+
"4. Call pattern_search(query) to find established conventions",
|
|
53
|
+
"5. Work on the task, using memory_store for important findings",
|
|
54
|
+
"6. Call session_end(session_id, summary) when done"
|
|
55
|
+
].join("\n")
|
|
377
56
|
}
|
|
378
57
|
);
|
|
379
|
-
this.
|
|
380
|
-
this.setupResourceHandlers();
|
|
381
|
-
this.setupPromptHandlers();
|
|
58
|
+
this.setupHandlers();
|
|
382
59
|
this.logger.info("MCP server initialized");
|
|
383
60
|
}
|
|
384
61
|
/**
|
|
385
|
-
*
|
|
62
|
+
* Check if a tool is enabled by the current tool group configuration.
|
|
63
|
+
*/
|
|
64
|
+
isToolEnabled(name) {
|
|
65
|
+
return this.enabledTools === null || this.enabledTools.has(name);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Setup MCP protocol handlers
|
|
386
69
|
*/
|
|
387
|
-
|
|
388
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () =>
|
|
389
|
-
|
|
70
|
+
setupHandlers() {
|
|
71
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
72
|
+
const sessionHint = this.activeSessionId ? ` Active session: ${this.activeSessionId}.` : " Tip: Call session_start first to track what you are working on.";
|
|
73
|
+
const allTools = [
|
|
390
74
|
{
|
|
391
75
|
name: "memory_store",
|
|
392
|
-
description:
|
|
76
|
+
description: `Store a new memory. Use this to save important information, facts, preferences, or context that should be remembered for future conversations.${sessionHint}`,
|
|
393
77
|
inputSchema: {
|
|
394
78
|
type: "object",
|
|
395
79
|
properties: {
|
|
@@ -401,6 +85,35 @@ var MemoryRelayMCPServer = class {
|
|
|
401
85
|
type: "object",
|
|
402
86
|
description: "Optional key-value metadata to attach to the memory",
|
|
403
87
|
additionalProperties: { type: "string" }
|
|
88
|
+
},
|
|
89
|
+
deduplicate: {
|
|
90
|
+
type: "boolean",
|
|
91
|
+
description: "Check for duplicate/near-duplicate content before storing. Returns existing memory if match found.",
|
|
92
|
+
default: false
|
|
93
|
+
},
|
|
94
|
+
dedup_threshold: {
|
|
95
|
+
type: "number",
|
|
96
|
+
description: "Semantic similarity threshold for dedup (0.5-1.0). Only used when deduplicate=true.",
|
|
97
|
+
minimum: 0.5,
|
|
98
|
+
maximum: 1,
|
|
99
|
+
default: 0.95
|
|
100
|
+
},
|
|
101
|
+
project: {
|
|
102
|
+
type: "string",
|
|
103
|
+
description: 'Project slug to associate the memory with (e.g., "my-api")',
|
|
104
|
+
maxLength: 100
|
|
105
|
+
},
|
|
106
|
+
importance: {
|
|
107
|
+
type: "number",
|
|
108
|
+
description: "Memory importance (0.0-1.0). Defaults to 0.5. Values >= 0.8 promote to hot tier.",
|
|
109
|
+
minimum: 0,
|
|
110
|
+
maximum: 1,
|
|
111
|
+
default: 0.5
|
|
112
|
+
},
|
|
113
|
+
tier: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: 'Memory tier override: "hot" (always in context), "warm" (default), "cold" (archived). Auto-computed from importance if omitted.',
|
|
116
|
+
enum: ["hot", "warm", "cold"]
|
|
404
117
|
}
|
|
405
118
|
},
|
|
406
119
|
required: ["content"]
|
|
@@ -408,7 +121,7 @@ var MemoryRelayMCPServer = class {
|
|
|
408
121
|
},
|
|
409
122
|
{
|
|
410
123
|
name: "memory_search",
|
|
411
|
-
description: "Search memories using natural language. Returns the most relevant memories based on semantic similarity to the query.",
|
|
124
|
+
description: "Search memories using natural language. Returns the most relevant memories based on semantic similarity to the query. Omit agent_id to search across all agents.",
|
|
412
125
|
inputSchema: {
|
|
413
126
|
type: "object",
|
|
414
127
|
properties: {
|
|
@@ -416,6 +129,10 @@ var MemoryRelayMCPServer = class {
|
|
|
416
129
|
type: "string",
|
|
417
130
|
description: "Natural language search query"
|
|
418
131
|
},
|
|
132
|
+
agent_id: {
|
|
133
|
+
type: "string",
|
|
134
|
+
description: "Optional agent ID to scope the search. If omitted, searches across all agents."
|
|
135
|
+
},
|
|
419
136
|
limit: {
|
|
420
137
|
type: "number",
|
|
421
138
|
description: "Maximum number of results to return (1-50)",
|
|
@@ -429,6 +146,41 @@ var MemoryRelayMCPServer = class {
|
|
|
429
146
|
minimum: 0,
|
|
430
147
|
maximum: 1,
|
|
431
148
|
default: 0.5
|
|
149
|
+
},
|
|
150
|
+
include_confidential: {
|
|
151
|
+
type: "boolean",
|
|
152
|
+
description: "Include confidential memories in results (default: false)",
|
|
153
|
+
default: false
|
|
154
|
+
},
|
|
155
|
+
include_archived: {
|
|
156
|
+
type: "boolean",
|
|
157
|
+
description: "Include archived memories in results (default: false)",
|
|
158
|
+
default: false
|
|
159
|
+
},
|
|
160
|
+
compress: {
|
|
161
|
+
type: "boolean",
|
|
162
|
+
description: "Enable context compression on results",
|
|
163
|
+
default: false
|
|
164
|
+
},
|
|
165
|
+
max_context_tokens: {
|
|
166
|
+
type: "number",
|
|
167
|
+
description: "Target total token budget for compressed results"
|
|
168
|
+
},
|
|
169
|
+
project: {
|
|
170
|
+
type: "string",
|
|
171
|
+
description: "Filter by project slug. Omit for cross-project search.",
|
|
172
|
+
maxLength: 100
|
|
173
|
+
},
|
|
174
|
+
tier: {
|
|
175
|
+
type: "string",
|
|
176
|
+
description: 'Filter by tier: "hot", "warm", or "cold". Omit to search all tiers.',
|
|
177
|
+
enum: ["hot", "warm", "cold"]
|
|
178
|
+
},
|
|
179
|
+
min_importance: {
|
|
180
|
+
type: "number",
|
|
181
|
+
description: "Minimum importance threshold (0.0-1.0). Only return memories above this.",
|
|
182
|
+
minimum: 0,
|
|
183
|
+
maximum: 1
|
|
432
184
|
}
|
|
433
185
|
},
|
|
434
186
|
required: ["query"]
|
|
@@ -557,79 +309,803 @@ var MemoryRelayMCPServer = class {
|
|
|
557
309
|
}
|
|
558
310
|
},
|
|
559
311
|
{
|
|
560
|
-
name: "
|
|
561
|
-
description: "
|
|
312
|
+
name: "entity_list",
|
|
313
|
+
description: "List entities in the knowledge graph with pagination. Entities are people, places, organizations, projects, and concepts extracted from memories.",
|
|
562
314
|
inputSchema: {
|
|
563
315
|
type: "object",
|
|
564
|
-
properties: {
|
|
316
|
+
properties: {
|
|
317
|
+
limit: {
|
|
318
|
+
type: "number",
|
|
319
|
+
description: "Number of entities to return (1-100)",
|
|
320
|
+
minimum: 1,
|
|
321
|
+
maximum: 100,
|
|
322
|
+
default: 20
|
|
323
|
+
},
|
|
324
|
+
offset: {
|
|
325
|
+
type: "number",
|
|
326
|
+
description: "Offset for pagination",
|
|
327
|
+
minimum: 0,
|
|
328
|
+
default: 0
|
|
329
|
+
}
|
|
330
|
+
}
|
|
565
331
|
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
{
|
|
582
|
-
type: "text",
|
|
583
|
-
text: JSON.stringify(memory, null, 2)
|
|
584
|
-
}
|
|
585
|
-
]
|
|
586
|
-
};
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "agent_list",
|
|
335
|
+
description: "List all agents with their memory counts. Agents are namespaces that isolate memories.",
|
|
336
|
+
inputSchema: {
|
|
337
|
+
type: "object",
|
|
338
|
+
properties: {
|
|
339
|
+
limit: {
|
|
340
|
+
type: "number",
|
|
341
|
+
description: "Number of agents to return (1-100)",
|
|
342
|
+
minimum: 1,
|
|
343
|
+
maximum: 100,
|
|
344
|
+
default: 20
|
|
345
|
+
}
|
|
346
|
+
}
|
|
587
347
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
};
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: "agent_create",
|
|
351
|
+
description: "Create a named agent. Agents are namespaces that isolate memories for different use cases or AI assistants.",
|
|
352
|
+
inputSchema: {
|
|
353
|
+
type: "object",
|
|
354
|
+
properties: {
|
|
355
|
+
name: {
|
|
356
|
+
type: "string",
|
|
357
|
+
description: 'Agent name (descriptive, e.g. "iris", "friday", "code-assistant")'
|
|
358
|
+
},
|
|
359
|
+
description: {
|
|
360
|
+
type: "string",
|
|
361
|
+
description: "Optional description of the agent's purpose"
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
required: ["name"]
|
|
606
365
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "agent_get",
|
|
369
|
+
description: "Get details of a specific agent by ID, including memory count.",
|
|
370
|
+
inputSchema: {
|
|
371
|
+
type: "object",
|
|
372
|
+
properties: {
|
|
373
|
+
id: {
|
|
374
|
+
type: "string",
|
|
375
|
+
description: "The agent ID (UUID) to retrieve"
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
required: ["id"]
|
|
620
379
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: "entity_graph",
|
|
383
|
+
description: "Explore the knowledge graph around an entity. Returns neighboring entities and their relationships up to N hops away. Useful for understanding connections between people, projects, and concepts.",
|
|
384
|
+
inputSchema: {
|
|
385
|
+
type: "object",
|
|
386
|
+
properties: {
|
|
387
|
+
entity_id: {
|
|
388
|
+
type: "string",
|
|
389
|
+
description: "Entity UUID to explore from"
|
|
390
|
+
},
|
|
391
|
+
depth: {
|
|
392
|
+
type: "number",
|
|
393
|
+
description: "How many hops to traverse (1 or 2)",
|
|
394
|
+
minimum: 1,
|
|
395
|
+
maximum: 2,
|
|
396
|
+
default: 1
|
|
397
|
+
},
|
|
398
|
+
max_neighbors: {
|
|
399
|
+
type: "number",
|
|
400
|
+
description: "Maximum neighbor entities to return (1-100)",
|
|
401
|
+
minimum: 1,
|
|
402
|
+
maximum: 100,
|
|
403
|
+
default: 50
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
required: ["entity_id"]
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "memory_batch_store",
|
|
411
|
+
description: "Store multiple memories in a single operation. More efficient than calling memory_store repeatedly when saving several related facts or observations.",
|
|
412
|
+
inputSchema: {
|
|
413
|
+
type: "object",
|
|
414
|
+
properties: {
|
|
415
|
+
memories: {
|
|
416
|
+
type: "array",
|
|
417
|
+
description: "Array of memories to store (1-100 items)",
|
|
418
|
+
items: {
|
|
419
|
+
type: "object",
|
|
420
|
+
properties: {
|
|
421
|
+
content: {
|
|
422
|
+
type: "string",
|
|
423
|
+
description: "The memory content to store"
|
|
424
|
+
},
|
|
425
|
+
metadata: {
|
|
426
|
+
type: "object",
|
|
427
|
+
description: "Optional key-value metadata",
|
|
428
|
+
additionalProperties: { type: "string" }
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
required: ["content"]
|
|
432
|
+
},
|
|
433
|
+
minItems: 1,
|
|
434
|
+
maxItems: 100
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
required: ["memories"]
|
|
438
|
+
}
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: "memory_context",
|
|
442
|
+
description: "Build a formatted context string from relevant memories for a given query. Returns ranked memories with similarity scores, ready for use in prompts. More convenient than memory_search when you need a single context block.",
|
|
443
|
+
inputSchema: {
|
|
444
|
+
type: "object",
|
|
445
|
+
properties: {
|
|
446
|
+
query: {
|
|
447
|
+
type: "string",
|
|
448
|
+
description: "Natural language query to find relevant memories"
|
|
449
|
+
},
|
|
450
|
+
limit: {
|
|
451
|
+
type: "number",
|
|
452
|
+
description: "Maximum number of memories to include (1-50)",
|
|
453
|
+
minimum: 1,
|
|
454
|
+
maximum: 50,
|
|
455
|
+
default: 10
|
|
456
|
+
},
|
|
457
|
+
threshold: {
|
|
458
|
+
type: "number",
|
|
459
|
+
description: "Minimum similarity threshold (0-1)",
|
|
460
|
+
minimum: 0,
|
|
461
|
+
maximum: 1,
|
|
462
|
+
default: 0.5
|
|
463
|
+
},
|
|
464
|
+
max_tokens: {
|
|
465
|
+
type: "number",
|
|
466
|
+
description: "Maximum token budget for the context string. Results are truncated to fit."
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
required: ["query"]
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
name: "project_register",
|
|
474
|
+
description: "Register a project (codebase/repository) to scope memories, sessions, and decisions. Projects have a unique slug for ergonomic referencing.",
|
|
475
|
+
inputSchema: {
|
|
476
|
+
type: "object",
|
|
477
|
+
properties: {
|
|
478
|
+
slug: {
|
|
479
|
+
type: "string",
|
|
480
|
+
description: 'URL-safe identifier (lowercase, hyphens allowed, e.g., "my-api")',
|
|
481
|
+
pattern: "^[a-z0-9][a-z0-9-]*$",
|
|
482
|
+
maxLength: 100
|
|
483
|
+
},
|
|
484
|
+
name: {
|
|
485
|
+
type: "string",
|
|
486
|
+
description: 'Human-readable project name (e.g., "My API Service")',
|
|
487
|
+
maxLength: 255
|
|
488
|
+
},
|
|
489
|
+
description: {
|
|
490
|
+
type: "string",
|
|
491
|
+
description: "Optional project description"
|
|
492
|
+
},
|
|
493
|
+
stack: {
|
|
494
|
+
type: "object",
|
|
495
|
+
description: 'Technical stack metadata (e.g., {"languages": ["python"], "frameworks": ["fastapi"]})'
|
|
496
|
+
},
|
|
497
|
+
repo_url: {
|
|
498
|
+
type: "string",
|
|
499
|
+
description: 'Repository URL (e.g., "https://github.com/org/repo")',
|
|
500
|
+
maxLength: 500
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
required: ["slug", "name"]
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: "project_list",
|
|
508
|
+
description: "List registered projects. Shows all projects with their memory counts.",
|
|
509
|
+
inputSchema: {
|
|
510
|
+
type: "object",
|
|
511
|
+
properties: {
|
|
512
|
+
limit: {
|
|
513
|
+
type: "number",
|
|
514
|
+
description: "Maximum number of projects to return (1-100)",
|
|
515
|
+
minimum: 1,
|
|
516
|
+
maximum: 100,
|
|
517
|
+
default: 20
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
name: "project_info",
|
|
524
|
+
description: "Get detailed information about a specific project by its slug, including memory count and stack metadata.",
|
|
525
|
+
inputSchema: {
|
|
526
|
+
type: "object",
|
|
527
|
+
properties: {
|
|
528
|
+
slug: {
|
|
529
|
+
type: "string",
|
|
530
|
+
description: "Project slug to look up"
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
required: ["slug"]
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
// ── Project Relationship Tools (Issue #186) ──
|
|
537
|
+
{
|
|
538
|
+
name: "project_add_relationship",
|
|
539
|
+
description: "Register a relationship between two projects (e.g., depends_on, api_consumer, shares_schema, shares_infra, pattern_source, forked_from).",
|
|
540
|
+
inputSchema: {
|
|
541
|
+
type: "object",
|
|
542
|
+
properties: {
|
|
543
|
+
from: {
|
|
544
|
+
type: "string",
|
|
545
|
+
description: "Source project slug"
|
|
546
|
+
},
|
|
547
|
+
to: {
|
|
548
|
+
type: "string",
|
|
549
|
+
description: "Target project slug"
|
|
550
|
+
},
|
|
551
|
+
type: {
|
|
552
|
+
type: "string",
|
|
553
|
+
description: "Relationship type (e.g., depends_on, api_consumer, shares_schema)"
|
|
554
|
+
},
|
|
555
|
+
details: {
|
|
556
|
+
type: "object",
|
|
557
|
+
description: "Optional relationship metadata",
|
|
558
|
+
additionalProperties: true
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
required: ["from", "to", "type"]
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
name: "project_dependencies",
|
|
566
|
+
description: "What does this project depend on? Returns outgoing depends_on and api_consumer relationships.",
|
|
567
|
+
inputSchema: {
|
|
568
|
+
type: "object",
|
|
569
|
+
properties: {
|
|
570
|
+
project: {
|
|
571
|
+
type: "string",
|
|
572
|
+
description: "Project slug"
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
required: ["project"]
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
name: "project_dependents",
|
|
580
|
+
description: "What depends on this project? Returns incoming depends_on and api_consumer relationships.",
|
|
581
|
+
inputSchema: {
|
|
582
|
+
type: "object",
|
|
583
|
+
properties: {
|
|
584
|
+
project: {
|
|
585
|
+
type: "string",
|
|
586
|
+
description: "Project slug"
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
required: ["project"]
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
name: "project_related",
|
|
594
|
+
description: "All related projects with relationship types and direction indicators.",
|
|
595
|
+
inputSchema: {
|
|
596
|
+
type: "object",
|
|
597
|
+
properties: {
|
|
598
|
+
project: {
|
|
599
|
+
type: "string",
|
|
600
|
+
description: "Project slug"
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
required: ["project"]
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
name: "project_impact",
|
|
608
|
+
description: "Impact analysis: given a change description, which dependent projects might be affected? Searches decisions and memories in downstream projects.",
|
|
609
|
+
inputSchema: {
|
|
610
|
+
type: "object",
|
|
611
|
+
properties: {
|
|
612
|
+
project: {
|
|
613
|
+
type: "string",
|
|
614
|
+
description: "Project slug being changed"
|
|
615
|
+
},
|
|
616
|
+
change_description: {
|
|
617
|
+
type: "string",
|
|
618
|
+
description: "Description of the change to analyze (max 5000 chars)",
|
|
619
|
+
maxLength: 5e3
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
required: ["project", "change_description"]
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
name: "project_shared_patterns",
|
|
627
|
+
description: "What do two projects have in common? Returns patterns adopted by both projects.",
|
|
628
|
+
inputSchema: {
|
|
629
|
+
type: "object",
|
|
630
|
+
properties: {
|
|
631
|
+
project_a: {
|
|
632
|
+
type: "string",
|
|
633
|
+
description: "First project slug"
|
|
634
|
+
},
|
|
635
|
+
project_b: {
|
|
636
|
+
type: "string",
|
|
637
|
+
description: "Second project slug"
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
required: ["project_a", "project_b"]
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
name: "memory_health",
|
|
645
|
+
description: "Check API connectivity and health status.",
|
|
646
|
+
inputSchema: {
|
|
647
|
+
type: "object",
|
|
648
|
+
properties: {}
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
name: "session_start",
|
|
653
|
+
description: "Start a new session to group related memories. Sessions track bounded interaction periods (e.g., a coding session, a conversation). Use session_recall to see what happened in previous sessions.",
|
|
654
|
+
inputSchema: {
|
|
655
|
+
type: "object",
|
|
656
|
+
properties: {
|
|
657
|
+
title: {
|
|
658
|
+
type: "string",
|
|
659
|
+
description: 'Session title or goal (e.g., "Fix email queue retry logic")',
|
|
660
|
+
maxLength: 500
|
|
661
|
+
},
|
|
662
|
+
project: {
|
|
663
|
+
type: "string",
|
|
664
|
+
description: 'Project label for filtering sessions (e.g., "northrelay")',
|
|
665
|
+
maxLength: 255
|
|
666
|
+
},
|
|
667
|
+
metadata: {
|
|
668
|
+
type: "object",
|
|
669
|
+
description: "Optional metadata",
|
|
670
|
+
additionalProperties: { type: "string" }
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
name: "session_end",
|
|
677
|
+
description: "End an active session with an optional summary of what was accomplished.",
|
|
678
|
+
inputSchema: {
|
|
679
|
+
type: "object",
|
|
680
|
+
properties: {
|
|
681
|
+
id: {
|
|
682
|
+
type: "string",
|
|
683
|
+
description: "Session ID (UUID) to end"
|
|
684
|
+
},
|
|
685
|
+
summary: {
|
|
686
|
+
type: "string",
|
|
687
|
+
description: "Summary of what was accomplished during the session",
|
|
688
|
+
maxLength: 5e4
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
required: ["id"]
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
name: "session_recall",
|
|
696
|
+
description: "Recall a session by ID, including all memories created during it. Use this to review what happened in a past session.",
|
|
697
|
+
inputSchema: {
|
|
698
|
+
type: "object",
|
|
699
|
+
properties: {
|
|
700
|
+
id: {
|
|
701
|
+
type: "string",
|
|
702
|
+
description: "Session ID (UUID) to recall"
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
required: ["id"]
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
name: "session_list",
|
|
710
|
+
description: "List recent sessions, optionally filtered by project or status. Shows session summaries to understand what happened in previous sessions.",
|
|
711
|
+
inputSchema: {
|
|
712
|
+
type: "object",
|
|
713
|
+
properties: {
|
|
714
|
+
limit: {
|
|
715
|
+
type: "number",
|
|
716
|
+
description: "Maximum number of sessions to return (1-100)",
|
|
717
|
+
minimum: 1,
|
|
718
|
+
maximum: 100,
|
|
719
|
+
default: 20
|
|
720
|
+
},
|
|
721
|
+
project: {
|
|
722
|
+
type: "string",
|
|
723
|
+
description: "Filter by project label"
|
|
724
|
+
},
|
|
725
|
+
status: {
|
|
726
|
+
type: "string",
|
|
727
|
+
enum: ["active", "ended"],
|
|
728
|
+
description: "Filter by session status"
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
name: "decision_record",
|
|
735
|
+
description: "Record an architectural decision with rationale. Use to log important technical choices, design decisions, or policy changes so they can be referenced later.",
|
|
736
|
+
inputSchema: {
|
|
737
|
+
type: "object",
|
|
738
|
+
properties: {
|
|
739
|
+
title: {
|
|
740
|
+
type: "string",
|
|
741
|
+
description: 'Decision title (e.g., "Use PostgreSQL instead of MongoDB")',
|
|
742
|
+
maxLength: 500
|
|
743
|
+
},
|
|
744
|
+
rationale: {
|
|
745
|
+
type: "string",
|
|
746
|
+
description: "Why this decision was made. Include context, constraints, and reasoning.",
|
|
747
|
+
maxLength: 5e4
|
|
748
|
+
},
|
|
749
|
+
alternatives: {
|
|
750
|
+
type: "string",
|
|
751
|
+
description: "Alternatives that were considered and why they were rejected"
|
|
752
|
+
},
|
|
753
|
+
project: {
|
|
754
|
+
type: "string",
|
|
755
|
+
description: "Project slug to scope the decision to",
|
|
756
|
+
maxLength: 100
|
|
757
|
+
},
|
|
758
|
+
tags: {
|
|
759
|
+
type: "array",
|
|
760
|
+
items: { type: "string" },
|
|
761
|
+
description: 'Tags for categorization (e.g., ["architecture", "database"])'
|
|
762
|
+
},
|
|
763
|
+
status: {
|
|
764
|
+
type: "string",
|
|
765
|
+
enum: ["active", "experimental"],
|
|
766
|
+
description: "Decision status (default: active)"
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
required: ["title", "rationale"]
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
name: "decision_list",
|
|
774
|
+
description: "List recorded decisions, optionally filtered by project, status, or tags.",
|
|
775
|
+
inputSchema: {
|
|
776
|
+
type: "object",
|
|
777
|
+
properties: {
|
|
778
|
+
limit: {
|
|
779
|
+
type: "number",
|
|
780
|
+
description: "Maximum results (1-100, default 20)",
|
|
781
|
+
minimum: 1,
|
|
782
|
+
maximum: 100,
|
|
783
|
+
default: 20
|
|
784
|
+
},
|
|
785
|
+
project: {
|
|
786
|
+
type: "string",
|
|
787
|
+
description: "Filter by project slug"
|
|
788
|
+
},
|
|
789
|
+
status: {
|
|
790
|
+
type: "string",
|
|
791
|
+
enum: ["active", "superseded", "reverted", "experimental"],
|
|
792
|
+
description: "Filter by decision status"
|
|
793
|
+
},
|
|
794
|
+
tags: {
|
|
795
|
+
type: "string",
|
|
796
|
+
description: "Comma-separated tags to filter by"
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
name: "decision_supersede",
|
|
803
|
+
description: "Supersede an existing decision with a new one. Marks the old decision as superseded and creates a new active decision.",
|
|
804
|
+
inputSchema: {
|
|
805
|
+
type: "object",
|
|
806
|
+
properties: {
|
|
807
|
+
id: {
|
|
808
|
+
type: "string",
|
|
809
|
+
description: "ID (UUID) of the decision to supersede"
|
|
810
|
+
},
|
|
811
|
+
title: {
|
|
812
|
+
type: "string",
|
|
813
|
+
description: "Title of the new decision"
|
|
814
|
+
},
|
|
815
|
+
rationale: {
|
|
816
|
+
type: "string",
|
|
817
|
+
description: "Rationale for the new decision"
|
|
818
|
+
},
|
|
819
|
+
alternatives: {
|
|
820
|
+
type: "string",
|
|
821
|
+
description: "Alternatives considered"
|
|
822
|
+
},
|
|
823
|
+
tags: {
|
|
824
|
+
type: "array",
|
|
825
|
+
items: { type: "string" },
|
|
826
|
+
description: "Tags for the new decision (inherits from old if omitted)"
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
required: ["id", "title", "rationale"]
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
name: "decision_check",
|
|
834
|
+
description: "Check if there are existing decisions about a topic using semantic search. Use this before making a new decision to see if the topic has already been addressed.",
|
|
835
|
+
inputSchema: {
|
|
836
|
+
type: "object",
|
|
837
|
+
properties: {
|
|
838
|
+
query: {
|
|
839
|
+
type: "string",
|
|
840
|
+
description: "Natural language query describing the decision topic to check"
|
|
841
|
+
},
|
|
842
|
+
project: {
|
|
843
|
+
type: "string",
|
|
844
|
+
description: "Filter by project slug"
|
|
845
|
+
},
|
|
846
|
+
limit: {
|
|
847
|
+
type: "number",
|
|
848
|
+
description: "Maximum results (1-20, default 5)",
|
|
849
|
+
minimum: 1,
|
|
850
|
+
maximum: 20,
|
|
851
|
+
default: 5
|
|
852
|
+
},
|
|
853
|
+
threshold: {
|
|
854
|
+
type: "number",
|
|
855
|
+
description: "Minimum similarity threshold (0-1, default 0.3)",
|
|
856
|
+
minimum: 0,
|
|
857
|
+
maximum: 1,
|
|
858
|
+
default: 0.3
|
|
859
|
+
},
|
|
860
|
+
include_superseded: {
|
|
861
|
+
type: "boolean",
|
|
862
|
+
description: "Include superseded decisions in results (default: false)",
|
|
863
|
+
default: false
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
required: ["query"]
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
name: "pattern_create",
|
|
871
|
+
description: 'Create a reusable development pattern or convention. Use to document patterns like "always use Zod for validation" or "error responses follow RFC 7807" so they can be discovered and adopted across projects.',
|
|
872
|
+
inputSchema: {
|
|
873
|
+
type: "object",
|
|
874
|
+
properties: {
|
|
875
|
+
title: {
|
|
876
|
+
type: "string",
|
|
877
|
+
description: 'Pattern title (e.g., "Zod validation at API boundaries")',
|
|
878
|
+
maxLength: 500
|
|
879
|
+
},
|
|
880
|
+
description: {
|
|
881
|
+
type: "string",
|
|
882
|
+
description: "Full description with context, rationale, and when to apply this pattern",
|
|
883
|
+
maxLength: 5e4
|
|
884
|
+
},
|
|
885
|
+
category: {
|
|
886
|
+
type: "string",
|
|
887
|
+
description: 'Category (e.g., "validation", "error-handling", "auth", "testing")',
|
|
888
|
+
maxLength: 100
|
|
889
|
+
},
|
|
890
|
+
example_code: {
|
|
891
|
+
type: "string",
|
|
892
|
+
description: "Code snippet demonstrating the pattern"
|
|
893
|
+
},
|
|
894
|
+
scope: {
|
|
895
|
+
type: "string",
|
|
896
|
+
enum: ["global", "project"],
|
|
897
|
+
description: "Scope: global (all projects) or project (specific projects). Default: global"
|
|
898
|
+
},
|
|
899
|
+
tags: {
|
|
900
|
+
type: "array",
|
|
901
|
+
items: { type: "string" },
|
|
902
|
+
description: "Tags for categorization"
|
|
903
|
+
},
|
|
904
|
+
source_project: {
|
|
905
|
+
type: "string",
|
|
906
|
+
description: "Slug of the project where this pattern was first established"
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
required: ["title", "description"]
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
name: "pattern_search",
|
|
914
|
+
description: "Search for development patterns using semantic similarity. When working on a project, shows both global patterns and patterns adopted by that project.",
|
|
915
|
+
inputSchema: {
|
|
916
|
+
type: "object",
|
|
917
|
+
properties: {
|
|
918
|
+
query: {
|
|
919
|
+
type: "string",
|
|
920
|
+
description: 'Natural language query describing the pattern to find (e.g., "validation", "error handling")'
|
|
921
|
+
},
|
|
922
|
+
category: {
|
|
923
|
+
type: "string",
|
|
924
|
+
description: "Filter by category"
|
|
925
|
+
},
|
|
926
|
+
project: {
|
|
927
|
+
type: "string",
|
|
928
|
+
description: "Project context \u2014 shows global patterns plus patterns adopted by this project"
|
|
929
|
+
},
|
|
930
|
+
limit: {
|
|
931
|
+
type: "number",
|
|
932
|
+
description: "Maximum results (1-50, default 10)",
|
|
933
|
+
minimum: 1,
|
|
934
|
+
maximum: 50,
|
|
935
|
+
default: 10
|
|
936
|
+
},
|
|
937
|
+
threshold: {
|
|
938
|
+
type: "number",
|
|
939
|
+
description: "Minimum similarity threshold (0-1, default 0.3)",
|
|
940
|
+
minimum: 0,
|
|
941
|
+
maximum: 1,
|
|
942
|
+
default: 0.3
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
required: ["query"]
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
name: "pattern_adopt",
|
|
950
|
+
description: "Mark a pattern as adopted by a project. This links the pattern to the project so it shows up in project-scoped searches.",
|
|
951
|
+
inputSchema: {
|
|
952
|
+
type: "object",
|
|
953
|
+
properties: {
|
|
954
|
+
id: {
|
|
955
|
+
type: "string",
|
|
956
|
+
description: "Pattern ID (UUID) to adopt"
|
|
957
|
+
},
|
|
958
|
+
project: {
|
|
959
|
+
type: "string",
|
|
960
|
+
description: "Project slug to adopt this pattern for"
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
required: ["id", "project"]
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
name: "pattern_suggest",
|
|
968
|
+
description: "Suggest relevant patterns for a project based on what similar projects have adopted. Returns patterns not yet adopted by the target project, ranked by popularity.",
|
|
969
|
+
inputSchema: {
|
|
970
|
+
type: "object",
|
|
971
|
+
properties: {
|
|
972
|
+
project: {
|
|
973
|
+
type: "string",
|
|
974
|
+
description: "Project slug to get suggestions for"
|
|
975
|
+
},
|
|
976
|
+
limit: {
|
|
977
|
+
type: "number",
|
|
978
|
+
description: "Maximum suggestions (1-50, default 10)",
|
|
979
|
+
minimum: 1,
|
|
980
|
+
maximum: 50,
|
|
981
|
+
default: 10
|
|
982
|
+
}
|
|
983
|
+
},
|
|
984
|
+
required: ["project"]
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
name: "project_context",
|
|
989
|
+
description: "Get full project context for LLM consumption. Returns hot-tier memories, active decisions, adopted patterns, and a pre-formatted markdown context string.",
|
|
990
|
+
inputSchema: {
|
|
991
|
+
type: "object",
|
|
992
|
+
properties: {
|
|
993
|
+
project: {
|
|
994
|
+
type: "string",
|
|
995
|
+
description: "Project slug to get context for"
|
|
996
|
+
}
|
|
997
|
+
},
|
|
998
|
+
required: ["project"]
|
|
999
|
+
}
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
name: "memory_promote",
|
|
1003
|
+
description: "Update a memory's importance and tier. Use to promote critical memories to hot tier (always in context) or demote them to cold tier (archived).",
|
|
1004
|
+
inputSchema: {
|
|
1005
|
+
type: "object",
|
|
1006
|
+
properties: {
|
|
1007
|
+
memory_id: {
|
|
1008
|
+
type: "string",
|
|
1009
|
+
description: "Memory ID (UUID) to promote/demote"
|
|
1010
|
+
},
|
|
1011
|
+
importance: {
|
|
1012
|
+
type: "number",
|
|
1013
|
+
description: "New importance value (0.0-1.0). Values >= 0.8 promote to hot tier.",
|
|
1014
|
+
minimum: 0,
|
|
1015
|
+
maximum: 1
|
|
1016
|
+
},
|
|
1017
|
+
tier: {
|
|
1018
|
+
type: "string",
|
|
1019
|
+
description: 'Optional tier override: "hot", "warm", or "cold". Auto-computed from importance if omitted.',
|
|
1020
|
+
enum: ["hot", "warm", "cold"]
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
required: ["memory_id", "importance"]
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
];
|
|
1027
|
+
return {
|
|
1028
|
+
tools: allTools.filter((t) => this.isToolEnabled(t.name))
|
|
1029
|
+
};
|
|
1030
|
+
});
|
|
1031
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1032
|
+
const { name, arguments: args = {} } = request.params;
|
|
1033
|
+
this.logger.debug(`Tool called: ${name}`, args);
|
|
1034
|
+
try {
|
|
1035
|
+
switch (name) {
|
|
1036
|
+
case "memory_store": {
|
|
1037
|
+
const memory = await this.client.storeMemory(
|
|
1038
|
+
args.content,
|
|
1039
|
+
args.metadata,
|
|
1040
|
+
args.deduplicate,
|
|
1041
|
+
args.dedup_threshold,
|
|
1042
|
+
args.project,
|
|
1043
|
+
args.importance,
|
|
1044
|
+
args.tier
|
|
1045
|
+
);
|
|
1046
|
+
return {
|
|
1047
|
+
content: [
|
|
1048
|
+
{
|
|
1049
|
+
type: "text",
|
|
1050
|
+
text: JSON.stringify(memory, null, 2)
|
|
1051
|
+
}
|
|
1052
|
+
]
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
case "memory_search": {
|
|
1056
|
+
const searchAgentId = args.agent_id != null ? args.agent_id : null;
|
|
1057
|
+
const results = await this.client.searchMemories(
|
|
1058
|
+
args.query,
|
|
1059
|
+
args.limit,
|
|
1060
|
+
args.threshold,
|
|
1061
|
+
searchAgentId,
|
|
1062
|
+
args.include_confidential ?? false,
|
|
1063
|
+
args.include_archived ?? false,
|
|
1064
|
+
args.compress ?? false,
|
|
1065
|
+
args.max_context_tokens,
|
|
1066
|
+
args.project,
|
|
1067
|
+
args.tier,
|
|
1068
|
+
args.min_importance
|
|
1069
|
+
);
|
|
1070
|
+
return {
|
|
1071
|
+
content: [
|
|
1072
|
+
{
|
|
1073
|
+
type: "text",
|
|
1074
|
+
text: JSON.stringify(
|
|
1075
|
+
{ memories: results, total: results.length },
|
|
1076
|
+
null,
|
|
1077
|
+
2
|
|
1078
|
+
)
|
|
1079
|
+
}
|
|
1080
|
+
]
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
case "memory_list": {
|
|
1084
|
+
const response = await this.client.listMemories(
|
|
1085
|
+
args.limit,
|
|
1086
|
+
args.offset
|
|
1087
|
+
);
|
|
1088
|
+
return {
|
|
1089
|
+
content: [
|
|
1090
|
+
{
|
|
1091
|
+
type: "text",
|
|
1092
|
+
text: JSON.stringify(response, null, 2)
|
|
1093
|
+
}
|
|
1094
|
+
]
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
case "memory_get": {
|
|
1098
|
+
const id = args.id;
|
|
1099
|
+
validateUuid(id, "memory_id");
|
|
1100
|
+
const memory = await this.client.getMemory(id);
|
|
1101
|
+
return {
|
|
1102
|
+
content: [
|
|
1103
|
+
{
|
|
1104
|
+
type: "text",
|
|
1105
|
+
text: JSON.stringify(memory, null, 2)
|
|
1106
|
+
}
|
|
1107
|
+
]
|
|
1108
|
+
};
|
|
633
1109
|
}
|
|
634
1110
|
case "memory_update": {
|
|
635
1111
|
const id = args.id;
|
|
@@ -643,81 +1119,312 @@ var MemoryRelayMCPServer = class {
|
|
|
643
1119
|
content: [
|
|
644
1120
|
{
|
|
645
1121
|
type: "text",
|
|
646
|
-
text: JSON.stringify(memory, null, 2)
|
|
1122
|
+
text: JSON.stringify(memory, null, 2)
|
|
1123
|
+
}
|
|
1124
|
+
]
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
case "memory_delete": {
|
|
1128
|
+
const id = args.id;
|
|
1129
|
+
validateUuid(id, "memory_id");
|
|
1130
|
+
await this.client.deleteMemory(id);
|
|
1131
|
+
return {
|
|
1132
|
+
content: [
|
|
1133
|
+
{
|
|
1134
|
+
type: "text",
|
|
1135
|
+
text: JSON.stringify(
|
|
1136
|
+
{ success: true, message: "Memory deleted successfully" },
|
|
1137
|
+
null,
|
|
1138
|
+
2
|
|
1139
|
+
)
|
|
1140
|
+
}
|
|
1141
|
+
]
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
case "entity_create": {
|
|
1145
|
+
const entitySchema = z.object({
|
|
1146
|
+
name: z.string().min(1).max(200),
|
|
1147
|
+
type: z.enum(["person", "place", "organization", "project", "concept", "other"]),
|
|
1148
|
+
metadata: z.record(z.string()).optional()
|
|
1149
|
+
});
|
|
1150
|
+
const validatedInput = entitySchema.parse(args);
|
|
1151
|
+
const sanitizedName = sanitizeHtml(validatedInput.name);
|
|
1152
|
+
const entity = await this.client.createEntity(
|
|
1153
|
+
sanitizedName,
|
|
1154
|
+
validatedInput.type,
|
|
1155
|
+
validatedInput.metadata
|
|
1156
|
+
);
|
|
1157
|
+
return {
|
|
1158
|
+
content: [
|
|
1159
|
+
{
|
|
1160
|
+
type: "text",
|
|
1161
|
+
text: JSON.stringify(entity, null, 2)
|
|
1162
|
+
}
|
|
1163
|
+
]
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
case "entity_link": {
|
|
1167
|
+
const linkSchema = z.object({
|
|
1168
|
+
entity_id: z.string().uuid(),
|
|
1169
|
+
memory_id: z.string().uuid(),
|
|
1170
|
+
relationship: z.string().default("mentioned_in")
|
|
1171
|
+
});
|
|
1172
|
+
const validatedInput = linkSchema.parse(args);
|
|
1173
|
+
await this.client.linkEntity(
|
|
1174
|
+
validatedInput.entity_id,
|
|
1175
|
+
validatedInput.memory_id,
|
|
1176
|
+
validatedInput.relationship
|
|
1177
|
+
);
|
|
1178
|
+
return {
|
|
1179
|
+
content: [
|
|
1180
|
+
{
|
|
1181
|
+
type: "text",
|
|
1182
|
+
text: JSON.stringify(
|
|
1183
|
+
{
|
|
1184
|
+
success: true,
|
|
1185
|
+
message: "Entity linked to memory successfully",
|
|
1186
|
+
entity_id: validatedInput.entity_id,
|
|
1187
|
+
memory_id: validatedInput.memory_id,
|
|
1188
|
+
relationship: validatedInput.relationship
|
|
1189
|
+
},
|
|
1190
|
+
null,
|
|
1191
|
+
2
|
|
1192
|
+
)
|
|
1193
|
+
}
|
|
1194
|
+
]
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
case "entity_list": {
|
|
1198
|
+
const response = await this.client.listEntities(
|
|
1199
|
+
args.limit,
|
|
1200
|
+
args.offset
|
|
1201
|
+
);
|
|
1202
|
+
return {
|
|
1203
|
+
content: [
|
|
1204
|
+
{
|
|
1205
|
+
type: "text",
|
|
1206
|
+
text: JSON.stringify(response, null, 2)
|
|
647
1207
|
}
|
|
648
1208
|
]
|
|
649
1209
|
};
|
|
650
1210
|
}
|
|
651
|
-
case "
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
1211
|
+
case "agent_list": {
|
|
1212
|
+
const response = await this.client.listAgents(
|
|
1213
|
+
args.limit
|
|
1214
|
+
);
|
|
655
1215
|
return {
|
|
656
1216
|
content: [
|
|
657
1217
|
{
|
|
658
1218
|
type: "text",
|
|
659
|
-
text: JSON.stringify(
|
|
660
|
-
{ success: true, message: "Memory deleted successfully" },
|
|
661
|
-
null,
|
|
662
|
-
2
|
|
663
|
-
)
|
|
1219
|
+
text: JSON.stringify(response, null, 2)
|
|
664
1220
|
}
|
|
665
1221
|
]
|
|
666
1222
|
};
|
|
667
1223
|
}
|
|
668
|
-
case "
|
|
669
|
-
const
|
|
670
|
-
name:
|
|
671
|
-
|
|
672
|
-
metadata: z2.record(z2.string()).optional()
|
|
1224
|
+
case "agent_create": {
|
|
1225
|
+
const createSchema = z.object({
|
|
1226
|
+
name: z.string().min(1).max(255),
|
|
1227
|
+
description: z.string().max(1e3).optional()
|
|
673
1228
|
});
|
|
674
|
-
const validatedInput =
|
|
1229
|
+
const validatedInput = createSchema.parse(args);
|
|
675
1230
|
const sanitizedName = sanitizeHtml(validatedInput.name);
|
|
676
|
-
const
|
|
1231
|
+
const agent = await this.client.createAgent(
|
|
677
1232
|
sanitizedName,
|
|
678
|
-
validatedInput.
|
|
679
|
-
validatedInput.metadata
|
|
1233
|
+
validatedInput.description
|
|
680
1234
|
);
|
|
681
1235
|
return {
|
|
682
1236
|
content: [
|
|
683
1237
|
{
|
|
684
1238
|
type: "text",
|
|
685
|
-
text: JSON.stringify(
|
|
1239
|
+
text: JSON.stringify(agent, null, 2)
|
|
686
1240
|
}
|
|
687
1241
|
]
|
|
688
1242
|
};
|
|
689
1243
|
}
|
|
690
|
-
case "
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1244
|
+
case "agent_get": {
|
|
1245
|
+
const id = args.id;
|
|
1246
|
+
validateUuid(id, "agent_id");
|
|
1247
|
+
const agent = await this.client.getAgent(id);
|
|
1248
|
+
return {
|
|
1249
|
+
content: [
|
|
1250
|
+
{
|
|
1251
|
+
type: "text",
|
|
1252
|
+
text: JSON.stringify(agent, null, 2)
|
|
1253
|
+
}
|
|
1254
|
+
]
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
case "entity_graph": {
|
|
1258
|
+
const graphSchema = z.object({
|
|
1259
|
+
entity_id: z.string().uuid(),
|
|
1260
|
+
depth: z.number().min(1).max(2).default(1),
|
|
1261
|
+
max_neighbors: z.number().min(1).max(100).default(50)
|
|
695
1262
|
});
|
|
696
|
-
const validatedInput =
|
|
697
|
-
await this.client.
|
|
1263
|
+
const validatedInput = graphSchema.parse(args);
|
|
1264
|
+
const result = await this.client.getEntityNeighborhood(
|
|
698
1265
|
validatedInput.entity_id,
|
|
699
|
-
validatedInput.
|
|
700
|
-
validatedInput.
|
|
1266
|
+
validatedInput.depth,
|
|
1267
|
+
validatedInput.max_neighbors
|
|
701
1268
|
);
|
|
702
1269
|
return {
|
|
703
1270
|
content: [
|
|
704
1271
|
{
|
|
705
1272
|
type: "text",
|
|
706
|
-
text: JSON.stringify(
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1273
|
+
text: JSON.stringify(result, null, 2)
|
|
1274
|
+
}
|
|
1275
|
+
]
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
case "memory_batch_store": {
|
|
1279
|
+
const batchSchema = z.object({
|
|
1280
|
+
memories: z.array(z.object({
|
|
1281
|
+
content: z.string().min(1),
|
|
1282
|
+
metadata: z.record(z.string()).optional()
|
|
1283
|
+
})).min(1).max(100)
|
|
1284
|
+
});
|
|
1285
|
+
const validatedInput = batchSchema.parse(args);
|
|
1286
|
+
const result = await this.client.batchStoreMemories(validatedInput.memories);
|
|
1287
|
+
return {
|
|
1288
|
+
content: [
|
|
1289
|
+
{
|
|
1290
|
+
type: "text",
|
|
1291
|
+
text: JSON.stringify(result, null, 2)
|
|
1292
|
+
}
|
|
1293
|
+
]
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
case "memory_context": {
|
|
1297
|
+
const contextSchema = z.object({
|
|
1298
|
+
query: z.string().min(1),
|
|
1299
|
+
limit: z.number().min(1).max(50).default(10),
|
|
1300
|
+
threshold: z.number().min(0).max(1).default(0.5),
|
|
1301
|
+
max_tokens: z.number().positive().optional()
|
|
1302
|
+
});
|
|
1303
|
+
const validatedInput = contextSchema.parse(args);
|
|
1304
|
+
const result = await this.client.buildContext(
|
|
1305
|
+
validatedInput.query,
|
|
1306
|
+
validatedInput.limit,
|
|
1307
|
+
validatedInput.threshold,
|
|
1308
|
+
validatedInput.max_tokens
|
|
1309
|
+
);
|
|
1310
|
+
return {
|
|
1311
|
+
content: [
|
|
1312
|
+
{
|
|
1313
|
+
type: "text",
|
|
1314
|
+
text: JSON.stringify(result, null, 2)
|
|
717
1315
|
}
|
|
718
1316
|
]
|
|
719
1317
|
};
|
|
720
1318
|
}
|
|
1319
|
+
case "project_register": {
|
|
1320
|
+
const projectSchema = z.object({
|
|
1321
|
+
slug: z.string().min(1).max(100).regex(/^[a-z0-9][a-z0-9-]*$/),
|
|
1322
|
+
name: z.string().min(1).max(255),
|
|
1323
|
+
description: z.string().optional(),
|
|
1324
|
+
stack: z.record(z.unknown()).optional(),
|
|
1325
|
+
repo_url: z.string().max(500).optional()
|
|
1326
|
+
});
|
|
1327
|
+
const validatedInput = projectSchema.parse(args);
|
|
1328
|
+
const project = await this.client.createProject(
|
|
1329
|
+
validatedInput.slug,
|
|
1330
|
+
validatedInput.name,
|
|
1331
|
+
validatedInput.description,
|
|
1332
|
+
validatedInput.stack,
|
|
1333
|
+
validatedInput.repo_url
|
|
1334
|
+
);
|
|
1335
|
+
return {
|
|
1336
|
+
content: [{ type: "text", text: JSON.stringify(project, null, 2) }]
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
case "project_list": {
|
|
1340
|
+
const response = await this.client.listProjects(
|
|
1341
|
+
args.limit
|
|
1342
|
+
);
|
|
1343
|
+
return {
|
|
1344
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
case "project_info": {
|
|
1348
|
+
const slug = args.slug;
|
|
1349
|
+
if (!slug) {
|
|
1350
|
+
throw new Error("Project slug is required");
|
|
1351
|
+
}
|
|
1352
|
+
const project = await this.client.getProject(slug);
|
|
1353
|
+
return {
|
|
1354
|
+
content: [{ type: "text", text: JSON.stringify(project, null, 2) }]
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
// ── Project Relationship Handlers (Issue #186) ──
|
|
1358
|
+
case "project_add_relationship": {
|
|
1359
|
+
const relSchema = z.object({
|
|
1360
|
+
from: z.string().min(1),
|
|
1361
|
+
to: z.string().min(1),
|
|
1362
|
+
type: z.string().min(1).max(100),
|
|
1363
|
+
details: z.record(z.unknown()).optional()
|
|
1364
|
+
});
|
|
1365
|
+
const relArgs = relSchema.parse(args);
|
|
1366
|
+
const relationship = await this.client.addProjectRelationship(
|
|
1367
|
+
relArgs.from,
|
|
1368
|
+
relArgs.to,
|
|
1369
|
+
relArgs.type,
|
|
1370
|
+
relArgs.details
|
|
1371
|
+
);
|
|
1372
|
+
return {
|
|
1373
|
+
content: [{ type: "text", text: JSON.stringify(relationship, null, 2) }]
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
case "project_dependencies": {
|
|
1377
|
+
const depSlug = args.project;
|
|
1378
|
+
if (!depSlug) throw new Error("Project slug is required");
|
|
1379
|
+
const deps = await this.client.getProjectDependencies(depSlug);
|
|
1380
|
+
return {
|
|
1381
|
+
content: [{ type: "text", text: JSON.stringify(deps, null, 2) }]
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
case "project_dependents": {
|
|
1385
|
+
const deptSlug = args.project;
|
|
1386
|
+
if (!deptSlug) throw new Error("Project slug is required");
|
|
1387
|
+
const dependents = await this.client.getProjectDependents(deptSlug);
|
|
1388
|
+
return {
|
|
1389
|
+
content: [{ type: "text", text: JSON.stringify(dependents, null, 2) }]
|
|
1390
|
+
};
|
|
1391
|
+
}
|
|
1392
|
+
case "project_related": {
|
|
1393
|
+
const relatedSlug = args.project;
|
|
1394
|
+
if (!relatedSlug) throw new Error("Project slug is required");
|
|
1395
|
+
const related = await this.client.getProjectRelated(relatedSlug);
|
|
1396
|
+
return {
|
|
1397
|
+
content: [{ type: "text", text: JSON.stringify(related, null, 2) }]
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
case "project_impact": {
|
|
1401
|
+
const impactSchema = z.object({
|
|
1402
|
+
project: z.string().min(1),
|
|
1403
|
+
change_description: z.string().min(1).max(5e3)
|
|
1404
|
+
});
|
|
1405
|
+
const impactArgs = impactSchema.parse(args);
|
|
1406
|
+
const impact = await this.client.projectImpactAnalysis(
|
|
1407
|
+
impactArgs.project,
|
|
1408
|
+
impactArgs.change_description
|
|
1409
|
+
);
|
|
1410
|
+
return {
|
|
1411
|
+
content: [{ type: "text", text: JSON.stringify(impact, null, 2) }]
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
case "project_shared_patterns": {
|
|
1415
|
+
const spSchema = z.object({
|
|
1416
|
+
project_a: z.string().min(1),
|
|
1417
|
+
project_b: z.string().min(1)
|
|
1418
|
+
});
|
|
1419
|
+
const spArgs = spSchema.parse(args);
|
|
1420
|
+
const shared = await this.client.getSharedPatterns(
|
|
1421
|
+
spArgs.project_a,
|
|
1422
|
+
spArgs.project_b
|
|
1423
|
+
);
|
|
1424
|
+
return {
|
|
1425
|
+
content: [{ type: "text", text: JSON.stringify(shared, null, 2) }]
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
721
1428
|
case "memory_health": {
|
|
722
1429
|
const health = await this.client.healthCheck();
|
|
723
1430
|
return {
|
|
@@ -729,13 +1436,203 @@ var MemoryRelayMCPServer = class {
|
|
|
729
1436
|
]
|
|
730
1437
|
};
|
|
731
1438
|
}
|
|
1439
|
+
case "session_start": {
|
|
1440
|
+
const session = await this.client.startSession(
|
|
1441
|
+
args.title,
|
|
1442
|
+
args.project,
|
|
1443
|
+
args.metadata
|
|
1444
|
+
);
|
|
1445
|
+
this.activeSessionId = session.id ?? null;
|
|
1446
|
+
return {
|
|
1447
|
+
content: [
|
|
1448
|
+
{
|
|
1449
|
+
type: "text",
|
|
1450
|
+
text: JSON.stringify(session, null, 2)
|
|
1451
|
+
}
|
|
1452
|
+
]
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
case "session_end": {
|
|
1456
|
+
const id = args.id;
|
|
1457
|
+
validateUuid(id, "session_id");
|
|
1458
|
+
const session = await this.client.endSession(
|
|
1459
|
+
id,
|
|
1460
|
+
args.summary
|
|
1461
|
+
);
|
|
1462
|
+
if (this.activeSessionId === id) {
|
|
1463
|
+
this.activeSessionId = null;
|
|
1464
|
+
}
|
|
1465
|
+
return {
|
|
1466
|
+
content: [
|
|
1467
|
+
{
|
|
1468
|
+
type: "text",
|
|
1469
|
+
text: JSON.stringify(session, null, 2)
|
|
1470
|
+
}
|
|
1471
|
+
]
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
case "session_recall": {
|
|
1475
|
+
const id = args.id;
|
|
1476
|
+
validateUuid(id, "session_id");
|
|
1477
|
+
const session = await this.client.getSession(id);
|
|
1478
|
+
return {
|
|
1479
|
+
content: [
|
|
1480
|
+
{
|
|
1481
|
+
type: "text",
|
|
1482
|
+
text: JSON.stringify(session, null, 2)
|
|
1483
|
+
}
|
|
1484
|
+
]
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
case "session_list": {
|
|
1488
|
+
const response = await this.client.listSessions(
|
|
1489
|
+
args.limit,
|
|
1490
|
+
void 0,
|
|
1491
|
+
args.project,
|
|
1492
|
+
args.status
|
|
1493
|
+
);
|
|
1494
|
+
return {
|
|
1495
|
+
content: [
|
|
1496
|
+
{
|
|
1497
|
+
type: "text",
|
|
1498
|
+
text: JSON.stringify(response, null, 2)
|
|
1499
|
+
}
|
|
1500
|
+
]
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
case "decision_record": {
|
|
1504
|
+
const decision = await this.client.recordDecision(
|
|
1505
|
+
args.title,
|
|
1506
|
+
args.rationale,
|
|
1507
|
+
args.alternatives,
|
|
1508
|
+
args.project,
|
|
1509
|
+
args.tags,
|
|
1510
|
+
args.status
|
|
1511
|
+
);
|
|
1512
|
+
return {
|
|
1513
|
+
content: [{ type: "text", text: JSON.stringify(decision, null, 2) }]
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
case "decision_list": {
|
|
1517
|
+
const response = await this.client.listDecisions(
|
|
1518
|
+
args.limit,
|
|
1519
|
+
args.project,
|
|
1520
|
+
args.status,
|
|
1521
|
+
args.tags
|
|
1522
|
+
);
|
|
1523
|
+
return {
|
|
1524
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
case "decision_supersede": {
|
|
1528
|
+
const id = args.id;
|
|
1529
|
+
validateUuid(id, "decision_id");
|
|
1530
|
+
const decision = await this.client.supersedeDecision(
|
|
1531
|
+
id,
|
|
1532
|
+
args.title,
|
|
1533
|
+
args.rationale,
|
|
1534
|
+
args.alternatives,
|
|
1535
|
+
args.tags
|
|
1536
|
+
);
|
|
1537
|
+
return {
|
|
1538
|
+
content: [{ type: "text", text: JSON.stringify(decision, null, 2) }]
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
case "decision_check": {
|
|
1542
|
+
const response = await this.client.checkDecisions(
|
|
1543
|
+
args.query,
|
|
1544
|
+
args.project,
|
|
1545
|
+
args.limit,
|
|
1546
|
+
args.threshold,
|
|
1547
|
+
args.include_superseded ?? false
|
|
1548
|
+
);
|
|
1549
|
+
return {
|
|
1550
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
case "pattern_create": {
|
|
1554
|
+
const patternSchema = z.object({
|
|
1555
|
+
title: z.string().min(1).max(500),
|
|
1556
|
+
description: z.string().min(1).max(5e4),
|
|
1557
|
+
category: z.string().max(100).optional(),
|
|
1558
|
+
example_code: z.string().optional(),
|
|
1559
|
+
scope: z.enum(["global", "project"]).optional(),
|
|
1560
|
+
tags: z.array(z.string()).max(20).optional(),
|
|
1561
|
+
source_project: z.string().max(100).optional()
|
|
1562
|
+
});
|
|
1563
|
+
const validatedInput = patternSchema.parse(args);
|
|
1564
|
+
const pattern = await this.client.createPattern(
|
|
1565
|
+
validatedInput.title,
|
|
1566
|
+
validatedInput.description,
|
|
1567
|
+
validatedInput.category,
|
|
1568
|
+
validatedInput.example_code,
|
|
1569
|
+
validatedInput.scope,
|
|
1570
|
+
validatedInput.tags,
|
|
1571
|
+
validatedInput.source_project
|
|
1572
|
+
);
|
|
1573
|
+
return {
|
|
1574
|
+
content: [{ type: "text", text: JSON.stringify(pattern, null, 2) }]
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
case "pattern_search": {
|
|
1578
|
+
const response = await this.client.searchPatterns(
|
|
1579
|
+
args.query,
|
|
1580
|
+
args.category,
|
|
1581
|
+
args.project,
|
|
1582
|
+
args.limit,
|
|
1583
|
+
args.threshold
|
|
1584
|
+
);
|
|
1585
|
+
return {
|
|
1586
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
case "pattern_adopt": {
|
|
1590
|
+
const id = args.id;
|
|
1591
|
+
validateUuid(id, "pattern_id");
|
|
1592
|
+
const pattern = await this.client.adoptPattern(
|
|
1593
|
+
id,
|
|
1594
|
+
args.project
|
|
1595
|
+
);
|
|
1596
|
+
return {
|
|
1597
|
+
content: [{ type: "text", text: JSON.stringify(pattern, null, 2) }]
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
case "pattern_suggest": {
|
|
1601
|
+
const response = await this.client.suggestPatterns(
|
|
1602
|
+
args.project,
|
|
1603
|
+
args.limit
|
|
1604
|
+
);
|
|
1605
|
+
return {
|
|
1606
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
case "project_context": {
|
|
1610
|
+
const context = await this.client.getProjectContext(
|
|
1611
|
+
args.project
|
|
1612
|
+
);
|
|
1613
|
+
return {
|
|
1614
|
+
content: [{ type: "text", text: JSON.stringify(context, null, 2) }]
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
case "memory_promote": {
|
|
1618
|
+
const id = args.memory_id;
|
|
1619
|
+
validateUuid(id, "memory_id");
|
|
1620
|
+
const updated = await this.client.promoteMemory(
|
|
1621
|
+
id,
|
|
1622
|
+
args.importance,
|
|
1623
|
+
args.tier
|
|
1624
|
+
);
|
|
1625
|
+
return {
|
|
1626
|
+
content: [{ type: "text", text: JSON.stringify(updated, null, 2) }]
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
732
1629
|
default:
|
|
733
1630
|
throw new Error(`Unknown tool: ${name}`);
|
|
734
1631
|
}
|
|
735
1632
|
} catch (error) {
|
|
736
1633
|
let errorMessage = "Unknown error";
|
|
737
1634
|
let errorDetails = void 0;
|
|
738
|
-
if (error instanceof
|
|
1635
|
+
if (error instanceof z.ZodError) {
|
|
739
1636
|
errorMessage = "Validation error";
|
|
740
1637
|
errorDetails = error.errors;
|
|
741
1638
|
} else if (error instanceof Error) {
|
|
@@ -766,206 +1663,99 @@ var MemoryRelayMCPServer = class {
|
|
|
766
1663
|
});
|
|
767
1664
|
}
|
|
768
1665
|
/**
|
|
769
|
-
*
|
|
1666
|
+
* Start the MCP server with STDIO transport
|
|
770
1667
|
*/
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
uri: "memory:///recent",
|
|
776
|
-
name: "Recent Memories",
|
|
777
|
-
description: "The 20 most recent memories stored in MemoryRelay",
|
|
778
|
-
mimeType: "application/json"
|
|
779
|
-
}
|
|
780
|
-
]
|
|
781
|
-
}));
|
|
782
|
-
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
783
|
-
resourceTemplates: [
|
|
784
|
-
{
|
|
785
|
-
uriTemplate: "memory:///{id}",
|
|
786
|
-
name: "Memory by ID",
|
|
787
|
-
description: "Retrieve a specific memory by its UUID",
|
|
788
|
-
mimeType: "application/json"
|
|
789
|
-
}
|
|
790
|
-
]
|
|
791
|
-
}));
|
|
792
|
-
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
793
|
-
const { uri } = request.params;
|
|
794
|
-
try {
|
|
795
|
-
if (uri === "memory:///recent") {
|
|
796
|
-
const response = await this.client.listMemories(20, 0);
|
|
797
|
-
return {
|
|
798
|
-
contents: [
|
|
799
|
-
{
|
|
800
|
-
uri,
|
|
801
|
-
mimeType: "application/json",
|
|
802
|
-
text: JSON.stringify(response, null, 2)
|
|
803
|
-
}
|
|
804
|
-
]
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
const match = uri.match(/^memory:\/\/\/([0-9a-f-]{36})$/);
|
|
808
|
-
if (match) {
|
|
809
|
-
const id = match[1];
|
|
810
|
-
validateUuid(id, "memory_id");
|
|
811
|
-
const memory = await this.client.getMemory(id);
|
|
812
|
-
return {
|
|
813
|
-
contents: [
|
|
814
|
-
{
|
|
815
|
-
uri,
|
|
816
|
-
mimeType: "application/json",
|
|
817
|
-
text: JSON.stringify(memory, null, 2)
|
|
818
|
-
}
|
|
819
|
-
]
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
throw new Error(`Unknown resource: ${uri}`);
|
|
823
|
-
} catch (error) {
|
|
824
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
825
|
-
this.logger.error(`Resource read failed: ${uri}`, { error: message });
|
|
826
|
-
throw error;
|
|
827
|
-
}
|
|
828
|
-
});
|
|
1668
|
+
async start() {
|
|
1669
|
+
const transport = new StdioServerTransport();
|
|
1670
|
+
await this.server.connect(transport);
|
|
1671
|
+
this.logger.info("MCP server started on STDIO");
|
|
829
1672
|
}
|
|
830
1673
|
/**
|
|
831
|
-
*
|
|
1674
|
+
* Connect the MCP server to a custom transport (for testing)
|
|
832
1675
|
*/
|
|
833
|
-
|
|
834
|
-
this.server.
|
|
835
|
-
prompts: [
|
|
836
|
-
{
|
|
837
|
-
name: "store_memory",
|
|
838
|
-
description: "Store information as a persistent memory with appropriate metadata",
|
|
839
|
-
arguments: [
|
|
840
|
-
{
|
|
841
|
-
name: "information",
|
|
842
|
-
description: "The information to remember",
|
|
843
|
-
required: true
|
|
844
|
-
},
|
|
845
|
-
{
|
|
846
|
-
name: "category",
|
|
847
|
-
description: "Category for the memory (e.g., preference, fact, instruction, context)",
|
|
848
|
-
required: false
|
|
849
|
-
}
|
|
850
|
-
]
|
|
851
|
-
},
|
|
852
|
-
{
|
|
853
|
-
name: "recall_memories",
|
|
854
|
-
description: "Search for and recall relevant memories about a topic",
|
|
855
|
-
arguments: [
|
|
856
|
-
{
|
|
857
|
-
name: "topic",
|
|
858
|
-
description: "The topic or question to search memories for",
|
|
859
|
-
required: true
|
|
860
|
-
}
|
|
861
|
-
]
|
|
862
|
-
},
|
|
863
|
-
{
|
|
864
|
-
name: "summarize_memories",
|
|
865
|
-
description: "List and summarize all recent memories for context",
|
|
866
|
-
arguments: []
|
|
867
|
-
}
|
|
868
|
-
]
|
|
869
|
-
}));
|
|
870
|
-
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
871
|
-
const { name, arguments: args } = request.params;
|
|
872
|
-
switch (name) {
|
|
873
|
-
case "store_memory": {
|
|
874
|
-
const information = args?.information ?? "";
|
|
875
|
-
const category = args?.category ?? "";
|
|
876
|
-
const metadataInstruction = category ? ` Attach metadata with category "${category}".` : " Attach appropriate metadata with a category tag.";
|
|
877
|
-
return {
|
|
878
|
-
messages: [
|
|
879
|
-
{
|
|
880
|
-
role: "user",
|
|
881
|
-
content: {
|
|
882
|
-
type: "text",
|
|
883
|
-
text: `Please store the following information as a persistent memory using the memory_store tool.${metadataInstruction}
|
|
884
|
-
|
|
885
|
-
Information to remember:
|
|
886
|
-
${information}`
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
]
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
case "recall_memories": {
|
|
893
|
-
const topic = args?.topic ?? "";
|
|
894
|
-
return {
|
|
895
|
-
messages: [
|
|
896
|
-
{
|
|
897
|
-
role: "user",
|
|
898
|
-
content: {
|
|
899
|
-
type: "text",
|
|
900
|
-
text: `Search my memories for information related to: "${topic}"
|
|
901
|
-
|
|
902
|
-
Use the memory_search tool to find relevant memories, then summarize what you found. If nothing relevant is found, let me know.`
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
]
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
case "summarize_memories": {
|
|
909
|
-
return {
|
|
910
|
-
messages: [
|
|
911
|
-
{
|
|
912
|
-
role: "user",
|
|
913
|
-
content: {
|
|
914
|
-
type: "text",
|
|
915
|
-
text: "List my recent memories using the memory_list tool and provide a brief summary of what has been remembered. Group them by topic or category if possible."
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
]
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
default:
|
|
922
|
-
throw new Error(`Unknown prompt: ${name}`);
|
|
923
|
-
}
|
|
924
|
-
});
|
|
1676
|
+
async connectTransport(transport) {
|
|
1677
|
+
await this.server.connect(transport);
|
|
925
1678
|
}
|
|
926
1679
|
/**
|
|
927
|
-
*
|
|
1680
|
+
* Close the MCP server connection
|
|
928
1681
|
*/
|
|
929
|
-
async
|
|
930
|
-
|
|
931
|
-
await this.server.connect(transport);
|
|
932
|
-
this.logger.info("MCP server started on STDIO");
|
|
1682
|
+
async close() {
|
|
1683
|
+
await this.server.close();
|
|
933
1684
|
}
|
|
934
1685
|
};
|
|
935
1686
|
|
|
936
1687
|
// src/index.ts
|
|
1688
|
+
async function startServer() {
|
|
1689
|
+
const config = loadConfig();
|
|
1690
|
+
initLogger(config.logLevel);
|
|
1691
|
+
const logger = getLogger();
|
|
1692
|
+
logger.info("Starting MemoryRelay MCP server");
|
|
1693
|
+
const agentId = getAgentId(config);
|
|
1694
|
+
const server = new MemoryRelayMCPServer({
|
|
1695
|
+
apiKey: config.apiKey,
|
|
1696
|
+
apiUrl: config.apiUrl,
|
|
1697
|
+
agentId,
|
|
1698
|
+
timeout: config.timeout
|
|
1699
|
+
});
|
|
1700
|
+
await server.start();
|
|
1701
|
+
process.on("SIGINT", () => {
|
|
1702
|
+
logger.info("Received SIGINT, shutting down gracefully");
|
|
1703
|
+
process.exit(0);
|
|
1704
|
+
});
|
|
1705
|
+
process.on("SIGTERM", () => {
|
|
1706
|
+
logger.info("Received SIGTERM, shutting down gracefully");
|
|
1707
|
+
process.exit(0);
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
937
1710
|
async function main() {
|
|
1711
|
+
const command = process.argv[2];
|
|
938
1712
|
try {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1713
|
+
switch (command) {
|
|
1714
|
+
case "setup": {
|
|
1715
|
+
const { runSetup } = await import("./cli/setup.js");
|
|
1716
|
+
await runSetup();
|
|
1717
|
+
break;
|
|
1718
|
+
}
|
|
1719
|
+
case "test": {
|
|
1720
|
+
const { runTest } = await import("./cli/test.js");
|
|
1721
|
+
await runTest();
|
|
1722
|
+
break;
|
|
1723
|
+
}
|
|
1724
|
+
case "--help":
|
|
1725
|
+
case "-h": {
|
|
1726
|
+
console.error(`
|
|
1727
|
+
memoryrelay-mcp - Persistent memory for AI agents
|
|
1728
|
+
|
|
1729
|
+
Usage:
|
|
1730
|
+
npx memoryrelay-mcp Start MCP server (stdio transport)
|
|
1731
|
+
npx memoryrelay-mcp setup Interactive setup wizard
|
|
1732
|
+
npx memoryrelay-mcp test Test API connectivity
|
|
1733
|
+
npx memoryrelay-mcp --help Show this help message
|
|
1734
|
+
|
|
1735
|
+
Environment variables:
|
|
1736
|
+
MEMORYRELAY_API_KEY API key (required, starts with "mem_")
|
|
1737
|
+
MEMORYRELAY_API_URL API URL (default: https://api.memoryrelay.net)
|
|
1738
|
+
MEMORYRELAY_AGENT_ID Agent ID (optional, auto-detected)
|
|
1739
|
+
MEMORYRELAY_TIMEOUT Request timeout in ms (default: 30000)
|
|
1740
|
+
MEMORYRELAY_LOG_LEVEL Log level: debug|info|warn|error (default: info)
|
|
1741
|
+
|
|
1742
|
+
Documentation: https://github.com/memoryrelay/mcp-server
|
|
1743
|
+
`);
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
default:
|
|
1747
|
+
await startServer();
|
|
1748
|
+
}
|
|
959
1749
|
} catch (error) {
|
|
960
|
-
const
|
|
1750
|
+
const logger = getLogger();
|
|
961
1751
|
if (error instanceof Error) {
|
|
962
|
-
|
|
963
|
-
console.error("\
|
|
1752
|
+
logger.error("Fatal error:", { message: error.message });
|
|
1753
|
+
console.error("\nFailed to start MemoryRelay MCP server\n");
|
|
964
1754
|
console.error(error.message);
|
|
965
|
-
console.error("\nFor help,
|
|
1755
|
+
console.error("\nFor help, run: npx memoryrelay-mcp --help\n");
|
|
966
1756
|
} else {
|
|
967
|
-
|
|
968
|
-
console.error("\
|
|
1757
|
+
logger.error("Fatal error:", { error });
|
|
1758
|
+
console.error("\nAn unexpected error occurred\n");
|
|
969
1759
|
}
|
|
970
1760
|
process.exit(1);
|
|
971
1761
|
}
|