@memoryrelay/mcp-server 0.1.6

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/dist/index.js ADDED
@@ -0,0 +1,804 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config.ts
4
+ import { z } from "zod";
5
+ var configSchema = z.object({
6
+ apiKey: z.string().startsWith("mem_", { message: 'API key must start with "mem_"' }).min(20, { message: "API key appears to be invalid (too short)" }),
7
+ apiUrl: z.string().url({ message: "API URL must be a valid URL" }).default("https://api.memoryrelay.net"),
8
+ agentId: z.string().optional().describe("Agent identifier - auto-detected if not provided"),
9
+ timeout: z.number().positive({ message: "Timeout must be positive" }).default(3e4),
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
+ const hostname = process.env.HOSTNAME || "unknown";
47
+ return `agent-${hostname.slice(0, 8)}`;
48
+ }
49
+
50
+ // src/logger.ts
51
+ var LOG_LEVELS = {
52
+ debug: 0,
53
+ info: 1,
54
+ warn: 2,
55
+ error: 3
56
+ };
57
+ var Logger = class {
58
+ minLevel;
59
+ constructor(level = "info") {
60
+ this.minLevel = LOG_LEVELS[level];
61
+ }
62
+ /**
63
+ * Mask sensitive data in log messages
64
+ * - API keys starting with "mem_" are masked
65
+ * - Internal paths are sanitized
66
+ */
67
+ sanitize(message) {
68
+ let sanitized = message;
69
+ sanitized = sanitized.replace(/mem_[a-zA-Z0-9_-]+/g, "mem_****");
70
+ sanitized = sanitized.replace(/\/[a-zA-Z0-9_\-./]+\.(ts|js|json)/g, "<file>");
71
+ sanitized = sanitized.replace(/at\s+[^\s]+\s+\([^)]+\)/g, "at <location>");
72
+ return sanitized;
73
+ }
74
+ /**
75
+ * Format log message with timestamp and level
76
+ */
77
+ format(level, message, data) {
78
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
79
+ const sanitizedMessage = this.sanitize(message);
80
+ let output = `[${timestamp}] [${level.toUpperCase()}] ${sanitizedMessage}`;
81
+ if (data !== void 0) {
82
+ const sanitizedData = this.sanitize(JSON.stringify(data, null, 2));
83
+ output += `
84
+ ${sanitizedData}`;
85
+ }
86
+ return output;
87
+ }
88
+ debug(message, data) {
89
+ if (this.minLevel <= LOG_LEVELS.debug) {
90
+ console.error(this.format("debug", message, data));
91
+ }
92
+ }
93
+ info(message, data) {
94
+ if (this.minLevel <= LOG_LEVELS.info) {
95
+ console.error(this.format("info", message, data));
96
+ }
97
+ }
98
+ warn(message, data) {
99
+ if (this.minLevel <= LOG_LEVELS.warn) {
100
+ console.error(this.format("warn", message, data));
101
+ }
102
+ }
103
+ error(message, data) {
104
+ if (this.minLevel <= LOG_LEVELS.error) {
105
+ console.error(this.format("error", message, data));
106
+ }
107
+ }
108
+ };
109
+ var logger;
110
+ function initLogger(level = "info") {
111
+ logger = new Logger(level);
112
+ return logger;
113
+ }
114
+ function getLogger() {
115
+ if (!logger) {
116
+ logger = new Logger();
117
+ }
118
+ return logger;
119
+ }
120
+
121
+ // src/server.ts
122
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
123
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
124
+ import {
125
+ CallToolRequestSchema,
126
+ ListToolsRequestSchema
127
+ } from "@modelcontextprotocol/sdk/types.js";
128
+ import { z as z2 } from "zod";
129
+
130
+ // src/client.ts
131
+ var MAX_RETRIES = 3;
132
+ var INITIAL_DELAY_MS = 1e3;
133
+ var MAX_CONTENT_SIZE = 50 * 1024;
134
+ async function withRetry(fn, retries = MAX_RETRIES) {
135
+ let lastError;
136
+ for (let attempt = 0; attempt <= retries; attempt++) {
137
+ try {
138
+ return await fn();
139
+ } catch (error) {
140
+ lastError = error instanceof Error ? error : new Error(String(error));
141
+ if (lastError.message.includes("401") || lastError.message.includes("403") || lastError.message.includes("404") || lastError.message.includes("400")) {
142
+ throw lastError;
143
+ }
144
+ if (attempt === retries) {
145
+ throw lastError;
146
+ }
147
+ const delay = INITIAL_DELAY_MS * Math.pow(2, attempt);
148
+ const jitter = Math.random() * 0.3 * delay;
149
+ await new Promise((resolve) => setTimeout(resolve, delay + jitter));
150
+ }
151
+ }
152
+ throw lastError || new Error("Retry failed");
153
+ }
154
+ function maskApiKey(message, apiKey) {
155
+ if (!apiKey) return message;
156
+ const maskedKey = apiKey.substring(0, 8) + "***";
157
+ return message.replace(new RegExp(apiKey, "g"), maskedKey);
158
+ }
159
+ var MemoryRelayClient = class {
160
+ config;
161
+ logger = getLogger();
162
+ constructor(config) {
163
+ this.config = config;
164
+ this.logger.info("MemoryRelay client initialized", {
165
+ apiUrl: config.apiUrl,
166
+ agentId: config.agentId
167
+ });
168
+ }
169
+ /**
170
+ * Make authenticated HTTP request to MemoryRelay API with retry logic
171
+ */
172
+ async request(method, path, body) {
173
+ return withRetry(async () => {
174
+ const url = `${this.config.apiUrl}${path}`;
175
+ this.logger.debug(`API request: ${method} ${path}`);
176
+ const controller = new AbortController();
177
+ const timeout = setTimeout(() => controller.abort(), this.config.timeout);
178
+ try {
179
+ const response = await fetch(url, {
180
+ method,
181
+ headers: {
182
+ "Content-Type": "application/json",
183
+ "Authorization": `Bearer ${this.config.apiKey}`,
184
+ "User-Agent": "@memoryrelay/mcp-server"
185
+ },
186
+ body: body ? JSON.stringify(body) : void 0,
187
+ signal: controller.signal
188
+ });
189
+ if (!response.ok) {
190
+ if (response.status === 429) {
191
+ const retryAfter = response.headers.get("Retry-After");
192
+ const waitMs = retryAfter ? parseInt(retryAfter) * 1e3 : 5e3;
193
+ this.logger.warn(`Rate limited, waiting ${waitMs}ms`);
194
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
195
+ throw new Error(`Rate limited: 429 - Retry after ${waitMs}ms`);
196
+ }
197
+ const errorData = await response.json().catch(() => ({}));
198
+ const errorMsg = `API request failed: ${response.status} ${response.statusText}` + (errorData.message ? ` - ${errorData.message}` : "");
199
+ throw new Error(maskApiKey(errorMsg, this.config.apiKey));
200
+ }
201
+ const data = await response.json();
202
+ this.logger.debug(`API response: ${method} ${path}`, { status: response.status });
203
+ return data;
204
+ } catch (error) {
205
+ if (error instanceof Error) {
206
+ if (error.name === "AbortError") {
207
+ throw new Error(`Request timeout after ${this.config.timeout}ms`);
208
+ }
209
+ error.message = maskApiKey(error.message, this.config.apiKey);
210
+ }
211
+ throw error;
212
+ } finally {
213
+ clearTimeout(timeout);
214
+ }
215
+ });
216
+ }
217
+ /**
218
+ * Validate content size
219
+ */
220
+ validateContentSize(content) {
221
+ if (content.length > MAX_CONTENT_SIZE) {
222
+ throw new Error(`Content exceeds maximum size of ${MAX_CONTENT_SIZE} bytes`);
223
+ }
224
+ }
225
+ /**
226
+ * Store a new memory
227
+ */
228
+ async storeMemory(content, metadata) {
229
+ this.validateContentSize(content);
230
+ return this.request("POST", "/v1/memories/memories", {
231
+ content,
232
+ metadata,
233
+ agent_id: this.config.agentId
234
+ });
235
+ }
236
+ /**
237
+ * Search memories using semantic search
238
+ */
239
+ async searchMemories(query, limit = 10, threshold = 0.5) {
240
+ this.validateContentSize(query);
241
+ const response = await this.request(
242
+ "POST",
243
+ "/v1/memories/memories/search",
244
+ { query, limit, threshold, agent_id: this.config.agentId }
245
+ );
246
+ return response.data;
247
+ }
248
+ /**
249
+ * List recent memories with pagination
250
+ */
251
+ async listMemories(limit = 20, offset = 0) {
252
+ return this.request(
253
+ "GET",
254
+ `/v1/memories/memories?limit=${limit}&offset=${offset}`
255
+ );
256
+ }
257
+ /**
258
+ * Get a specific memory by ID
259
+ */
260
+ async getMemory(id) {
261
+ return this.request("GET", `/v1/memories/memories/${id}`);
262
+ }
263
+ /**
264
+ * Update an existing memory
265
+ */
266
+ async updateMemory(id, content, metadata) {
267
+ this.validateContentSize(content);
268
+ return this.request("PATCH", `/v1/memories/memories/${id}`, {
269
+ content,
270
+ metadata
271
+ });
272
+ }
273
+ /**
274
+ * Delete a memory
275
+ */
276
+ async deleteMemory(id) {
277
+ await this.request("DELETE", `/v1/memories/memories/${id}`);
278
+ }
279
+ /**
280
+ * Create a named entity
281
+ */
282
+ async createEntity(name, type, metadata) {
283
+ this.validateContentSize(name);
284
+ return this.request("POST", "/v1/entities", {
285
+ name,
286
+ type,
287
+ metadata
288
+ });
289
+ }
290
+ /**
291
+ * Link an entity to a memory
292
+ */
293
+ async linkEntity(entityId, memoryId, relationship = "mentioned_in") {
294
+ await this.request("POST", "/v1/entities/links", {
295
+ entity_id: entityId,
296
+ memory_id: memoryId,
297
+ relationship
298
+ });
299
+ }
300
+ /**
301
+ * Get an entity by ID
302
+ */
303
+ async getEntity(id) {
304
+ return this.request("GET", `/v1/entities/${id}`);
305
+ }
306
+ /**
307
+ * List entities with pagination
308
+ */
309
+ async listEntities(limit = 20, offset = 0) {
310
+ return this.request(
311
+ "GET",
312
+ `/v1/entities?limit=${limit}&offset=${offset}`
313
+ );
314
+ }
315
+ /**
316
+ * Delete an entity
317
+ */
318
+ async deleteEntity(id) {
319
+ await this.request("DELETE", `/v1/entities/${id}`);
320
+ }
321
+ /**
322
+ * Health check - verify API connectivity
323
+ */
324
+ async healthCheck() {
325
+ try {
326
+ await this.request("GET", "/v1/health");
327
+ return {
328
+ status: "healthy",
329
+ message: "API connection successful"
330
+ };
331
+ } catch (error) {
332
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
333
+ return {
334
+ status: "unhealthy",
335
+ message: `API connection failed: ${errorMsg}`
336
+ };
337
+ }
338
+ }
339
+ };
340
+
341
+ // src/server.ts
342
+ function sanitizeHtml(str) {
343
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;").replace(/\//g, "&#x2F;");
344
+ }
345
+ var uuidSchema = z2.string().uuid();
346
+ function validateUuid(id, fieldName = "id") {
347
+ const result = uuidSchema.safeParse(id);
348
+ if (!result.success) {
349
+ throw new Error(`Invalid ${fieldName}: must be a valid UUID`);
350
+ }
351
+ }
352
+ var MemoryRelayMCPServer = class {
353
+ server;
354
+ client;
355
+ logger = getLogger();
356
+ constructor(config) {
357
+ this.client = new MemoryRelayClient(config);
358
+ this.server = new Server(
359
+ {
360
+ name: "@memoryrelay/mcp-server",
361
+ version: "0.1.0"
362
+ },
363
+ {
364
+ capabilities: {
365
+ tools: {}
366
+ }
367
+ }
368
+ );
369
+ this.setupHandlers();
370
+ this.logger.info("MCP server initialized");
371
+ }
372
+ /**
373
+ * Setup MCP protocol handlers
374
+ */
375
+ setupHandlers() {
376
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
377
+ tools: [
378
+ {
379
+ name: "memory_store",
380
+ description: "Store a new memory. Use this to save important information, facts, preferences, or context that should be remembered for future conversations.",
381
+ inputSchema: {
382
+ type: "object",
383
+ properties: {
384
+ content: {
385
+ type: "string",
386
+ description: "The memory content to store. Be specific and include relevant context."
387
+ },
388
+ metadata: {
389
+ type: "object",
390
+ description: "Optional key-value metadata to attach to the memory",
391
+ additionalProperties: { type: "string" }
392
+ }
393
+ },
394
+ required: ["content"]
395
+ }
396
+ },
397
+ {
398
+ name: "memory_search",
399
+ description: "Search memories using natural language. Returns the most relevant memories based on semantic similarity to the query.",
400
+ inputSchema: {
401
+ type: "object",
402
+ properties: {
403
+ query: {
404
+ type: "string",
405
+ description: "Natural language search query"
406
+ },
407
+ limit: {
408
+ type: "number",
409
+ description: "Maximum number of results to return (1-50)",
410
+ minimum: 1,
411
+ maximum: 50,
412
+ default: 10
413
+ },
414
+ threshold: {
415
+ type: "number",
416
+ description: "Minimum similarity threshold (0-1)",
417
+ minimum: 0,
418
+ maximum: 1,
419
+ default: 0.5
420
+ }
421
+ },
422
+ required: ["query"]
423
+ }
424
+ },
425
+ {
426
+ name: "memory_list",
427
+ description: "List recent memories chronologically. Use to review what has been remembered.",
428
+ inputSchema: {
429
+ type: "object",
430
+ properties: {
431
+ limit: {
432
+ type: "number",
433
+ description: "Number of memories to return (1-100)",
434
+ minimum: 1,
435
+ maximum: 100,
436
+ default: 20
437
+ },
438
+ offset: {
439
+ type: "number",
440
+ description: "Offset for pagination",
441
+ minimum: 0,
442
+ default: 0
443
+ }
444
+ }
445
+ }
446
+ },
447
+ {
448
+ name: "memory_get",
449
+ description: "Retrieve a specific memory by its ID.",
450
+ inputSchema: {
451
+ type: "object",
452
+ properties: {
453
+ id: {
454
+ type: "string",
455
+ description: "The memory ID (UUID) to retrieve"
456
+ }
457
+ },
458
+ required: ["id"]
459
+ }
460
+ },
461
+ {
462
+ name: "memory_update",
463
+ description: "Update the content of an existing memory. Use to correct or expand stored information.",
464
+ inputSchema: {
465
+ type: "object",
466
+ properties: {
467
+ id: {
468
+ type: "string",
469
+ description: "The memory ID (UUID) to update"
470
+ },
471
+ content: {
472
+ type: "string",
473
+ description: "The new content to replace the existing memory"
474
+ },
475
+ metadata: {
476
+ type: "object",
477
+ description: "Updated metadata (replaces existing)",
478
+ additionalProperties: { type: "string" }
479
+ }
480
+ },
481
+ required: ["id", "content"]
482
+ }
483
+ },
484
+ {
485
+ name: "memory_delete",
486
+ description: "Permanently delete a memory. Use sparingly - memories are valuable context.",
487
+ inputSchema: {
488
+ type: "object",
489
+ properties: {
490
+ id: {
491
+ type: "string",
492
+ description: "The memory ID (UUID) to delete"
493
+ }
494
+ },
495
+ required: ["id"]
496
+ }
497
+ },
498
+ {
499
+ name: "entity_create",
500
+ description: "Create a named entity (person, place, organization, project, concept) for the knowledge graph. Entities help organize and connect memories.",
501
+ inputSchema: {
502
+ type: "object",
503
+ properties: {
504
+ name: {
505
+ type: "string",
506
+ minLength: 1,
507
+ maxLength: 200,
508
+ description: "Entity name (1-200 characters)"
509
+ },
510
+ type: {
511
+ type: "string",
512
+ enum: ["person", "place", "organization", "project", "concept", "other"],
513
+ description: "Entity type classification"
514
+ },
515
+ metadata: {
516
+ type: "object",
517
+ description: "Optional key-value metadata",
518
+ additionalProperties: { type: "string" }
519
+ }
520
+ },
521
+ required: ["name", "type"]
522
+ }
523
+ },
524
+ {
525
+ name: "entity_link",
526
+ description: "Link an entity to a memory to establish relationships in the knowledge graph.",
527
+ inputSchema: {
528
+ type: "object",
529
+ properties: {
530
+ entity_id: {
531
+ type: "string",
532
+ description: "Entity UUID"
533
+ },
534
+ memory_id: {
535
+ type: "string",
536
+ description: "Memory UUID"
537
+ },
538
+ relationship: {
539
+ type: "string",
540
+ description: 'Relationship type (e.g., "mentioned_in", "created_by", "relates_to")',
541
+ default: "mentioned_in"
542
+ }
543
+ },
544
+ required: ["entity_id", "memory_id"]
545
+ }
546
+ },
547
+ {
548
+ name: "memory_health",
549
+ description: "Check API connectivity and health status.",
550
+ inputSchema: {
551
+ type: "object",
552
+ properties: {}
553
+ }
554
+ }
555
+ ]
556
+ }));
557
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
558
+ const { name, arguments: args } = request.params;
559
+ this.logger.debug(`Tool called: ${name}`, args);
560
+ try {
561
+ switch (name) {
562
+ case "memory_store": {
563
+ const memory = await this.client.storeMemory(
564
+ args.content,
565
+ args.metadata
566
+ );
567
+ return {
568
+ content: [
569
+ {
570
+ type: "text",
571
+ text: JSON.stringify(memory, null, 2)
572
+ }
573
+ ]
574
+ };
575
+ }
576
+ case "memory_search": {
577
+ const results = await this.client.searchMemories(
578
+ args.query,
579
+ args.limit,
580
+ args.threshold
581
+ );
582
+ return {
583
+ content: [
584
+ {
585
+ type: "text",
586
+ text: JSON.stringify(
587
+ { memories: results, total: results.length },
588
+ null,
589
+ 2
590
+ )
591
+ }
592
+ ]
593
+ };
594
+ }
595
+ case "memory_list": {
596
+ const response = await this.client.listMemories(
597
+ args.limit,
598
+ args.offset
599
+ );
600
+ return {
601
+ content: [
602
+ {
603
+ type: "text",
604
+ text: JSON.stringify(response, null, 2)
605
+ }
606
+ ]
607
+ };
608
+ }
609
+ case "memory_get": {
610
+ const id = args.id;
611
+ validateUuid(id, "memory_id");
612
+ const memory = await this.client.getMemory(id);
613
+ return {
614
+ content: [
615
+ {
616
+ type: "text",
617
+ text: JSON.stringify(memory, null, 2)
618
+ }
619
+ ]
620
+ };
621
+ }
622
+ case "memory_update": {
623
+ const id = args.id;
624
+ validateUuid(id, "memory_id");
625
+ const memory = await this.client.updateMemory(
626
+ id,
627
+ args.content,
628
+ args.metadata
629
+ );
630
+ return {
631
+ content: [
632
+ {
633
+ type: "text",
634
+ text: JSON.stringify(memory, null, 2)
635
+ }
636
+ ]
637
+ };
638
+ }
639
+ case "memory_delete": {
640
+ const id = args.id;
641
+ validateUuid(id, "memory_id");
642
+ await this.client.deleteMemory(id);
643
+ return {
644
+ content: [
645
+ {
646
+ type: "text",
647
+ text: JSON.stringify(
648
+ { success: true, message: "Memory deleted successfully" },
649
+ null,
650
+ 2
651
+ )
652
+ }
653
+ ]
654
+ };
655
+ }
656
+ case "entity_create": {
657
+ const entitySchema = z2.object({
658
+ name: z2.string().min(1).max(200),
659
+ type: z2.enum(["person", "place", "organization", "project", "concept", "other"]),
660
+ metadata: z2.record(z2.string()).optional()
661
+ });
662
+ const validatedInput = entitySchema.parse(args);
663
+ const sanitizedName = sanitizeHtml(validatedInput.name);
664
+ const entity = await this.client.createEntity(
665
+ sanitizedName,
666
+ validatedInput.type,
667
+ validatedInput.metadata
668
+ );
669
+ return {
670
+ content: [
671
+ {
672
+ type: "text",
673
+ text: JSON.stringify(entity, null, 2)
674
+ }
675
+ ]
676
+ };
677
+ }
678
+ case "entity_link": {
679
+ const linkSchema = z2.object({
680
+ entity_id: z2.string().uuid(),
681
+ memory_id: z2.string().uuid(),
682
+ relationship: z2.string().default("mentioned_in")
683
+ });
684
+ const validatedInput = linkSchema.parse(args);
685
+ await this.client.linkEntity(
686
+ validatedInput.entity_id,
687
+ validatedInput.memory_id,
688
+ validatedInput.relationship
689
+ );
690
+ return {
691
+ content: [
692
+ {
693
+ type: "text",
694
+ text: JSON.stringify(
695
+ {
696
+ success: true,
697
+ message: "Entity linked to memory successfully",
698
+ entity_id: validatedInput.entity_id,
699
+ memory_id: validatedInput.memory_id,
700
+ relationship: validatedInput.relationship
701
+ },
702
+ null,
703
+ 2
704
+ )
705
+ }
706
+ ]
707
+ };
708
+ }
709
+ case "memory_health": {
710
+ const health = await this.client.healthCheck();
711
+ return {
712
+ content: [
713
+ {
714
+ type: "text",
715
+ text: JSON.stringify(health, null, 2)
716
+ }
717
+ ]
718
+ };
719
+ }
720
+ default:
721
+ throw new Error(`Unknown tool: ${name}`);
722
+ }
723
+ } catch (error) {
724
+ let errorMessage = "Unknown error";
725
+ let errorDetails = void 0;
726
+ if (error instanceof z2.ZodError) {
727
+ errorMessage = "Validation error";
728
+ errorDetails = error.errors;
729
+ } else if (error instanceof Error) {
730
+ errorMessage = error.message;
731
+ }
732
+ this.logger.error(`Tool execution failed: ${name}`, {
733
+ error: errorMessage,
734
+ details: errorDetails
735
+ });
736
+ return {
737
+ content: [
738
+ {
739
+ type: "text",
740
+ text: JSON.stringify(
741
+ {
742
+ error: "Tool execution failed",
743
+ message: errorMessage,
744
+ details: errorDetails
745
+ },
746
+ null,
747
+ 2
748
+ )
749
+ }
750
+ ],
751
+ isError: true
752
+ };
753
+ }
754
+ });
755
+ }
756
+ /**
757
+ * Start the MCP server with STDIO transport
758
+ */
759
+ async start() {
760
+ const transport = new StdioServerTransport();
761
+ await this.server.connect(transport);
762
+ this.logger.info("MCP server started on STDIO");
763
+ }
764
+ };
765
+
766
+ // src/index.ts
767
+ async function main() {
768
+ try {
769
+ const config = loadConfig();
770
+ initLogger(config.logLevel);
771
+ const logger2 = getLogger();
772
+ logger2.info("Starting MemoryRelay MCP server");
773
+ const agentId = getAgentId(config);
774
+ const server = new MemoryRelayMCPServer({
775
+ apiKey: config.apiKey,
776
+ apiUrl: config.apiUrl,
777
+ agentId,
778
+ timeout: config.timeout
779
+ });
780
+ await server.start();
781
+ process.on("SIGINT", () => {
782
+ logger2.info("Received SIGINT, shutting down gracefully");
783
+ process.exit(0);
784
+ });
785
+ process.on("SIGTERM", () => {
786
+ logger2.info("Received SIGTERM, shutting down gracefully");
787
+ process.exit(0);
788
+ });
789
+ } catch (error) {
790
+ const logger2 = getLogger();
791
+ if (error instanceof Error) {
792
+ logger2.error("Fatal error:", { message: error.message });
793
+ console.error("\n\u274C Failed to start MemoryRelay MCP server\n");
794
+ console.error(error.message);
795
+ console.error("\nFor help, see: https://github.com/Alteriom/ai-memory-service/tree/main/mcp\n");
796
+ } else {
797
+ logger2.error("Fatal error:", { error });
798
+ console.error("\n\u274C An unexpected error occurred\n");
799
+ }
800
+ process.exit(1);
801
+ }
802
+ }
803
+ main();
804
+ //# sourceMappingURL=index.js.map