memory-journal-mcp 7.2.0 → 7.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 CHANGED
@@ -10,7 +10,7 @@
10
10
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-Published-green)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.neverinfamous/memory-journal-mcp)
11
11
  [![Security](https://img.shields.io/badge/Security-Enhanced-green.svg)](SECURITY.md)
12
12
  [![TypeScript](https://img.shields.io/badge/TypeScript-Strict-blue.svg)](https://github.com/neverinfamous/memory-journal-mcp)
13
- ![Coverage](https://img.shields.io/badge/Coverage-97.14%25-brightgreen.svg)
13
+ ![Coverage](https://img.shields.io/badge/Coverage-97.1%25-brightgreen.svg)
14
14
  ![Tests](https://img.shields.io/badge/Tests-1782_passed-brightgreen.svg)
15
15
  ![E2E Tests](https://img.shields.io/badge/E2E_Tests-391_passed-brightgreen.svg)
16
16
  [![CI](https://github.com/neverinfamous/memory-journal-mcp/actions/workflows/gatekeeper.yml/badge.svg)](https://github.com/neverinfamous/memory-journal-mcp/actions/workflows/gatekeeper.yml)
@@ -30,24 +30,24 @@
30
30
 
31
31
  **67 MCP Tools** · **17 Workflow Prompts** · **34 Resources** · **10 Tool Groups** · **Code Mode** · **GitHub Commander** (Issue Triage, PR Review, Milestone Sprints, Security/Quality/Perf Audits) · **GitHub Integration** (Issues, PRs, Actions, Kanban, Milestones, Insights) · **Team Collaboration** (Shared DB, Vector Search, Cross-Project Insights)
32
32
 
33
- | Feature | Description |
34
- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
35
- | **Session Intelligence** | Agents auto-query project history, create entries at checkpoints, and hand off context between sessions via `/session-summary` and `team-session-summary` |
36
- | **GitHub Integration** | 18 tools for Issues, PRs, Actions, Kanban, Milestones (%), Copilot Reviews, and 14-day Insights |
37
- | **Dynamic Project Routing** | Seamlessly switch contexts and access CI/Issue tracking across multiple repositories using a single server instance via `PROJECT_REGISTRY` |
38
- | **Knowledge Graphs** | 8 relationship types linking specs → implementations → tests → PRs with Mermaid visualization |
39
- | **Hybrid Search** | Reciprocal Rank Fusion combining FTS5 keywords, semantic vector similarity, auto-heuristics, and date-range filters |
40
- | **Code Mode** | Execute multi-step operations in a secure sandbox — up to 90% token savings via `mj.*` API |
41
- | **Configurable Briefing** | 14 env vars / CLI flags control `memory://briefing` content — entries, team, GitHub detail, skills awareness |
42
- | **Reports & Analytics** | Standups, retrospectives, PR summaries, digests, period analyses, and milestone tracking |
43
- | **Team Collaboration** | 22 tools with full parity — CRUD, vector search, relationship graphs, cross-project insights, author attribution |
44
- | **Data Interoperability** | Bidirectional Markdown roundtripping, unified IO namespace, and schema-safe JSON exports with hard bounds-checked path traversal defenses |
45
- | **Backup & Restore** | One-command backup/restore with automated scheduling, retention policies, and safety-net auto-backups |
46
- | **Security & Transport** | OAuth 2.1 (RFC 9728/8414, JWT/JWKS, scopes), Streamable HTTP + SSE, rate limiting, CORS, SQL injection prevention, non-root Docker |
47
- | **Structured Error Handling** | Every tool returns `{success, error, code, category, suggestion, recoverable}` — agents get classification, remediation hints, and recoverability signals |
48
- | **Agent Collaboration** | IDE agents and Copilot share context; review findings become searchable knowledge; agents suggest reusable rules and skills ([setup](docs/copilot-setup.md)) |
33
+ | Feature | Description |
34
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
35
+ | **Session Intelligence** | Agents auto-query project history, create entries at checkpoints, and hand off context between sessions via `/session-summary` and `team-session-summary` |
36
+ | **GitHub Integration** | 18 tools for Issues, PRs, Actions, Kanban, Milestones (%), Copilot Reviews, and 14-day Insights |
37
+ | **Dynamic Project Routing** | Seamlessly switch contexts and access CI/Issue tracking across multiple repositories using a single server instance via `PROJECT_REGISTRY` |
38
+ | **Knowledge Graphs** | 8 relationship types linking specs → implementations → tests → PRs with Mermaid visualization |
39
+ | **Hybrid Search** | Reciprocal Rank Fusion combining FTS5 keywords, semantic vector similarity, auto-heuristics, and date-range filters |
40
+ | **Code Mode** | Execute multi-step operations in a secure sandbox — up to 90% token savings via `mj.*` API |
41
+ | **Configurable Briefing** | 14 env vars / CLI flags control `memory://briefing` content — entries, team, GitHub detail, skills awareness |
42
+ | **Reports & Analytics** | Standups, retrospectives, PR summaries, digests, period analyses, and milestone tracking |
43
+ | **Team Collaboration** | 22 tools with full parity — CRUD, vector search, relationship graphs, cross-project insights, author attribution |
44
+ | **Data Interoperability** | Bidirectional Markdown roundtripping, unified IO namespace, and schema-safe JSON exports with hard bounds-checked path traversal defenses |
45
+ | **Backup & Restore** | One-command backup/restore with automated scheduling, retention policies, and safety-net auto-backups |
46
+ | **Security & Transport** | OAuth 2.1 (RFC 9728/8414, JWT/JWKS, scopes), Streamable HTTP + SSE, rate limiting, CORS, SQL injection prevention, non-root Docker |
47
+ | **Structured Error Handling** | Every tool returns `{success, error, code, category, suggestion, recoverable}` — agents get classification, remediation hints, and recoverability signals |
48
+ | **Agent Collaboration** | IDE agents and Copilot share context; review findings become searchable knowledge; agents suggest reusable rules and skills ([setup](docs/copilot-setup.md)) |
49
49
  | **Native Agent Skills** | Bundled foundational coding paradigms (`autonomous-dev`, `bun`, `gitlab`, `golang`, `mysql`, `playwright-standard`, `postgres`, `react-best-practices`, `rust`, `shadcn-ui`, `skill-builder`, `sqlite`, `typescript`, `vitest-standard`) establishing permanent AI behavior and architecture rules |
50
- | **GitHub Commander** | Pipeline skills for issue triage, PR reviews, sprint milestones, and security/quality/performance audits with journal trails ([docs](skills/github-commander/SKILL.md)) |
50
+ | **GitHub Commander** | Pipeline skills for issue triage, PR reviews, sprint milestones, and security/quality/performance audits with journal trails ([docs](skills/github-commander/SKILL.md)) |
51
51
 
52
52
  ---
53
53
 
@@ -246,7 +246,7 @@ Control which tools are exposed via `MEMORY_JOURNAL_MCP_TOOL_FILTER` (or CLI: `-
246
246
  - `memory://help/{group}` - Per-group tool reference with parameters and annotations
247
247
  - `memory://briefing/{repo}` - Context targeted to a specific repository
248
248
 
249
- *Note: The `memory://github/status`, `memory://github/insights`, `memory://github/milestones`, and `memory://milestones/{number}` resources also accept an optional `/{repo}` path suffix for cross-repo targeting.*
249
+ _Note: The `memory://github/status`, `memory://github/insights`, `memory://github/milestones`, and `memory://milestones/{number}` resources also accept an optional `/{repo}` path suffix for cross-repo targeting._
250
250
 
251
251
  ---
252
252
 
@@ -1,5 +1,5 @@
1
- import { withSessionInit, withPriority, ASSISTANT_FOCUSED, TOOL_GROUPS, HIGH_PRIORITY, LOW_PRIORITY, MEDIUM_PRIORITY, setDefaultSandboxMode, initializeAuditLogger, parseToolFilter, getFilterSummary, getToolFilterFromEnv, getTools, getEnabledGroups, callTool, getGlobalAuditLogger, sendProgress, SUPPORTED_SCOPES, getRequiredScope, hasScope, getAuditResourceDef, execQuery, transformEntryRow, resolveGitHubRepo, isResourceError, milestoneCompletionPct, parseScopes, BASE_SCOPES, getAllToolNames, globalMetrics, DEFAULT_BRIEFING_CONFIG } from './chunk-ORV7ZZOE.js';
2
- import { logger, GitHubIntegration, ConfigurationError, ResourceNotFoundError, ConnectionError, QueryError, assertNoPathTraversal, ValidationError, MemoryJournalMcpError, validateDateFormatPattern } from './chunk-IWKLHSPU.js';
1
+ import { withSessionInit, withPriority, ASSISTANT_FOCUSED, TOOL_GROUPS, HIGH_PRIORITY, LOW_PRIORITY, MEDIUM_PRIORITY, setDefaultSandboxMode, initializeAuditLogger, parseToolFilter, getFilterSummary, getToolFilterFromEnv, getTools, getEnabledGroups, callTool, getGlobalAuditLogger, sendProgress, SUPPORTED_SCOPES, getRequiredScope, hasScope, getAuditResourceDef, execQuery, transformEntryRow, resolveGitHubRepo, isResourceError, milestoneCompletionPct, parseScopes, BASE_SCOPES, getAllToolNames, globalMetrics, DEFAULT_BRIEFING_CONFIG } from './chunk-ZJJD2F5T.js';
2
+ import { logger, GitHubIntegration, ConfigurationError, ResourceNotFoundError, ConnectionError, QueryError, assertNoPathTraversal, ValidationError, MemoryJournalMcpError, validateDateFormatPattern } from './chunk-WXDEVIFL.js';
3
3
  import { createRequire } from 'module';
4
4
  import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
5
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -1540,7 +1540,7 @@ var VectorSearchManager = class {
1540
1540
  WHERE embedding MATCH ?
1541
1541
  ORDER BY distance
1542
1542
  LIMIT ?`
1543
- ).all(queryVec, limit * 2);
1543
+ ).all(queryVec, limit);
1544
1544
  const filteredResults = results.map((r) => ({
1545
1545
  entryId: r.entry_id,
1546
1546
  score: 1 / (1 + r.distance)
@@ -1593,7 +1593,7 @@ var VectorSearchManager = class {
1593
1593
  WHERE embedding MATCH ?
1594
1594
  ORDER BY distance
1595
1595
  LIMIT ?`
1596
- ).all(queryVec, (limit + 1) * 2);
1596
+ ).all(queryVec, limit + 1);
1597
1597
  const filteredResults = results.filter((r) => r.entry_id !== entryId).map((r) => ({
1598
1598
  entryId: r.entry_id,
1599
1599
  score: 1 / (1 + r.distance)
@@ -2476,18 +2476,60 @@ function buildQuickAccess(groups) {
2476
2476
  return table;
2477
2477
  }
2478
2478
  var CODE_MODE_NAMESPACE_ROWS = [
2479
- { group: "core", label: "Core", namespace: "`mj.core.*`", example: '`mj.core.createEntry("Implemented feature X")`' },
2480
- { group: "search", label: "Search", namespace: "`mj.search.*`", example: '`mj.search.searchEntries("performance")`' },
2481
- { group: "analytics", label: "Analytics", namespace: "`mj.analytics.*`", example: "`mj.analytics.getStatistics()`" },
2482
- { group: "relationships", label: "Relationships", namespace: "`mj.relationships.*`", example: '`mj.relationships.linkEntries(1, 2, "implements")`' },
2479
+ {
2480
+ group: "core",
2481
+ label: "Core",
2482
+ namespace: "`mj.core.*`",
2483
+ example: '`mj.core.createEntry("Implemented feature X")`'
2484
+ },
2485
+ {
2486
+ group: "search",
2487
+ label: "Search",
2488
+ namespace: "`mj.search.*`",
2489
+ example: '`mj.search.searchEntries("performance")`'
2490
+ },
2491
+ {
2492
+ group: "analytics",
2493
+ label: "Analytics",
2494
+ namespace: "`mj.analytics.*`",
2495
+ example: "`mj.analytics.getStatistics()`"
2496
+ },
2497
+ {
2498
+ group: "relationships",
2499
+ label: "Relationships",
2500
+ namespace: "`mj.relationships.*`",
2501
+ example: '`mj.relationships.linkEntries(1, 2, "implements")`'
2502
+ },
2483
2503
  { group: "io", label: "IO", namespace: "`mj.io.*`", example: '`mj.io.exportEntries("json")`' },
2484
- { group: "admin", label: "Admin", namespace: "`mj.admin.*`", example: "`mj.admin.rebuildVectorIndex()`" },
2485
- { group: "github", label: "GitHub", namespace: "`mj.github.*`", example: '`mj.github.getGithubIssues({ state: "open" })`' },
2486
- { group: "backup", label: "Backup", namespace: "`mj.backup.*`", example: "`mj.backup.backupJournal()`" },
2487
- { group: "team", label: "Team", namespace: "`mj.team.*`", example: '`mj.team.teamCreateEntry("Team update")`' }
2504
+ {
2505
+ group: "admin",
2506
+ label: "Admin",
2507
+ namespace: "`mj.admin.*`",
2508
+ example: "`mj.admin.rebuildVectorIndex()`"
2509
+ },
2510
+ {
2511
+ group: "github",
2512
+ label: "GitHub",
2513
+ namespace: "`mj.github.*`",
2514
+ example: '`mj.github.getGithubIssues({ state: "open" })`'
2515
+ },
2516
+ {
2517
+ group: "backup",
2518
+ label: "Backup",
2519
+ namespace: "`mj.backup.*`",
2520
+ example: "`mj.backup.backupJournal()`"
2521
+ },
2522
+ {
2523
+ group: "team",
2524
+ label: "Team",
2525
+ namespace: "`mj.team.*`",
2526
+ example: '`mj.team.teamCreateEntry("Team update")`'
2527
+ }
2488
2528
  ];
2489
2529
  function buildCodeModeInstructions(groups) {
2490
- const rows = CODE_MODE_NAMESPACE_ROWS.filter((r) => groups.has(r.group)).map((r) => `| ${r.label.padEnd(13)} | ${r.namespace.padEnd(20)} | ${r.example.padEnd(50)} |`).join("\n");
2530
+ const rows = CODE_MODE_NAMESPACE_ROWS.filter((r) => groups.has(r.group)).map(
2531
+ (r) => `| ${r.label.padEnd(13)} | ${r.namespace.padEnd(20)} | ${r.example.padEnd(50)} |`
2532
+ ).join("\n");
2491
2533
  const fullSection = CODE_MODE_FULL_TEXT;
2492
2534
  const tableStart = fullSection.indexOf("| Group");
2493
2535
  const tableEnd = fullSection.indexOf("\n\n**Features**");
@@ -4173,21 +4215,25 @@ function getGitHubResourceDefinitions() {
4173
4215
  error: kanbanResult.reason
4174
4216
  });
4175
4217
  }
4176
- let milestoneSummary = null;
4177
- if (milestoneResult.status === "fulfilled" && milestoneResult.value.length > 0) {
4178
- milestoneSummary = milestoneResult.value.map((ms) => {
4179
- const pct = milestoneCompletionPct(ms.openIssues, ms.closedIssues);
4180
- return {
4181
- number: ms.number,
4182
- title: ms.title,
4183
- state: ms.state,
4184
- openIssues: ms.openIssues,
4185
- closedIssues: ms.closedIssues,
4186
- completionPercentage: pct,
4187
- dueOn: ms.dueOn
4188
- };
4189
- });
4218
+ let milestoneSummary = { openCount: 0, items: [] };
4219
+ if (milestoneResult.status === "fulfilled") {
4220
+ milestoneSummary = {
4221
+ openCount: milestoneResult.value.length,
4222
+ items: milestoneResult.value.map((ms) => {
4223
+ const pct = milestoneCompletionPct(ms.openIssues, ms.closedIssues);
4224
+ return {
4225
+ number: ms.number,
4226
+ title: ms.title,
4227
+ state: ms.state,
4228
+ openIssues: ms.openIssues,
4229
+ closedIssues: ms.closedIssues,
4230
+ completionPercentage: pct,
4231
+ dueOn: ms.dueOn
4232
+ };
4233
+ })
4234
+ };
4190
4235
  } else if (milestoneResult.status === "rejected") {
4236
+ milestoneSummary = null;
4191
4237
  logger.debug("Failed to fetch milestones", {
4192
4238
  module: "RESOURCE",
4193
4239
  operation: "github-status",
@@ -4933,7 +4979,7 @@ function getHelpResourceDefinitions() {
4933
4979
  var toolIndexModule = null;
4934
4980
  async function getAllToolDefinitionsAsync(context) {
4935
4981
  try {
4936
- toolIndexModule ??= await import('./tools-CXR2FEB2.js');
4982
+ toolIndexModule ??= await import('./tools-MNMGDTQI.js');
4937
4983
  if (toolIndexModule === null) return [];
4938
4984
  const tools = toolIndexModule.getTools(context.db, null);
4939
4985
  return tools.map((t) => ({
@@ -6489,15 +6535,30 @@ function registerPrompts(server, prompts, db, teamDb) {
6489
6535
  ...promptDef.icons ? { icons: promptDef.icons } : {}
6490
6536
  },
6491
6537
  (providedArgs) => {
6492
- const args = providedArgs;
6493
- const promptResult = getPrompt(promptDef.name, args, db, teamDb);
6494
- const result = {
6495
- messages: promptResult.messages.map((m) => ({
6496
- role: m.role,
6497
- content: m.content
6498
- }))
6499
- };
6500
- return Promise.resolve(result);
6538
+ try {
6539
+ const args = providedArgs;
6540
+ const promptResult = getPrompt(promptDef.name, args, db, teamDb);
6541
+ const result = {
6542
+ messages: promptResult.messages.map((m) => ({
6543
+ role: m.role,
6544
+ content: m.content
6545
+ }))
6546
+ };
6547
+ return Promise.resolve(result);
6548
+ } catch (err) {
6549
+ const message = err instanceof Error ? err.message : String(err);
6550
+ return Promise.resolve({
6551
+ messages: [
6552
+ {
6553
+ role: "user",
6554
+ content: {
6555
+ type: "text",
6556
+ text: `[Prompt handler error] ${message}`
6557
+ }
6558
+ }
6559
+ ]
6560
+ });
6561
+ }
6501
6562
  }
6502
6563
  );
6503
6564
  }
@@ -35,7 +35,7 @@ var ERROR_SUGGESTIONS = [
35
35
  {
36
36
  pattern: /SQLITE_CONSTRAINT|unique constraint/i,
37
37
  suggestion: "A uniqueness or integrity constraint was violated. Check input values.",
38
- code: "VALIDATION_FAILED"
38
+ code: "VALIDATION_ERROR"
39
39
  },
40
40
  // Disk / space patterns
41
41
  {
@@ -46,18 +46,18 @@ var ERROR_SUGGESTIONS = [
46
46
  {
47
47
  pattern: /malformed|invalid json|unexpected token/i,
48
48
  suggestion: "The input appears malformed. Check the format and try again.",
49
- code: "VALIDATION_FAILED"
49
+ code: "VALIDATION_ERROR"
50
50
  },
51
51
  // Schema / types patterns
52
52
  {
53
53
  pattern: /invalid input syntax for type|requires a.*column/i,
54
54
  suggestion: "The provided value is not valid for the assigned type.",
55
- code: "VALIDATION_FAILED"
55
+ code: "VALIDATION_ERROR"
56
56
  },
57
57
  {
58
58
  pattern: /^Missing required parameters:/i,
59
59
  suggestion: "Provide all required parameters in your request.",
60
- code: "VALIDATION_FAILED"
60
+ code: "VALIDATION_ERROR"
61
61
  },
62
62
  // Codemode / Sandbox patterns
63
63
  {
@@ -68,7 +68,7 @@ var ERROR_SUGGESTIONS = [
68
68
  {
69
69
  pattern: /code validation failed/i,
70
70
  suggestion: "Check for blocked patterns. Use mj.* API instead.",
71
- code: "VALIDATION_FAILED"
71
+ code: "VALIDATION_ERROR"
72
72
  },
73
73
  {
74
74
  pattern: /sandbox.*not initialized/i,
@@ -148,7 +148,7 @@ var QueryError = class extends MemoryJournalMcpError {
148
148
  };
149
149
  var ValidationError = class extends MemoryJournalMcpError {
150
150
  constructor(message, details) {
151
- super(message, "VALIDATION_FAILED", "validation" /* VALIDATION */, {
151
+ super(message, "VALIDATION_ERROR", "validation" /* VALIDATION */, {
152
152
  suggestion: "Check input parameters against the tool schema",
153
153
  recoverable: false,
154
154
  details
@@ -1,5 +1,5 @@
1
1
  import { transformAutoReturn } from './chunk-OKOVZ5QE.js';
2
- import { GitHubIntegration, resolveAuthor, logger, ValidationError, ResourceNotFoundError, assertSafeDirectoryPath, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-IWKLHSPU.js';
2
+ import { GitHubIntegration, resolveAuthor, logger, ValidationError, ResourceNotFoundError, assertSafeDirectoryPath, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-WXDEVIFL.js';
3
3
  import { z, ZodError } from 'zod';
4
4
  import { open, stat, mkdir, rename, appendFile, readdir, readFile } from 'fs/promises';
5
5
  import { tmpdir } from 'os';
@@ -59,7 +59,7 @@ async function resolveIssueUrl(context, projectNumber, issueNumber, existingUrl)
59
59
  ([_, v]) => v.project_number === projectNumber
60
60
  );
61
61
  if (entry) {
62
- const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-2TFMXHIJ.js');
62
+ const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-YODGZH3K.js');
63
63
  const targetGithub = new GitHubIntegration2(entry[1].path);
64
64
  const repoInfo = await targetGithub.getRepoInfo();
65
65
  if (repoInfo.owner && repoInfo.repo) {
@@ -208,7 +208,7 @@ var CreateEntrySchemaMcp = z.object({
208
208
  share_with_team: z.boolean().optional().default(false)
209
209
  });
210
210
  var GetEntryByIdSchema = z.object({
211
- entry_id: z.number(),
211
+ entry_id: z.number().int(),
212
212
  include_relationships: z.boolean().optional().default(true)
213
213
  });
214
214
  var GetEntryByIdSchemaMcp = z.object({
@@ -247,6 +247,7 @@ var EntryByIdOutputSchema = z.object({
247
247
  error: z.string().optional()
248
248
  }).extend(ErrorFieldsMixin.shape);
249
249
  var TestSimpleOutputSchema = z.object({
250
+ success: z.boolean().optional(),
250
251
  message: z.string()
251
252
  }).extend(ErrorFieldsMixin.shape);
252
253
  var TagsListOutputSchema = z.object({
@@ -364,6 +365,7 @@ function getCoreTools(context) {
364
365
  }
365
366
  const { score: importance, breakdown: importanceBreakdown } = db.calculateImportance(entry_id);
366
367
  const result = {
368
+ success: true,
367
369
  entry,
368
370
  importance,
369
371
  importanceBreakdown
@@ -389,7 +391,7 @@ function getCoreTools(context) {
389
391
  try {
390
392
  const { limit, is_personal } = GetRecentEntriesSchema.parse(params);
391
393
  const entries = db.getRecentEntries(limit, is_personal);
392
- return { entries, count: entries.length };
394
+ return { success: true, entries, count: entries.length };
393
395
  } catch (err) {
394
396
  return formatHandlerError(err);
395
397
  }
@@ -425,7 +427,7 @@ function getCoreTools(context) {
425
427
  handler: (params) => {
426
428
  try {
427
429
  const { message } = TestSimpleSchema.parse(params);
428
- return { message: `Test response: ${message}` };
430
+ return { success: true, message: `Test response: ${message}` };
429
431
  } catch (err) {
430
432
  return formatHandlerError(err);
431
433
  }
@@ -443,7 +445,7 @@ function getCoreTools(context) {
443
445
  try {
444
446
  const rawTags = db.listTags();
445
447
  const tags = rawTags.map((t) => ({ name: t.name, count: t.usageCount }));
446
- return { tags, count: tags.length };
448
+ return { success: true, tags, count: tags.length };
447
449
  } catch (err) {
448
450
  return formatHandlerError(err);
449
451
  }
@@ -470,6 +472,30 @@ function mergeAndDedup(personal, team, limit) {
470
472
  }
471
473
  return limit !== void 0 ? merged.slice(0, limit) : merged;
472
474
  }
475
+ function passMetadataFilters(entry, options, db) {
476
+ if (options.isPersonal !== void 0 && entry.isPersonal !== options.isPersonal) return false;
477
+ if (options.projectNumber !== void 0 && entry.projectNumber !== options.projectNumber)
478
+ return false;
479
+ if (options.issueNumber !== void 0 && entry.issueNumber !== options.issueNumber) return false;
480
+ if (options.prNumber !== void 0 && entry.prNumber !== options.prNumber) return false;
481
+ if (options.prStatus !== void 0 && entry.prStatus !== options.prStatus) return false;
482
+ if (options.workflowRunId !== void 0 && entry.workflowRunId !== options.workflowRunId)
483
+ return false;
484
+ if (options.entryType && entry.entryType !== options.entryType) return false;
485
+ if (options.startDate) {
486
+ const entryDate = entry.timestamp.split("T")[0] ?? "";
487
+ if (entryDate < options.startDate) return false;
488
+ }
489
+ if (options.endDate) {
490
+ const entryDate = entry.timestamp.split("T")[0] ?? "";
491
+ if (entryDate > options.endDate) return false;
492
+ }
493
+ if (options.tags && options.tags.length > 0) {
494
+ const entryTags = Array.isArray(entry.tags) ? entry.tags : db.getTagsForEntry(entry.id);
495
+ if (!options.tags.some((t) => entryTags.includes(t))) return false;
496
+ }
497
+ return true;
498
+ }
473
499
 
474
500
  // src/handlers/tools/search/auto.ts
475
501
  var QUESTION_PATTERNS = [
@@ -603,7 +629,7 @@ async function hybridSearch(query, db, vectorManager, options) {
603
629
  for (const id of sortedIds) {
604
630
  const entry = entriesMap.get(id);
605
631
  if (!entry) continue;
606
- if (options.isPersonal !== void 0 && entry.isPersonal !== options.isPersonal) continue;
632
+ if (!passMetadataFilters(entry, options, db)) continue;
607
633
  entries.push({ ...entry, source: "personal" });
608
634
  }
609
635
  return { entries, fusionScores };
@@ -738,7 +764,9 @@ function getSearchTools(context) {
738
764
  ...formatHandlerError(
739
765
  new ValidationError(
740
766
  "Search requires either a query string or at least one filter",
741
- { suggestion: "Provide a search query or use get_recent_entries instead" }
767
+ {
768
+ suggestion: "Provide a search query or use get_recent_entries instead"
769
+ }
742
770
  )
743
771
  ),
744
772
  entries: [],
@@ -766,9 +794,10 @@ function getSearchTools(context) {
766
794
  const result = ftsSearch(input.query, db, teamDb, searchOptions);
767
795
  return { ...result, searchMode: "fts (fallback)" };
768
796
  }
797
+ const internalLimit = hasFilters ? Math.min(Math.max(input.limit * 10, 100), 1e3) : input.limit * 2;
769
798
  const semanticResults = await vectorManager.search(
770
799
  query,
771
- input.limit,
800
+ internalLimit,
772
801
  0.25
773
802
  );
774
803
  const entryIds = semanticResults.map((r) => r.entryId);
@@ -776,11 +805,11 @@ function getSearchTools(context) {
776
805
  const entries = semanticResults.map((r) => {
777
806
  const entry = entriesMap.get(r.entryId);
778
807
  if (!entry) return null;
779
- if (input.is_personal !== void 0 && entry.isPersonal !== input.is_personal)
780
- return null;
808
+ if (!passMetadataFilters(entry, searchOptions, db)) return null;
781
809
  return { ...entry, source: "personal" };
782
810
  }).filter((e) => e !== null);
783
811
  return {
812
+ success: true,
784
813
  entries,
785
814
  count: entries.length,
786
815
  searchMode: isAuto ? "semantic (auto)" : "semantic"
@@ -798,6 +827,7 @@ function getSearchTools(context) {
798
827
  searchOptions
799
828
  );
800
829
  return {
830
+ success: true,
801
831
  entries,
802
832
  count: entries.length,
803
833
  searchMode: isAuto ? "hybrid (auto)" : "hybrid"
@@ -808,6 +838,7 @@ function getSearchTools(context) {
808
838
  const result = ftsSearch(input.query, db, teamDb, searchOptions);
809
839
  return {
810
840
  ...result,
841
+ success: true,
811
842
  searchMode: isAuto ? "fts (auto)" : "fts"
812
843
  };
813
844
  }
@@ -868,9 +899,13 @@ function getSearchTools(context) {
868
899
  teamEntries.map((e) => ({ ...e, source: "team" })),
869
900
  input.limit
870
901
  );
871
- return { entries: merged, count: merged.length };
902
+ return { success: true, entries: merged, count: merged.length };
872
903
  }
873
- return { entries: personalEntries, count: personalEntries.length };
904
+ return {
905
+ success: true,
906
+ entries: personalEntries,
907
+ count: personalEntries.length
908
+ };
874
909
  } catch (err) {
875
910
  return formatHandlerError(err);
876
911
  }
@@ -912,17 +947,19 @@ function getSearchTools(context) {
912
947
  count: 0
913
948
  };
914
949
  }
950
+ const hasFilters = input.is_personal !== void 0 || input.tags !== void 0 || input.entry_type !== void 0 || input.start_date !== void 0 || input.end_date !== void 0;
951
+ const internalLimit = hasFilters ? Math.min(Math.max(input.limit * 10, 100), 1e3) : input.limit * 2;
915
952
  let results;
916
953
  if (input.entry_id !== void 0) {
917
954
  results = await vectorManager.searchByEntryId(
918
955
  input.entry_id,
919
- input.limit ?? 10,
956
+ internalLimit,
920
957
  input.similarity_threshold ?? 0.25
921
958
  );
922
959
  } else {
923
960
  results = await vectorManager.search(
924
961
  input.query ?? "",
925
- input.limit ?? 10,
962
+ internalLimit,
926
963
  input.similarity_threshold ?? 0.25
927
964
  );
928
965
  }
@@ -931,29 +968,21 @@ function getSearchTools(context) {
931
968
  const entries = results.map((r) => {
932
969
  const entry = entriesMap.get(r.entryId);
933
970
  if (!entry) return null;
934
- if (input.is_personal !== void 0 && entry.isPersonal !== input.is_personal)
935
- return null;
936
- if (input.tags && input.tags.length > 0) {
937
- const entryTags = db.getTagsForEntry(entry.id);
938
- if (!input.tags.some((t) => entryTags.includes(t))) return null;
939
- }
940
- if (input.entry_type && entry.entryType !== input.entry_type)
941
- return null;
942
- if (input.start_date) {
943
- const entryDate = entry.timestamp.split("T")[0] ?? "";
944
- if (entryDate < input.start_date) return null;
945
- }
946
- if (input.end_date) {
947
- const entryDate = entry.timestamp.split("T")[0] ?? "";
948
- if (entryDate > input.end_date) return null;
949
- }
971
+ const filterOptions = {
972
+ isPersonal: input.is_personal,
973
+ tags: input.tags,
974
+ entryType: input.entry_type,
975
+ startDate: input.start_date,
976
+ endDate: input.end_date
977
+ };
978
+ if (!passMetadataFilters(entry, filterOptions, db)) return null;
950
979
  if (input.entry_id !== void 0 && entry.id === input.entry_id)
951
980
  return null;
952
981
  return {
953
982
  ...entry,
954
983
  similarity: Math.round(r.score * 100) / 100
955
984
  };
956
- }).filter((e) => e !== null);
985
+ }).filter((e) => e !== null).slice(0, input.limit);
957
986
  const stats = vectorManager.getStats();
958
987
  const isIndexEmpty = stats.itemCount === 0;
959
988
  const includeHint = input.hint_on_empty ?? true;
@@ -962,6 +991,7 @@ function getSearchTools(context) {
962
991
  const allNoise = entries.length > 0 && bestSimilarity < QUALITY_FLOOR2;
963
992
  const hint = isIndexEmpty && includeHint ? "No entries in vector index. Use rebuild_vector_index to index existing entries." : entries.length === 0 && includeHint ? `No entries matched your query above the similarity threshold (${String(input.similarity_threshold ?? 0.25)}). Try lowering similarity_threshold (e.g., 0.15) for broader matches.` : allNoise ? `Results may be noise \u2014 best similarity (${String(bestSimilarity)}) is below quality floor (${String(QUALITY_FLOOR2)}). Try a more specific query or raise similarity_threshold to filter weak matches.` : void 0;
964
993
  return {
994
+ success: true,
965
995
  query: input.query,
966
996
  ...input.entry_id !== void 0 ? { entryId: input.entry_id } : {},
967
997
  entries,
@@ -1120,7 +1150,7 @@ function getAnalyticsTools(context) {
1120
1150
  end_date,
1121
1151
  project_breakdown
1122
1152
  );
1123
- return { ...stats, groupBy: group_by };
1153
+ return { success: true, ...stats, groupBy: group_by };
1124
1154
  } catch (err) {
1125
1155
  return formatHandlerError(err);
1126
1156
  }
@@ -1162,6 +1192,7 @@ function getAnalyticsTools(context) {
1162
1192
  );
1163
1193
  if (!projectsResult[0] || projectsResult[0].values.length === 0) {
1164
1194
  return {
1195
+ success: true,
1165
1196
  project_count: 0,
1166
1197
  total_entries: 0,
1167
1198
  projects: [],
@@ -1233,6 +1264,7 @@ function getAnalyticsTools(context) {
1233
1264
  )
1234
1265
  }));
1235
1266
  return {
1267
+ success: true,
1236
1268
  project_count: projects.length,
1237
1269
  total_entries: totalEntries,
1238
1270
  projects: projects.map((p) => ({
@@ -1338,6 +1370,24 @@ function getRelationshipTools(context) {
1338
1370
  success: false
1339
1371
  };
1340
1372
  }
1373
+ const fromEntry = db.getEntryById(input.from_entry_id);
1374
+ if (!fromEntry || fromEntry.deletedAt) {
1375
+ return {
1376
+ ...formatHandlerError(
1377
+ new ResourceNotFoundError("Entry", String(input.from_entry_id))
1378
+ ),
1379
+ success: false
1380
+ };
1381
+ }
1382
+ const toEntry = db.getEntryById(input.to_entry_id);
1383
+ if (!toEntry || toEntry.deletedAt) {
1384
+ return {
1385
+ ...formatHandlerError(
1386
+ new ResourceNotFoundError("Entry", String(input.to_entry_id))
1387
+ ),
1388
+ success: false
1389
+ };
1390
+ }
1341
1391
  const existingRelationships = db.getRelationships(input.from_entry_id);
1342
1392
  const existing = existingRelationships.find(
1343
1393
  (r) => r.fromEntryId === input.from_entry_id && r.toEntryId === input.to_entry_id && r.relationshipType === input.relationship_type
@@ -1400,6 +1450,9 @@ function getRelationshipTools(context) {
1400
1450
  const entry = db.getEntryById(input.entry_id);
1401
1451
  if (!entry) {
1402
1452
  return {
1453
+ ...formatHandlerError(
1454
+ new ResourceNotFoundError("Entry", String(input.entry_id))
1455
+ ),
1403
1456
  success: false,
1404
1457
  entry_count: 0,
1405
1458
  relationship_count: 0,
@@ -2612,7 +2665,8 @@ var CreateGitHubIssueWithEntryOutputSchema = z.object({
2612
2665
  project: z.object({
2613
2666
  projectNumber: z.number(),
2614
2667
  added: z.boolean(),
2615
- message: z.string(),
2668
+ message: z.string().optional(),
2669
+ error: z.string().optional(),
2616
2670
  initialStatus: z.object({
2617
2671
  status: z.string(),
2618
2672
  set: z.boolean()
@@ -2711,6 +2765,7 @@ var DeleteMilestoneOutputSchema = z.object({
2711
2765
  instruction: z.string().optional()
2712
2766
  }).extend(ErrorFieldsMixin.shape);
2713
2767
  var RepoInsightsOutputSchema = z.object({
2768
+ success: z.boolean().optional(),
2714
2769
  owner: z.string().optional(),
2715
2770
  repo: z.string().optional(),
2716
2771
  section: z.string().optional(),
@@ -4304,6 +4359,7 @@ function getGitHubInsightsTools(context) {
4304
4359
  const repo = resolved.repo;
4305
4360
  const section = input.sections;
4306
4361
  const result = {
4362
+ success: true,
4307
4363
  owner,
4308
4364
  repo,
4309
4365
  section
@@ -4891,6 +4947,10 @@ var TeamSemanticSearchSchema = z.object({
4891
4947
  entry_id: z.number().optional().describe("Find entries related to this entry ID"),
4892
4948
  limit: z.number().max(500).optional().default(10),
4893
4949
  similarity_threshold: z.number().optional().default(0.25),
4950
+ tags: z.array(z.string()).optional(),
4951
+ entry_type: z.enum(ENTRY_TYPES).optional(),
4952
+ start_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
4953
+ end_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
4894
4954
  hint_on_empty: z.boolean().optional().default(true).describe("Include hint when no results found (default: true)")
4895
4955
  });
4896
4956
  var TeamSemanticSearchSchemaMcp = z.object({
@@ -4898,6 +4958,10 @@ var TeamSemanticSearchSchemaMcp = z.object({
4898
4958
  entry_id: relaxedNumber().optional().describe("Find entries related to this entry ID"),
4899
4959
  limit: relaxedNumber().optional().default(10),
4900
4960
  similarity_threshold: relaxedNumber().optional().default(0.25),
4961
+ tags: z.array(z.string()).optional(),
4962
+ entry_type: z.string().optional(),
4963
+ start_date: z.string().optional(),
4964
+ end_date: z.string().optional(),
4901
4965
  hint_on_empty: z.boolean().optional().default(true).describe("Include hint when no results found (default: true)")
4902
4966
  });
4903
4967
  var TeamAddToVectorIndexSchema = z.object({
@@ -5148,11 +5212,12 @@ function getTeamSearchTools(context) {
5148
5212
  return { ...TEAM_DB_ERROR_RESPONSE };
5149
5213
  }
5150
5214
  const { query, tags, limit } = TeamSearchSchema.parse(params);
5215
+ const searchLimit = tags && tags.length > 0 ? Math.max(limit * 5, 50) : limit;
5151
5216
  let entries;
5152
5217
  if (query) {
5153
- entries = teamDb.searchEntries(query, { limit });
5218
+ entries = teamDb.searchEntries(query, { limit: searchLimit });
5154
5219
  } else {
5155
- entries = teamDb.getRecentEntries(limit);
5220
+ entries = teamDb.getRecentEntries(searchLimit);
5156
5221
  }
5157
5222
  if (tags && tags.length > 0) {
5158
5223
  const entryIds = entries.map((e) => e.id);
@@ -5178,6 +5243,7 @@ function getTeamSearchTools(context) {
5178
5243
  });
5179
5244
  }
5180
5245
  }
5246
+ entries = entries.slice(0, limit);
5181
5247
  const authorMap = batchFetchAuthors(
5182
5248
  teamDb,
5183
5249
  entries.map((e) => e.id)
@@ -5186,7 +5252,7 @@ function getTeamSearchTools(context) {
5186
5252
  ...e,
5187
5253
  author: authorMap.get(e.id) ?? null
5188
5254
  }));
5189
- return { entries: enriched, count: enriched.length };
5255
+ return { success: true, entries: enriched, count: enriched.length };
5190
5256
  } catch (err) {
5191
5257
  return formatHandlerError(err);
5192
5258
  }
@@ -5229,7 +5295,7 @@ function getTeamSearchTools(context) {
5229
5295
  ...e,
5230
5296
  author: authorMap.get(e.id) ?? null
5231
5297
  }));
5232
- return { entries: enriched, count: enriched.length };
5298
+ return { success: true, entries: enriched, count: enriched.length };
5233
5299
  } catch (err) {
5234
5300
  return formatHandlerError(err);
5235
5301
  }
@@ -5638,6 +5704,17 @@ function getTeamRelationshipTools(context) {
5638
5704
  const { entry_id, tag, depth } = TeamVisualizeRelationshipsSchema.parse(params);
5639
5705
  let entryIds = [];
5640
5706
  if (entry_id !== void 0) {
5707
+ const fromEntry = teamDb.getEntryById(entry_id);
5708
+ if (!fromEntry) {
5709
+ return {
5710
+ success: false,
5711
+ error: `Team entry ${String(entry_id)} not found`,
5712
+ code: "RESOURCE_NOT_FOUND",
5713
+ category: "resource",
5714
+ suggestion: "Verify the team entry ID and try again",
5715
+ recoverable: true
5716
+ };
5717
+ }
5641
5718
  entryIds = [entry_id];
5642
5719
  const visited = new Set(entryIds);
5643
5720
  let frontier = [...entryIds];
@@ -6083,17 +6160,19 @@ function getTeamVectorTools(context) {
6083
6160
  count: 0
6084
6161
  };
6085
6162
  }
6163
+ const hasFilters = input.tags !== void 0 || input.entry_type !== void 0 || input.start_date !== void 0 || input.end_date !== void 0;
6164
+ const internalLimit = hasFilters ? Math.min(Math.max(input.limit * 10, 100), 1e3) : input.limit * 2;
6086
6165
  let results;
6087
6166
  if (input.entry_id !== void 0) {
6088
6167
  results = await teamVectorManager.searchByEntryId(
6089
6168
  input.entry_id,
6090
- input.limit ?? 10,
6169
+ internalLimit,
6091
6170
  input.similarity_threshold ?? 0.25
6092
6171
  );
6093
6172
  } else {
6094
6173
  results = await teamVectorManager.search(
6095
6174
  input.query ?? "",
6096
- input.limit ?? 10,
6175
+ internalLimit,
6097
6176
  input.similarity_threshold ?? 0.25
6098
6177
  );
6099
6178
  }
@@ -6105,12 +6184,24 @@ function getTeamVectorTools(context) {
6105
6184
  if (!entry) return null;
6106
6185
  if (input.entry_id !== void 0 && entry.id === input.entry_id)
6107
6186
  return null;
6187
+ if (!passMetadataFilters(
6188
+ entry,
6189
+ {
6190
+ tags: input.tags,
6191
+ entryType: input.entry_type,
6192
+ startDate: input.start_date,
6193
+ endDate: input.end_date
6194
+ },
6195
+ teamDb
6196
+ )) {
6197
+ return null;
6198
+ }
6108
6199
  return {
6109
6200
  ...entry,
6110
6201
  author: authorMap.get(r.entryId) ?? null,
6111
6202
  similarity: Math.round(r.score * 100) / 100
6112
6203
  };
6113
- }).filter((e) => e !== null);
6204
+ }).filter((e) => e !== null).slice(0, input.limit);
6114
6205
  const stats = teamVectorManager.getStats();
6115
6206
  const isIndexEmpty = stats.itemCount === 0;
6116
6207
  const includeHint = input.hint_on_empty ?? true;
@@ -6118,6 +6209,7 @@ function getTeamVectorTools(context) {
6118
6209
  const allNoise = entries.length > 0 && bestSimilarity < QUALITY_FLOOR;
6119
6210
  const hint = isIndexEmpty && includeHint ? "No entries in team vector index. Use team_rebuild_vector_index to index existing entries." : entries.length === 0 && includeHint ? `No entries matched your query above the similarity threshold (${String(input.similarity_threshold ?? 0.25)}). Try lowering similarity_threshold (e.g., 0.15) for broader matches.` : allNoise ? `Results may be noise \u2014 best similarity (${String(bestSimilarity)}) is below quality floor (${String(QUALITY_FLOOR)}). Try a more specific query or raise similarity_threshold to filter weak matches.` : void 0;
6120
6211
  return {
6212
+ success: true,
6121
6213
  query: input.query,
6122
6214
  ...input.entry_id !== void 0 ? { entryId: input.entry_id } : {},
6123
6215
  entries,
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
- import { VERSION, createServer } from './chunk-GR4T3SRW.js';
2
- import { DEFAULT_AUDIT_LOG_MAX_SIZE_BYTES } from './chunk-ORV7ZZOE.js';
1
+ import { VERSION, createServer } from './chunk-CHWIPVQN.js';
2
+ import { DEFAULT_AUDIT_LOG_MAX_SIZE_BYTES } from './chunk-ZJJD2F5T.js';
3
3
  import './chunk-OKOVZ5QE.js';
4
- import { logger } from './chunk-IWKLHSPU.js';
4
+ import { logger } from './chunk-WXDEVIFL.js';
5
5
  import { Command } from 'commander';
6
6
  import * as fs from 'fs';
7
7
 
@@ -0,0 +1 @@
1
+ export { GitHubIntegration } from './chunk-WXDEVIFL.js';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- export { VERSION, createServer } from './chunk-GR4T3SRW.js';
2
- export { META_GROUPS, TOOL_GROUPS, calculateTokenSavings, filterTools, getAllToolNames, getFilterSummary, getToolFilterFromEnv, getToolGroup, isToolEnabled, parseToolFilter } from './chunk-ORV7ZZOE.js';
1
+ export { VERSION, createServer } from './chunk-CHWIPVQN.js';
2
+ export { META_GROUPS, TOOL_GROUPS, calculateTokenSavings, filterTools, getAllToolNames, getFilterSummary, getToolFilterFromEnv, getToolGroup, isToolEnabled, parseToolFilter } from './chunk-ZJJD2F5T.js';
3
3
  import './chunk-OKOVZ5QE.js';
4
- export { logger } from './chunk-IWKLHSPU.js';
4
+ export { logger } from './chunk-WXDEVIFL.js';
5
5
 
6
6
  // src/types/index.ts
7
7
  var DEFAULT_CONFIG = {
@@ -1,3 +1,3 @@
1
- export { callTool, getGlobalAuditLogger, getTools, initializeAuditLogger } from './chunk-ORV7ZZOE.js';
1
+ export { callTool, getGlobalAuditLogger, getTools, initializeAuditLogger } from './chunk-ZJJD2F5T.js';
2
2
  import './chunk-OKOVZ5QE.js';
3
- import './chunk-IWKLHSPU.js';
3
+ import './chunk-WXDEVIFL.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memory-journal-mcp",
3
- "version": "7.2.0",
3
+ "version": "7.3.0",
4
4
  "description": "Project context management for AI-assisted development - Persistent knowledge graphs and intelligent context recall across fragmented AI threads",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,8 +17,8 @@
17
17
  "scripts": {
18
18
  "build": "tsup",
19
19
  "dev": "tsc --watch",
20
- "lint": "eslint src/",
21
- "lint:fix": "eslint src/ --fix",
20
+ "lint": "eslint src/ tests/",
21
+ "lint:fix": "eslint src/ tests/ --fix",
22
22
  "typecheck": "tsc --noEmit",
23
23
  "check": "npm run lint && npm run typecheck",
24
24
  "test": "vitest run",
@@ -76,7 +76,7 @@
76
76
  "@playwright/test": "^1.59.1",
77
77
  "@types/better-sqlite3": "^7.6.13",
78
78
  "@types/express": "^5.0.6",
79
- "@types/node": "^25.5.2",
79
+ "@types/node": "^25.6.0",
80
80
  "@vitest/coverage-v8": "^4.1.3",
81
81
  "eslint": "^10.2.0",
82
82
  "globals": "^17.4.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neverinfamous-agent-skills",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Foundational AI agent metacognitive skills and workflows for the Adamic ecosystem.",
5
5
  "type": "module",
6
6
  "main": "README.md",
@@ -1 +0,0 @@
1
- export { GitHubIntegration } from './chunk-IWKLHSPU.js';