@postnesia/mcp 0.1.14 → 0.1.15

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 CHANGED
@@ -3,28 +3,28 @@
3
3
  * MCP Server for Postnesia Memory System
4
4
  * Exposes memory operations as Model Context Protocol tools
5
5
  */
6
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
- import { z } from "zod";
9
- import { getDb, closeDb, createMemory } from "@postnesia/db";
10
- import queries from "@postnesia/db/queries";
11
- import { embed } from "@postnesia/db/embeddings";
12
- import { logAccess } from "@postnesia/db/access";
13
- import { runConsolidation } from "@postnesia/db/importance";
6
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
+ import { z } from 'zod';
9
+ import { getDb, closeDb, createMemory } from '@postnesia/db';
10
+ import queries from '@postnesia/db/queries';
11
+ import { embed } from '@postnesia/db/embeddings';
12
+ import { logAccess } from '@postnesia/db/access';
13
+ import { runConsolidation } from '@postnesia/db/importance';
14
14
  const db = getDb();
15
15
  const server = new McpServer({
16
- name: "postnesia",
17
- version: "1.0.0",
16
+ name: 'postnesia',
17
+ version: '1.0.0',
18
18
  });
19
19
  // ---------------------------------------------------------------------------
20
20
  // memory tools
21
21
  // ---------------------------------------------------------------------------
