memory-journal-mcp 3.0.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.
Files changed (107) hide show
  1. package/.dockerignore +88 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +76 -0
  3. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +89 -0
  5. package/.github/ISSUE_TEMPLATE/question.md +63 -0
  6. package/.github/dependabot.yml +110 -0
  7. package/.github/pull_request_template.md +110 -0
  8. package/.github/workflows/DOCKER_DEPLOYMENT_SETUP.md +346 -0
  9. package/.github/workflows/codeql.yml +45 -0
  10. package/.github/workflows/dependabot-auto-merge.yml +42 -0
  11. package/.github/workflows/docker-publish.yml +277 -0
  12. package/.github/workflows/lint-and-test.yml +58 -0
  13. package/.github/workflows/publish-npm.yml +75 -0
  14. package/.github/workflows/secrets-scanning.yml +32 -0
  15. package/.github/workflows/security-update.yml +99 -0
  16. package/.memory-journal-team.db +0 -0
  17. package/.trivyignore +18 -0
  18. package/CHANGELOG.md +19 -0
  19. package/CODE_OF_CONDUCT.md +128 -0
  20. package/CONTRIBUTING.md +209 -0
  21. package/DOCKER_README.md +377 -0
  22. package/Dockerfile +64 -0
  23. package/LICENSE +21 -0
  24. package/README.md +461 -0
  25. package/SECURITY.md +200 -0
  26. package/VERSION +1 -0
  27. package/dist/cli.d.ts +5 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +42 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/constants/ServerInstructions.d.ts +8 -0
  32. package/dist/constants/ServerInstructions.d.ts.map +1 -0
  33. package/dist/constants/ServerInstructions.js +26 -0
  34. package/dist/constants/ServerInstructions.js.map +1 -0
  35. package/dist/database/SqliteAdapter.d.ts +198 -0
  36. package/dist/database/SqliteAdapter.d.ts.map +1 -0
  37. package/dist/database/SqliteAdapter.js +736 -0
  38. package/dist/database/SqliteAdapter.js.map +1 -0
  39. package/dist/filtering/ToolFilter.d.ts +63 -0
  40. package/dist/filtering/ToolFilter.d.ts.map +1 -0
  41. package/dist/filtering/ToolFilter.js +242 -0
  42. package/dist/filtering/ToolFilter.js.map +1 -0
  43. package/dist/github/GitHubIntegration.d.ts +91 -0
  44. package/dist/github/GitHubIntegration.d.ts.map +1 -0
  45. package/dist/github/GitHubIntegration.js +317 -0
  46. package/dist/github/GitHubIntegration.js.map +1 -0
  47. package/dist/handlers/prompts/index.d.ts +28 -0
  48. package/dist/handlers/prompts/index.d.ts.map +1 -0
  49. package/dist/handlers/prompts/index.js +366 -0
  50. package/dist/handlers/prompts/index.js.map +1 -0
  51. package/dist/handlers/resources/index.d.ts +27 -0
  52. package/dist/handlers/resources/index.d.ts.map +1 -0
  53. package/dist/handlers/resources/index.js +453 -0
  54. package/dist/handlers/resources/index.js.map +1 -0
  55. package/dist/handlers/tools/index.d.ts +26 -0
  56. package/dist/handlers/tools/index.d.ts.map +1 -0
  57. package/dist/handlers/tools/index.js +982 -0
  58. package/dist/handlers/tools/index.js.map +1 -0
  59. package/dist/index.d.ts +11 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +13 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/server/McpServer.d.ts +18 -0
  64. package/dist/server/McpServer.d.ts.map +1 -0
  65. package/dist/server/McpServer.js +171 -0
  66. package/dist/server/McpServer.js.map +1 -0
  67. package/dist/types/index.d.ts +300 -0
  68. package/dist/types/index.d.ts.map +1 -0
  69. package/dist/types/index.js +15 -0
  70. package/dist/types/index.js.map +1 -0
  71. package/dist/utils/McpLogger.d.ts +61 -0
  72. package/dist/utils/McpLogger.d.ts.map +1 -0
  73. package/dist/utils/McpLogger.js +113 -0
  74. package/dist/utils/McpLogger.js.map +1 -0
  75. package/dist/utils/logger.d.ts +30 -0
  76. package/dist/utils/logger.d.ts.map +1 -0
  77. package/dist/utils/logger.js +70 -0
  78. package/dist/utils/logger.js.map +1 -0
  79. package/dist/vector/VectorSearchManager.d.ts +63 -0
  80. package/dist/vector/VectorSearchManager.d.ts.map +1 -0
  81. package/dist/vector/VectorSearchManager.js +235 -0
  82. package/dist/vector/VectorSearchManager.js.map +1 -0
  83. package/docker-compose.yml +37 -0
  84. package/eslint.config.js +86 -0
  85. package/mcp-config-example.json +21 -0
  86. package/package.json +71 -0
  87. package/releases/release-notes-v2.2.0.md +165 -0
  88. package/releases/release-notes.md +214 -0
  89. package/releases/v3.0.0.md +236 -0
  90. package/server.json +42 -0
  91. package/src/cli.ts +52 -0
  92. package/src/constants/ServerInstructions.ts +25 -0
  93. package/src/database/SqliteAdapter.ts +952 -0
  94. package/src/filtering/ToolFilter.ts +271 -0
  95. package/src/github/GitHubIntegration.ts +409 -0
  96. package/src/handlers/prompts/index.ts +420 -0
  97. package/src/handlers/resources/index.ts +529 -0
  98. package/src/handlers/tools/index.ts +1081 -0
  99. package/src/index.ts +53 -0
  100. package/src/server/McpServer.ts +230 -0
  101. package/src/types/index.ts +435 -0
  102. package/src/types/sql.js.d.ts +34 -0
  103. package/src/utils/McpLogger.ts +155 -0
  104. package/src/utils/logger.ts +98 -0
  105. package/src/vector/VectorSearchManager.ts +277 -0
  106. package/tools.json +300 -0
  107. package/tsconfig.json +51 -0
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Memory Journal MCP Server - Prompt Handlers
3
+ *
4
+ * Exports all MCP prompts for workflow guidance.
5
+ */
6
+
7
+ import type { SqliteAdapter } from '../../database/SqliteAdapter.js';
8
+
9
+ /**
10
+ * Message format for MCP prompts
11
+ */
12
+ interface PromptMessage {
13
+ role: string;
14
+ content: {
15
+ type: string;
16
+ text: string;
17
+ };
18
+ }
19
+
20
+ /**
21
+ * Internal prompt definition with db handler
22
+ */
23
+ interface InternalPromptDef {
24
+ name: string;
25
+ description: string;
26
+ arguments?: {
27
+ name: string;
28
+ description: string;
29
+ required?: boolean;
30
+ }[];
31
+ handler: (args: Record<string, string>, db: SqliteAdapter) => { messages: PromptMessage[] };
32
+ }
33
+
34
+ /**
35
+ * Execute a raw SQL query on the database
36
+ */
37
+ function execQuery(db: SqliteAdapter, sql: string, params: unknown[] = []): Record<string, unknown>[] {
38
+ const rawDb = db.getRawDb();
39
+ const result = rawDb.exec(sql, params);
40
+ if (result.length === 0) return [];
41
+
42
+ const columns = result[0]?.columns ?? [];
43
+ return (result[0]?.values ?? []).map((values: unknown[]) => {
44
+ const obj: Record<string, unknown> = {};
45
+ columns.forEach((col: string, i: number) => {
46
+ obj[col] = values[i];
47
+ });
48
+ return obj;
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Get all prompt definitions for MCP list
54
+ */
55
+ export function getPrompts(): object[] {
56
+ const prompts = getAllPromptDefinitions();
57
+ return prompts.map(p => ({
58
+ name: p.name,
59
+ description: p.description,
60
+ arguments: p.arguments,
61
+ }));
62
+ }
63
+
64
+ /**
65
+ * Get a prompt by name
66
+ */
67
+ export function getPrompt(
68
+ name: string,
69
+ args: Record<string, string>,
70
+ db: SqliteAdapter
71
+ ): { messages: PromptMessage[] } {
72
+ const prompts = getAllPromptDefinitions();
73
+ const prompt = prompts.find(p => p.name === name);
74
+
75
+ if (!prompt) {
76
+ throw new Error(`Unknown prompt: ${name}`);
77
+ }
78
+
79
+ return prompt.handler(args, db);
80
+ }
81
+
82
+ /**
83
+ * Get all prompt definitions
84
+ */
85
+ function getAllPromptDefinitions(): InternalPromptDef[] {
86
+ return [
87
+ {
88
+ name: 'find-related',
89
+ description: 'Discover connected entries via semantic similarity',
90
+ arguments: [
91
+ { name: 'query', description: 'Search query for finding related entries', required: true },
92
+ ],
93
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
94
+ const query = args['query'] ?? '';
95
+ const entries = db.searchEntries(query, { limit: 5 });
96
+
97
+ return {
98
+ messages: [{
99
+ role: 'user',
100
+ content: {
101
+ type: 'text',
102
+ text: `Find entries related to: "${query}"\n\nRecent matching entries:\n${entries.map(e => `- [${String(e.id)}] ${e.content.slice(0, 100)}...`).join('\n')}`,
103
+ },
104
+ }],
105
+ };
106
+ },
107
+ },
108
+ {
109
+ name: 'prepare-standup',
110
+ description: 'Daily standup summaries',
111
+ arguments: [],
112
+ handler: (_args: Record<string, string>, db: SqliteAdapter) => {
113
+ const today = new Date().toISOString().split('T')[0] ?? '';
114
+ const yesterday = new Date(Date.now() - 86400000).toISOString().split('T')[0] ?? '';
115
+
116
+ const entries = db.searchByDateRange(yesterday, today);
117
+
118
+ return {
119
+ messages: [{
120
+ role: 'user',
121
+ content: {
122
+ type: 'text',
123
+ text: `Prepare a standup summary based on these recent entries:\n\n${entries.map(e => `[${e.timestamp}] ${e.entryType}: ${e.content}`).join('\n\n')}\n\nFormat as:\n- Yesterday: <summary>\n- Today: <planned work>\n- Blockers: <any blockers>`,
124
+ },
125
+ }],
126
+ };
127
+ },
128
+ },
129
+ {
130
+ name: 'prepare-retro',
131
+ description: 'Sprint retrospectives',
132
+ arguments: [
133
+ { name: 'days', description: 'Number of days to include (default: 14)', required: false },
134
+ ],
135
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
136
+ const days = parseInt(args['days'] ?? '14', 10);
137
+ const endDate = new Date().toISOString().split('T')[0] ?? '';
138
+ const startDate = new Date(Date.now() - days * 86400000).toISOString().split('T')[0] ?? '';
139
+
140
+ const entries = db.searchByDateRange(startDate, endDate);
141
+
142
+ return {
143
+ messages: [{
144
+ role: 'user',
145
+ content: {
146
+ type: 'text',
147
+ text: `Prepare a retrospective for the last ${String(days)} days based on these entries:\n\n${entries.slice(0, 20).map(e => `[${e.timestamp}] ${e.entryType}: ${e.content.slice(0, 200)}`).join('\n\n')}\n\nFormat as:\n- What went well\n- What could improve\n- Action items`,
148
+ },
149
+ }],
150
+ };
151
+ },
152
+ },
153
+ {
154
+ name: 'weekly-digest',
155
+ description: 'Day-by-day weekly summaries',
156
+ arguments: [],
157
+ handler: (_args: Record<string, string>, db: SqliteAdapter) => {
158
+ const endDate = new Date().toISOString().split('T')[0] ?? '';
159
+ const startDate = new Date(Date.now() - 7 * 86400000).toISOString().split('T')[0] ?? '';
160
+
161
+ const entries = db.searchByDateRange(startDate, endDate);
162
+
163
+ return {
164
+ messages: [{
165
+ role: 'user',
166
+ content: {
167
+ type: 'text',
168
+ text: `Create a weekly digest from these entries:\n\n${entries.map(e => `[${e.timestamp}] ${e.entryType}: ${e.content.slice(0, 150)}`).join('\n\n')}\n\nFormat as day-by-day summary with highlights.`,
169
+ },
170
+ }],
171
+ };
172
+ },
173
+ },
174
+ {
175
+ name: 'analyze-period',
176
+ description: 'Deep period analysis with insights',
177
+ arguments: [
178
+ { name: 'start_date', description: 'Start date (YYYY-MM-DD)', required: true },
179
+ { name: 'end_date', description: 'End date (YYYY-MM-DD)', required: true },
180
+ ],
181
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
182
+ const startDate = args['start_date'] ?? '';
183
+ const endDate = args['end_date'] ?? '';
184
+
185
+ const entries = db.searchByDateRange(startDate, endDate);
186
+ const stats = db.getStatistics('day');
187
+
188
+ return {
189
+ messages: [{
190
+ role: 'user',
191
+ content: {
192
+ type: 'text',
193
+ text: `Analyze the period ${startDate} to ${endDate}:\n\nStatistics: ${JSON.stringify(stats, null, 2)}\n\nEntries (${String(entries.length)} total):\n${entries.slice(0, 15).map(e => `[${e.timestamp}] ${e.entryType}: ${e.content.slice(0, 100)}`).join('\n')}\n\nProvide insights on patterns, productivity, and recommendations.`,
194
+ },
195
+ }],
196
+ };
197
+ },
198
+ },
199
+ {
200
+ name: 'goal-tracker',
201
+ description: 'Milestone and achievement tracking',
202
+ arguments: [],
203
+ handler: (_args: Record<string, string>, db: SqliteAdapter) => {
204
+ const entries = execQuery(db, `
205
+ SELECT * FROM memory_journal
206
+ WHERE significance_type IS NOT NULL
207
+ AND deleted_at IS NULL
208
+ ORDER BY timestamp DESC
209
+ LIMIT 20
210
+ `);
211
+
212
+ return {
213
+ messages: [{
214
+ role: 'user',
215
+ content: {
216
+ type: 'text',
217
+ text: `Track goals and milestones based on significant entries:\n\n${JSON.stringify(entries, null, 2)}\n\nSummarize progress toward goals and highlight achievements.`,
218
+ },
219
+ }],
220
+ };
221
+ },
222
+ },
223
+ {
224
+ name: 'get-context-bundle',
225
+ description: 'Project context with Git/GitHub',
226
+ arguments: [],
227
+ handler: (_args: Record<string, string>, db: SqliteAdapter) => {
228
+ const recent = db.getRecentEntries(10);
229
+ const stats = db.getStatistics('week');
230
+
231
+ return {
232
+ messages: [{
233
+ role: 'user',
234
+ content: {
235
+ type: 'text',
236
+ text: `Project context bundle:\n\nRecent Entries: ${JSON.stringify(recent.slice(0, 5), null, 2)}\n\nStatistics: ${JSON.stringify(stats, null, 2)}\n\nUse this context to understand the project state.`,
237
+ },
238
+ }],
239
+ };
240
+ },
241
+ },
242
+ {
243
+ name: 'get-recent-entries',
244
+ description: 'Formatted recent entries',
245
+ arguments: [
246
+ { name: 'limit', description: 'Number of entries (default: 10)', required: false },
247
+ ],
248
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
249
+ const limit = parseInt(args['limit'] ?? '10', 10);
250
+ const entries = db.getRecentEntries(limit);
251
+
252
+ return {
253
+ messages: [{
254
+ role: 'user',
255
+ content: {
256
+ type: 'text',
257
+ text: `Recent ${String(limit)} entries:\n\n${entries.map(e => `## ${e.timestamp} (${e.entryType})\n\n${e.content}\n\nTags: ${e.tags.join(', ') || 'none'}`).join('\n\n---\n\n')}`,
258
+ },
259
+ }],
260
+ };
261
+ },
262
+ },
263
+ {
264
+ name: 'project-status-summary',
265
+ description: 'GitHub Project status reports',
266
+ arguments: [
267
+ { name: 'project_number', description: 'GitHub Project number', required: true },
268
+ ],
269
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
270
+ const projectNumber = parseInt(args['project_number'] ?? '0', 10);
271
+ const entries = execQuery(db, `
272
+ SELECT * FROM memory_journal
273
+ WHERE project_number = ?
274
+ AND deleted_at IS NULL
275
+ ORDER BY timestamp DESC
276
+ LIMIT 20
277
+ `, [projectNumber]);
278
+
279
+ return {
280
+ messages: [{
281
+ role: 'user',
282
+ content: {
283
+ type: 'text',
284
+ text: `Generate a status summary for Project #${String(projectNumber)}:\n\nEntries: ${JSON.stringify(entries, null, 2)}\n\nProvide: overview, recent activity, blockers, next steps.`,
285
+ },
286
+ }],
287
+ };
288
+ },
289
+ },
290
+ {
291
+ name: 'pr-summary',
292
+ description: 'Pull request journal activity summary',
293
+ arguments: [
294
+ { name: 'pr_number', description: 'Pull request number', required: true },
295
+ ],
296
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
297
+ const prNumber = parseInt(args['pr_number'] ?? '0', 10);
298
+ const entries = execQuery(db, `
299
+ SELECT * FROM memory_journal
300
+ WHERE pr_number = ?
301
+ AND deleted_at IS NULL
302
+ ORDER BY timestamp ASC
303
+ `, [prNumber]);
304
+
305
+ return {
306
+ messages: [{
307
+ role: 'user',
308
+ content: {
309
+ type: 'text',
310
+ text: `Summarize PR #${String(prNumber)} activity:\n\nJournal entries: ${JSON.stringify(entries, null, 2)}\n\nProvide: summary of changes, decisions made, testing done.`,
311
+ },
312
+ }],
313
+ };
314
+ },
315
+ },
316
+ {
317
+ name: 'code-review-prep',
318
+ description: 'Comprehensive PR review preparation',
319
+ arguments: [
320
+ { name: 'pr_number', description: 'Pull request number', required: true },
321
+ ],
322
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
323
+ const prNumber = parseInt(args['pr_number'] ?? '0', 10);
324
+ const entries = execQuery(db, `
325
+ SELECT * FROM memory_journal
326
+ WHERE pr_number = ?
327
+ AND deleted_at IS NULL
328
+ ORDER BY timestamp ASC
329
+ `, [prNumber]);
330
+
331
+ return {
332
+ messages: [{
333
+ role: 'user',
334
+ content: {
335
+ type: 'text',
336
+ text: `Prepare for code review of PR #${String(prNumber)}:\n\nContext entries: ${JSON.stringify(entries, null, 2)}\n\nProvide: review checklist, areas of concern, testing recommendations.`,
337
+ },
338
+ }],
339
+ };
340
+ },
341
+ },
342
+ {
343
+ name: 'pr-retrospective',
344
+ description: 'Completed PR analysis with learnings',
345
+ arguments: [
346
+ { name: 'pr_number', description: 'Pull request number', required: true },
347
+ ],
348
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
349
+ const prNumber = parseInt(args['pr_number'] ?? '0', 10);
350
+ const entries = execQuery(db, `
351
+ SELECT * FROM memory_journal
352
+ WHERE pr_number = ?
353
+ AND deleted_at IS NULL
354
+ ORDER BY timestamp ASC
355
+ `, [prNumber]);
356
+
357
+ return {
358
+ messages: [{
359
+ role: 'user',
360
+ content: {
361
+ type: 'text',
362
+ text: `Retrospective for PR #${String(prNumber)}:\n\nJournal entries: ${JSON.stringify(entries, null, 2)}\n\nProvide: what went well, challenges, lessons learned.`,
363
+ },
364
+ }],
365
+ };
366
+ },
367
+ },
368
+ {
369
+ name: 'actions-failure-digest',
370
+ description: 'CI/CD failure analysis with root cause identification',
371
+ arguments: [],
372
+ handler: (_args: Record<string, string>, db: SqliteAdapter) => {
373
+ const entries = execQuery(db, `
374
+ SELECT * FROM memory_journal
375
+ WHERE workflow_run_id IS NOT NULL
376
+ AND deleted_at IS NULL
377
+ ORDER BY timestamp DESC
378
+ LIMIT 20
379
+ `);
380
+
381
+ return {
382
+ messages: [{
383
+ role: 'user',
384
+ content: {
385
+ type: 'text',
386
+ text: `Analyze CI/CD failures from these workflow entries:\n\n${JSON.stringify(entries, null, 2)}\n\nProvide: failure patterns, root causes, remediation steps.`,
387
+ },
388
+ }],
389
+ };
390
+ },
391
+ },
392
+ {
393
+ name: 'project-milestone-tracker',
394
+ description: 'Milestone progress tracking',
395
+ arguments: [
396
+ { name: 'project_number', description: 'GitHub Project number', required: true },
397
+ ],
398
+ handler: (args: Record<string, string>, db: SqliteAdapter) => {
399
+ const projectNumber = parseInt(args['project_number'] ?? '0', 10);
400
+ const entries = execQuery(db, `
401
+ SELECT * FROM memory_journal
402
+ WHERE project_number = ?
403
+ AND significance_type IS NOT NULL
404
+ AND deleted_at IS NULL
405
+ ORDER BY timestamp DESC
406
+ `, [projectNumber]);
407
+
408
+ return {
409
+ messages: [{
410
+ role: 'user',
411
+ content: {
412
+ type: 'text',
413
+ text: `Track milestones for Project #${String(projectNumber)}:\n\nMilestone entries: ${JSON.stringify(entries, null, 2)}\n\nProvide: progress summary, upcoming milestones, timeline.`,
414
+ },
415
+ }],
416
+ };
417
+ },
418
+ },
419
+ ];
420
+ }