22
- server.registerTool("memory_search", {
23
- description: "Search memories by semantic similarity. Optionally filter by type.",
22
+ server.registerTool('memory_search', {
23
+ description: 'Search memories by semantic similarity. Optionally filter by type.',
24
24
  inputSchema: {
25
- query: z.string().describe("Search query"),
26
- limit: z.number().optional().describe("Maximum results to return (default: 10)"),
27
- type: z.enum(["event", "decision", "lesson", "preference", "person", "technical"]).optional().describe("Filter by memory type"),
25
+ query: z.string().describe('Search query'),
26
+ limit: z.number().optional().describe('Maximum results to return (default: 10)'),
27
+ type: z.enum(['event', 'decision', 'lesson', 'preference', 'person', 'technical']).optional().describe('Filter by memory type'),
28
28
  },
29
29
  }, async ({ query, limit = 10, type }) => {
30
30
  const [embedding] = await embed(query);
@@ -36,86 +36,86 @@ server.registerTool("memory_search", {
36
36
  ? queries.vectorSearchByType(db).all(embeddingBuffer, limit, type)
37
37
  : queries.vectorSearch(db).all(embeddingBuffer, limit);
38
38
  for (const result of results) {
39
- logAccess(result.id, "search");
39
+ logAccess(result.id, 'search');
40
40
  }
41
41
  return {
42
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
42
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
43
43
  };
44
44
  });
45
- server.registerTool("memory_search_keyword", {
46
- description: "Keyword search memories by content (use when semantic search is not appropriate).",
45
+ server.registerTool('memory_search_keyword', {
46
+ description: 'Keyword search memories by content (use when semantic search is not appropriate).',
47
47
  inputSchema: {
48
- query: z.string().describe("Keyword or phrase to search for"),
49
- limit: z.number().optional().describe("Maximum results to return (default: 10)"),
48
+ query: z.string().describe('Keyword or phrase to search for'),
49
+ limit: z.number().optional().describe('Maximum results to return (default: 10)'),
50
50
  },
51
51
  }, async ({ query, limit = 10 }) => {
52
52
  const pattern = `%${query}%`;
53
53
  const results = queries.searchMemories(db).all(pattern, pattern, limit);
54
54
  for (const result of results) {
55
- logAccess(result.id, "search");
55
+ logAccess(result.id, 'search');
56
56
  }
57
57
  return {
58
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
58
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
59
59
  };
60
60
  });
61
- server.registerTool("memory_by_context", {
62
- description: "Find memories by context string (LIKE match).",
61
+ server.registerTool('memory_by_context', {
62
+ description: 'Find memories by context string (LIKE match).',
63
63
  inputSchema: {
64
- context: z.string().describe("Context string to search for"),
65
- limit: z.number().optional().describe("Maximum results (default: 10)"),
64
+ context: z.string().describe('Context string to search for'),
65
+ limit: z.number().optional().describe('Maximum results (default: 10)'),
66
66
  },
67
67
  }, async ({ context, limit = 10 }) => {
68
68
  const results = queries.getMemoriesByContext(db).all(`%${context}%`, limit);
69
69
  for (const r of results)
70
- logAccess(r.id, "search");
70
+ logAccess(r.id, 'search');
71
71
  return {
72
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
72
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
73
73
  };
74
74
  });
75
- server.registerTool("memory_by_tag", {
76
- description: "Find memories by tag (exact match).",
75
+ server.registerTool('memory_by_tag', {
76
+ description: 'Find memories by tag (exact match).',
77
77
  inputSchema: {
78
- tag: z.string().describe("Tag to filter by"),
79
- limit: z.number().optional().describe("Maximum results (default: 10)"),
78
+ tag: z.string().describe('Tag to filter by'),
79
+ limit: z.number().optional().describe('Maximum results (default: 10)'),
80
80
  },
81
81
  }, async ({ tag, limit = 10 }) => {
82
82
  const results = queries.getMemoriesByTag(db).all(tag, limit);
83
83
  for (const r of results)
84
- logAccess(r.id, "search");
84
+ logAccess(r.id, 'search');
85
85
  return {
86
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
86
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
87
87
  };
88
88
  });
89
- server.registerTool("memory_supersede_chain", {
90
- description: "Trace the supersede chain for a memory (shows the full replacement history).",
89
+ server.registerTool('memory_supersede_chain', {
90
+ description: 'Trace the supersede chain for a memory (shows the full replacement history).',
91
91
  inputSchema: {
92
- memoryId: z.number().describe("Memory ID to trace"),
92
+ memoryId: z.number().describe('Memory ID to trace'),
93
93
  },
94
94
  }, async ({ memoryId }) => {
95
95
  const chain = queries.getSupersedeChain(db).all(memoryId);
96
96
  return {
97
- content: [{ type: "text", text: JSON.stringify(chain, null, 2) }],
97
+ content: [{ type: 'text', text: JSON.stringify(chain, null, 2) }],
98
98
  };
99
99
  });
100
- server.registerTool("memory_core", {
101
- description: "Get all core memories (foundational memories that are always loaded).",
100
+ server.registerTool('memory_core', {
101
+ description: 'Get all core memories (foundational memories that are always loaded).',
102
102
  inputSchema: {},
103
103
  }, async () => {
104
104
  const results = queries.coreMemories(db).all();
105
105
  return {
106
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
106
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
107
107
  };
108
108
  });
109
- server.registerTool("memory_add", {
110
- description: "Add a new memory to the database",
109
+ server.registerTool('memory_add', {
110
+ description: 'Add a new memory to the database',
111
111
  inputSchema: {
112
- content: z.string().describe("Memory content (full form)"),
113
- contentL1: z.string().optional().describe("Compressed L1 form (optional, will auto-generate if omitted)"),
114
- type: z.enum(["event", "decision", "lesson", "preference", "person", "technical"]).describe("Memory type"),
115
- importance: z.number().describe("Base importance 1-5"),
116
- tags: z.array(z.string()).describe("Tags for categorization"),
117
- context: z.string().optional().describe("Optional context about when/why this memory was created"),
118
- core: z.boolean().optional().describe("Mark as a core memory (always loaded, never decays, cannot be superseded)"),
112
+ content: z.string().describe('Memory content (full form)'),
113
+ contentL1: z.string().optional().describe('Compressed L1 form (optional, will auto-generate if omitted)'),
114
+ type: z.enum(['event', 'decision', 'lesson', 'preference', 'person', 'technical']).describe('Memory type'),
115
+ importance: z.number().describe('Base importance 1-5'),
116
+ tags: z.array(z.string()).describe('Tags for categorization'),
117
+ context: z.string().optional().describe('Optional context about when/why this memory was created'),
118
+ core: z.boolean().optional().describe('Mark as a core memory (always loaded, never decays, cannot be superseded)'),
119
119
  },
120
120
  }, async ({ content, contentL1, type, importance, tags, context, core }) => {
121
121
  const content_l1 = contentL1 || content.slice(0, 200);
@@ -137,21 +137,21 @@ server.registerTool("memory_add", {
137
137
  return {
138
138
  content: [
139
139
  {
140
- type: "text",
141
- text: `Created memory #${id}\n Type: ${type}\n Core: ${core ? "yes" : "no"}\n Importance: ${importance}/5\n Tags: ${tags.join(", ")}`,
140
+ type: 'text',
141
+ text: `Created memory #${id}\n Type: ${type}\n Core: ${core ? 'yes' : 'no'}\n Importance: ${importance}/5\n Tags: ${tags.join(', ')}`,
142
142
  },
143
143
  ],
144
144
  };
145
145
  });
146
- server.registerTool("memory_update_core", {
147
- description: "Update the content of an existing core memory in place (core memories must be updated, never superseded)",
146
+ server.registerTool('memory_update_core', {
147
+ description: 'Update the content of an existing core memory in place (core memories must be updated, never superseded)',
148
148
  inputSchema: {
149
- memoryId: z.number().describe("ID of the memory to update"),
150
- content: z.string().describe("New full content"),
151
- contentL1: z.string().describe("New compressed L1 summary"),
149
+ memoryId: z.number().describe('ID of the memory to update'),
150
+ content: z.string().describe('New full content'),
151
+ contentL1: z.string().describe('New compressed L1 summary'),
152
152
  },
153
153
  }, async ({ memoryId, content, contentL1 }) => {
154
- const existing = db.prepare("SELECT id, core FROM memory WHERE id = ?").get(memoryId);
154
+ const existing = db.prepare('SELECT id, core FROM memory WHERE id = ?').get(memoryId);
155
155
  if (!existing)
156
156
  throw new Error(`Memory #${memoryId} not found`);
157
157
  if (!existing.core)
@@ -161,152 +161,152 @@ server.registerTool("memory_update_core", {
161
161
  throw new ReferenceError('Unable to create query embedding');
162
162
  }
163
163
  queries.updateCoreMemory(db).run(content, contentL1, memoryId);
164
- db.prepare("DELETE FROM vec_memories WHERE memory_id = ?").run(BigInt(memoryId));
165
- db.prepare("INSERT INTO vec_memories(memory_id, embedding) VALUES (?, ?)").run(BigInt(memoryId), Buffer.from(embedding.buffer));
164
+ db.prepare('DELETE FROM vec_memories WHERE memory_id = ?').run(BigInt(memoryId));
165
+ db.prepare('INSERT INTO vec_memories(memory_id, embedding) VALUES (?, ?)').run(BigInt(memoryId), Buffer.from(embedding.buffer));
166
166
  return {
167
- content: [{ type: "text", text: `Updated core memory #${memoryId}` }],
167
+ content: [{ type: 'text', text: `Updated core memory #${memoryId}` }],
168
168
  };
169
169
  });
170
- server.registerTool("memory_recent", {
171
- description: "Get recent memories within time window",
170
+ server.registerTool('memory_recent', {
171
+ description: 'Get recent memories within time window',
172
172
  inputSchema: {
173
- hours: z.number().optional().describe("Hours to look back (default: 24)"),
174
- limit: z.number().optional().describe("Maximum results (default: 20)"),
173
+ hours: z.number().optional().describe('Hours to look back (default: 24)'),
174
+ limit: z.number().optional().describe('Maximum results (default: 20)'),
175
175
  },
176
176
  }, async ({ hours = 24, limit = 20 }) => {
177
177
  const results = queries.getRecentMemories(db).all(`-${hours} hours`, limit);
178
178
  for (const result of results) {
179
- logAccess(result.id, "recent");
179
+ logAccess(result.id, 'recent');
180
180
  }
181
181
  return {
182
- content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
182
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
183
183
  };
184
184
  });
185
- server.registerTool("memory_stats", {
186
- description: "Get memory database statistics",
185
+ server.registerTool('memory_stats', {
186
+ description: 'Get memory database statistics',
187
187
  inputSchema: {},
188
188
  }, async () => {
189
189
  const stats = queries.getStats(db).all();
190
- const total = db.prepare("SELECT COUNT(*) as count FROM memory").get().count;
191
- const tags = db.prepare("SELECT COUNT(*) as count FROM tag").get().count;
192
- const relationships = db.prepare("SELECT COUNT(*) as count FROM relationship").get().count;
193
- const accessLogs = db.prepare("SELECT COUNT(*) as count FROM access_log").get().count;
190
+ const total = db.prepare('SELECT COUNT(*) as count FROM memory').get().count;
191
+ const tags = db.prepare('SELECT COUNT(*) as count FROM tag').get().count;
192
+ const relationships = db.prepare('SELECT COUNT(*) as count FROM relationship').get().count;
193
+ const accessLogs = db.prepare('SELECT COUNT(*) as count FROM access_log').get().count;
194
194
  const rows = stats
195
195
  .map((s) => ` ${s.type.padEnd(15)} ${s.count.toString().padStart(3)} memories (avg importance: ${s.avg_importance?.toFixed(1)})`)
196
- .join("\n");
196
+ .join('\n');
197
197
  return {
198
198
  content: [
199
199
  {
200
- type: "text",
200
+ type: 'text',
201
201
  text: `Memory Statistics:\n\n${rows}\n\n Total: ${total} memories\n Tags: ${tags}\n Relationships: ${relationships}\n Access Logs: ${accessLogs}`,
202
202
  },
203
203
  ],
204
204
  };
205
205
  });
206
- server.registerTool("memory_consolidate", {
207
- description: "Run memory consolidation cycle (decay old, boost accessed)",
206
+ server.registerTool('memory_consolidate', {
207
+ description: 'Run memory consolidation cycle (decay old, boost accessed)',
208
208
  inputSchema: {},
209
209
  }, async () => {
210
210
  const results = runConsolidation();
211
211
  return {
212
212
  content: [
213
213
  {
214
- type: "text",
214
+ type: 'text',
215
215
  text: `Consolidation Complete\n\nReviewed: ${results.reviewed} memories\nBoosted: ${results.boosted} memories\nDecayed: ${results.decayed} memories\nUnchanged: ${results.unchanged} memories\n\nChanges saved to database`,
216
216
  },
217
217
  ],
218
218
  };
219
219
  });
220
- server.registerTool("memory_relationships", {
221
- description: "View relationship graph for a memory",
220
+ server.registerTool('memory_relationships', {
221
+ description: 'View relationship graph for a memory',
222
222
  inputSchema: {
223
- memoryId: z.number().describe("Memory ID to explore"),
223
+ memoryId: z.number().describe('Memory ID to explore'),
224
224
  },
225
225
  }, async ({ memoryId }) => {
226
226
  const relationships = queries.getMemoryRelationships(db).all(memoryId, memoryId);
227
227
  return {
228
- content: [{ type: "text", text: JSON.stringify(relationships, null, 2) }],
228
+ content: [{ type: 'text', text: JSON.stringify(relationships, null, 2) }],
229
229
  };
230
230
  });
231
- server.registerTool("memory_link", {
232
- description: "Explicitly create a typed relationship between two memories",
231
+ server.registerTool('memory_link', {
232
+ description: 'Explicitly create a typed relationship between two memories',
233
233
  inputSchema: {
234
- fromId: z.number().describe("Source memory ID"),
235
- toId: z.number().describe("Target memory ID"),
236
- type: z.enum(["related", "supersedes", "supports", "contradicts", "derives_from"])
237
- .describe("Relationship type"),
234
+ fromId: z.number().describe('Source memory ID'),
235
+ toId: z.number().describe('Target memory ID'),
236
+ type: z.enum(['related', 'supersedes', 'supports', 'contradicts', 'derives_from'])
237
+ .describe('Relationship type'),
238
238
  },
239
239
  }, async ({ fromId, toId, type }) => {
240
- const from = db.prepare("SELECT id FROM memory WHERE id = ?").get(fromId);
240
+ const from = db.prepare('SELECT id FROM memory WHERE id = ?').get(fromId);
241
241
  if (!from)
242
242
  throw new Error(`Memory #${fromId} not found`);
243
- const to = db.prepare("SELECT id FROM memory WHERE id = ?").get(toId);
243
+ const to = db.prepare('SELECT id FROM memory WHERE id = ?').get(toId);
244
244
  if (!to)
245
245
  throw new Error(`Memory #${toId} not found`);
246
246
  const existing = queries.findRelationshipBetween(db).get(fromId, toId, type);
247
247
  if (existing) {
248
- return { content: [{ type: "text", text: `Relationship already exists (id: ${existing.id})` }] };
248
+ return { content: [{ type: 'text', text: `Relationship already exists (id: ${existing.id})` }] };
249
249
  }
250
250
  const result = queries.insertRelationship(db).run(fromId, toId, type);
251
251
  const id = Number(result.lastInsertRowid);
252
252
  return {
253
- content: [{ type: "text", text: `Created relationship #${id}: #${fromId} -[${type}]-> #${toId}` }],
253
+ content: [{ type: 'text', text: `Created relationship #${id}: #${fromId} -[${type}]-> #${toId}` }],
254
254
  };
255
255
  });
256
- server.registerTool("memory_unlink", {
257
- description: "Remove a relationship between two memories by relationship ID",
256
+ server.registerTool('memory_unlink', {
257
+ description: 'Remove a relationship between two memories by relationship ID',
258
258
  inputSchema: {
259
- relationshipId: z.number().describe("Relationship ID to delete"),
259
+ relationshipId: z.number().describe('Relationship ID to delete'),
260
260
  },
261
261
  }, async ({ relationshipId }) => {
262
- const existing = db.prepare("SELECT id FROM relationship WHERE id = ?").get(relationshipId);
262
+ const existing = db.prepare('SELECT id FROM relationship WHERE id = ?').get(relationshipId);
263
263
  if (!existing)
264
264
  throw new Error(`Relationship #${relationshipId} not found`);
265
265
  queries.deleteRelationship(db).run(relationshipId);
266
266
  return {
267
- content: [{ type: "text", text: `Deleted relationship #${relationshipId}` }],
267
+ content: [{ type: 'text', text: `Deleted relationship #${relationshipId}` }],
268
268
  };
269
269
  });
270
270
  // ---------------------------------------------------------------------------
271
271
  // journal tools
272
272
  // ---------------------------------------------------------------------------
273
- server.registerTool("journal_add", {
274
- description: "Add a daily journal entry",
273
+ server.registerTool('journal_add', {
274
+ description: 'Add a daily journal entry',
275
275
  inputSchema: {
276
- date: z.string().describe("Date YYYY-MM-DD"),
277
- content: z.string().describe("Full journal narrative"),
278
- learned: z.string().optional().describe("What I learned (optional)"),
279
- keyMoments: z.string().optional().describe("Key moments (optional)"),
280
- mood: z.string().optional().describe("Mood/feeling (optional)"),
276
+ date: z.string().describe('Date YYYY-MM-DD'),
277
+ content: z.string().describe('Full journal narrative'),
278
+ learned: z.string().optional().describe('What I learned (optional)'),
279
+ keyMoments: z.string().optional().describe('Key moments (optional)'),
280
+ mood: z.string().optional().describe('Mood/feeling (optional)'),
281
281
  },
282
282
  }, async ({ date, content, learned, keyMoments, mood }) => {
283
- const combinedLearned = [learned].filter(Boolean).join("\n\n") || null;
283
+ const combinedLearned = [learned].filter(Boolean).join('\n\n') || null;
284
284
  queries.insertJournal(db).run(date, content, combinedLearned, keyMoments || null, mood || null);
285
285
  return {
286
- content: [{ type: "text", text: `Journal entry created for ${date}` }],
286
+ content: [{ type: 'text', text: `Journal entry created for ${date}` }],
287
287
  };
288
288
  });
289
- server.registerTool("journal_recent", {
290
- description: "Get recent journal entries",
289
+ server.registerTool('journal_recent', {
290
+ description: 'Get recent journal entries',
291
291
  inputSchema: {
292
- days: z.number().optional().describe("Days to look back (default: 7)"),
292
+ days: z.number().optional().describe('Days to look back (default: 7)'),
293
293
  },
294
294
  }, async ({ days = 7 }) => {
295
295
  const entries = queries.getRecentJournals(db).all(`-${days} days`);
296
296
  return {
297
- content: [{ type: "text", text: JSON.stringify(entries, null, 2) }],
297
+ content: [{ type: 'text', text: JSON.stringify(entries, null, 2) }],
298
298
  };
299
299
  });
300
300
  // ---------------------------------------------------------------------------
301
301
  // task tools
302
302
  // ---------------------------------------------------------------------------
303
- server.registerTool("task_create", {
304
- description: "Create a new task. Use session_id to group tasks by project or feature.",
303
+ server.registerTool('task_create', {
304
+ description: 'Create a new task. Use session_id to group tasks by project or feature.',
305
305
  inputSchema: {
306
- title: z.string().describe("Short task title"),
307
- description: z.string().optional().describe("Detailed description of what needs to be done"),
306
+ title: z.string().describe('Short task title'),
307
+ description: z.string().optional().describe('Detailed description of what needs to be done'),
308
308
  session_id: z.string().optional().describe("Project or feature label to group related tasks (e.g. 'postnesia-mcp', 'auth-refactor')"),
309
- memory_id: z.number().optional().describe("Optional ID of a related memory"),
309
+ memory_id: z.number().optional().describe('Optional ID of a related memory'),
310
310
  },
311
311
  }, async ({ title, description, session_id, memory_id }) => {
312
312
  const result = queries.insertTask(db).run(title, description || null, session_id || null, memory_id || null);
@@ -314,19 +314,19 @@ server.registerTool("task_create", {
314
314
  return {
315
315
  content: [
316
316
  {
317
- type: "text",
318
- text: `Created task #${id}: ${title}${session_id ? `\n Session: ${session_id}` : ""}`,
317
+ type: 'text',
318
+ text: `Created task #${id}: ${title}${session_id ? `\n Session: ${session_id}` : ''}`,
319
319
  },
320
320
  ],
321
321
  };
322
322
  });
323
- server.registerTool("task_update", {
323
+ server.registerTool('task_update', {
324
324
  description: "Update a task's status, title, or description",
325
325
  inputSchema: {
326
- taskId: z.number().describe("ID of the task to update"),
327
- status: z.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("New status"),
328
- title: z.string().optional().describe("Updated title"),
329
- description: z.string().optional().describe("Updated description"),
326
+ taskId: z.number().describe('ID of the task to update'),
327
+ status: z.enum(['pending', 'in_progress', 'completed', 'cancelled']).optional().describe('New status'),
328
+ title: z.string().optional().describe('Updated title'),
329
+ description: z.string().optional().describe('Updated description'),
330
330
  },
331
331
  }, async ({ taskId, status, title, description }) => {
332
332
  const existing = queries.getTaskById(db).get(taskId);
@@ -337,31 +337,31 @@ server.registerTool("task_update", {
337
337
  return {
338
338
  content: [
339
339
  {
340
- type: "text",
340
+ type: 'text',
341
341
  text: `Task #${taskId} updated\n Status: ${updated.status}\n Title: ${updated.title}`,
342
342
  },
343
343
  ],
344
344
  };
345
345
  });
346
- server.registerTool("task_list", {
347
- description: "List tasks, optionally filtered by status and/or session_id. Use at session start to resume open work.",
346
+ server.registerTool('task_list', {
347
+ description: 'List tasks, optionally filtered by status and/or session_id. Use at session start to resume open work.',
348
348
  inputSchema: {
349
- status: z.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Filter by status"),
350
- session_id: z.string().optional().describe("Filter by project/feature label"),
351
- limit: z.number().optional().describe("Maximum results (default: 50)"),
349
+ status: z.enum(['pending', 'in_progress', 'completed', 'cancelled']).optional().describe('Filter by status'),
350
+ session_id: z.string().optional().describe('Filter by project/feature label'),
351
+ limit: z.number().optional().describe('Maximum results (default: 50)'),
352
352
  },
353
353
  }, async ({ status, session_id, limit = 50 }) => {
354
354
  const conditions = [];
355
355
  const params = [];
356
356
  if (status) {
357
- conditions.push("status = ?");
357
+ conditions.push('status = ?');
358
358
  params.push(status);
359
359
  }
360
360
  if (session_id) {
361
- conditions.push("session_id = ?");
361
+ conditions.push('session_id = ?');
362
362
  params.push(session_id);
363
363
  }
364
- const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
364
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
365
365
  params.push(limit);
366
366
  const tasks = db.prepare(`
367
367
  SELECT * FROM task
@@ -370,7 +370,7 @@ server.registerTool("task_list", {
370
370
  LIMIT ?
371
371
  `).all(...params);
372
372
  return {
373
- content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }],
373
+ content: [{ type: 'text', text: JSON.stringify(tasks, null, 2) }],
374
374
  };
375
375
  });
376
376
  // ---------------------------------------------------------------------------
@@ -379,12 +379,16 @@ server.registerTool("task_list", {
379
379
  async function main() {
380
380
  const transport = new StdioServerTransport();
381
381
  await server.connect(transport);
382
- console.error("Postnesia MCP Server running on stdio");
382
+ console.error('Postnesia MCP Server running on stdio');
383
383
  }
384
+ const exit = () => {
385
+ closeDb();
386
+ process.exit(0);
387
+ };
384
388
  main().catch((error) => {
385
- console.error("Fatal error:", error);
389
+ console.error('Fatal error:', error);
386
390
  closeDb();
387
391
  process.exit(1);
388
392
  });
389
- process.on('SIGINT', () => { closeDb(); process.exit(0); });
390
- process.on('SIGTERM', () => { closeDb(); process.exit(0); });
393
+ process.on('SIGINT', exit);
394
+ process.on('SIGTERM', exit);
package/dist/install.js CHANGED
@@ -24,11 +24,11 @@ const config = {
24
24
  claude: {
25
25
  mcpServers: {
26
26
  postnesia: {
27
- type: "stdio",
27
+ type: 'stdio',
28
28
  enabled: true,
29
- command: "npx",
29
+ command: 'npx',
30
30
  args: [
31
- "pn-mcp"
31
+ 'pn-mcp'
32
32
  ],
33
33
  env: {
34
34
  DATABASE_URL: process.env['DATABASE_URL'],
@@ -40,11 +40,11 @@ const config = {
40
40
  opencode: {
41
41
  mcp: {
42
42
  postnesia: {
43
- type: "local",
43
+ type: 'local',
44
44
  enabled: false,
45
45
  command: [
46
- "npx",
47
- "pn-mcp"
46
+ 'npx',
47
+ 'pn-mcp'
48
48
  ],
49
49
  environment: {
50
50
  DATABASE_URL: process.env['DATABASE_URL'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postnesia/mcp",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "An MCP server to interact with the Postnesia database",
5
5
  "type": "module",
6
6
  "private": false,
@@ -14,10 +14,10 @@
14
14
  "dependencies": {
15
15
  "@modelcontextprotocol/sdk": "^1.26.0",
16
16
  "zod": "^4.3.6",
17
- "@postnesia/db": "0.1.14"
17
+ "@postnesia/db": "0.1.15"
18
18
  },
19
19
  "devDependencies": {
20
- "@types/node": "^22.10.5",
20
+ "@types/node": "^24.12.0",
21
21
  "dotenv": "^17.3.1",
22
22
  "tsx": "^4.19.2",
23
23
  "typescript": "^5.7.3"
@@ -25,6 +25,8 @@
25
25
  "scripts": {
26
26
  "build": "pnpm clean && tsc -p tsconfig.json",
27
27
  "clean": "rm -rf ./dist",
28
- "start": "tsx src/index.ts"
28
+ "start": "tsx src/index.ts",
29
+ "lint": "pnpm eslint .",
30
+ "lint:fix": "pnpm eslint --fix ."
29
31
  }
30
32
  }