prism-mcp-server 2.1.2 β†’ 2.3.1

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/prism-mcp-server?color=cb0000&label=npm)](https://www.npmjs.com/package/prism-mcp-server)
4
4
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-00ADD8?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0xMiAyTDIgN2wxMCA1IDEwLTUtMTAtNXpNMiAxN2wxMCA1IDEwLTV2LTJMMTI0djJMMiA5djh6Ii8+PC9zdmc+)](https://registry.modelcontextprotocol.io)
5
- [![Glama](https://img.shields.io/badge/Glama-listed-FF5601)](https://glama.ai/mcp/servers/@dcostenco/prism-mcp)
5
+ [![Glama](https://img.shields.io/badge/Glama-listed-FF5601)](https://glama.ai/mcp/servers/dcostenco/BCBA)
6
6
  [![Smithery](https://img.shields.io/badge/Smithery-listed-6B4FBB)](https://smithery.ai/server/prism-mcp-server)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
@@ -14,9 +14,26 @@
14
14
 
15
15
  ---
16
16
 
17
- ## What's New in v2.0 "Mind Palace" 🧠
17
+ ## What's New in v2.3.0 β€” AI Reasoning Engine 🧠
18
18
 
19
- Prism MCP has been completely rebuilt from the ground up to support **local-first workflows**, **visual agent memory**, and **multi-client synchronization**.
19
+ | Feature | Description |
20
+ |---|---|
21
+ | πŸ•ΈοΈ **Neural Graph** | Interactive knowledge graph on the Mind Palace Dashboard β€” visualize how projects connect through shared keywords and categories using Vis.js force-directed layout. |
22
+ | πŸ›‘οΈ **Prompt Injection Shield** | Gemini-powered security scan in `session_health_check` β€” detects system override attempts, jailbreaks, and data exfiltration hidden in agent memory. Tuned to avoid false positives on normal dev commands. |
23
+ | 🧬 **Fact Merger** | Async LLM contradiction resolution on every handoff save β€” if old context says "Postgres" and new says "MySQL", Gemini silently merges the facts in the background. Zero latency impact (fire-and-forget). |
24
+
25
+ <details>
26
+ <summary><strong>What's in v2.2.0</strong></summary>
27
+
28
+ | Feature | Description |
29
+ |---|---|
30
+ | 🩺 **Brain Health Check** | `session_health_check` β€” like Unix `fsck` for your agent's memory. Detects missing embeddings, duplicate entries, orphaned handoffs, and stale rollups. Use `auto_fix: true` to repair automatically. |
31
+ | πŸ“Š **Mind Palace Health** | Brain health indicator on the Mind Palace Dashboard β€” see your memory integrity at a glance. |
32
+
33
+ </details>
34
+
35
+ <details>
36
+ <summary><strong>What's in v2.0 "Mind Palace"</strong></summary>
20
37
 
21
38
  | Feature | Description |
22
39
  |---|---|
@@ -29,6 +46,33 @@ Prism MCP has been completely rebuilt from the ground up to support **local-firs
29
46
  | πŸ“ **Code Mode Templates** | 8 pre-built QuickJS extraction templates for GitHub, Jira, OpenAPI, Slack, CSV, and DOM parsing β€” zero reasoning tokens. |
30
47
  | πŸ” **Reality Drift Detection** | Prism captures Git state on save and warns if files changed outside the agent's view. |
31
48
 
49
+ </details>
50
+
51
+ ---
52
+
53
+ ## How Prism Compares
54
+
55
+ | Feature | **Prism MCP** | [MCP Memory](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) | [Mem0](https://github.com/mem0ai/mem0) | [Mnemory](https://github.com/fpytloun/mnemory) | [Basic Memory](https://github.com/basicmachines-co/basic-memory) |
56
+ |---|---|---|---|---|---|
57
+ | **Storage** | SQLite (local) + Supabase (cloud) | JSON file | Postgres + Qdrant (hosted or self-hosted) | Qdrant + S3/MinIO | Markdown files |
58
+ | **Zero Config** | βœ… `npx -y prism-mcp-server` | βœ… | ❌ Requires Qdrant/Postgres | βœ… `uvx mnemory` | βœ… `pip install basic-memory` |
59
+ | **Semantic Search** | βœ… F32_BLOB vectors + FTS5 | ❌ | βœ… pgvector | βœ… Qdrant vectors | ❌ Text search only |
60
+ | **Knowledge Graph** | βœ… Neural Graph (Vis.js dashboard) | βœ… Entity/Relation model | ❌ | βœ… Relationship graph | βœ… Markdown links |
61
+ | **Time Travel** | βœ… `memory_history` / `memory_checkout` | ❌ | ❌ | ❌ | ❌ |
62
+ | **Fact Merging** | βœ… Async Gemini (fire-and-forget) | ❌ | βœ… Built-in | βœ… Contradiction resolution | ❌ |
63
+ | **Security Scan** | βœ… Prompt injection detection | ❌ | ❌ | βœ… Anti-injection in fsck | ❌ |
64
+ | **Health Check** | βœ… `session_health_check` (fsck) | ❌ | ❌ | βœ… 3-phase fsck | ❌ |
65
+ | **Visual Dashboard** | βœ… Mind Palace (localhost:3000) | ❌ | βœ… Cloud dashboard | βœ… Management UI | ❌ |
66
+ | **Multi-Agent Sync** | βœ… Real-time cross-client | ❌ | ❌ | ❌ Per-user isolation | ❌ |
67
+ | **Visual Memory** | βœ… Screenshot vault + auto-capture | ❌ | ❌ | βœ… Artifact store | ❌ |
68
+ | **Auto-Compaction** | βœ… Gemini rollups | ❌ | ❌ | ❌ | ❌ |
69
+ | **Morning Briefing** | βœ… Gemini synthesis | ❌ | ❌ | ❌ | ❌ |
70
+ | **OCC (Concurrency)** | βœ… Version-based | ❌ | ❌ | ❌ | ❌ |
71
+ | **MCP Native** | βœ… stdio (Claude Desktop, Cursor) | βœ… stdio | ❌ Python SDK / REST | βœ… HTTP + MCP | βœ… stdio |
72
+ | **Language** | TypeScript | TypeScript | Python | Python | Python |
73
+
74
+ > **When to choose Prism MCP:** You want MCP-native memory with zero infrastructure overhead, progressive context loading, and enterprise features (OCC, compaction, time travel, security scanning) that work directly in Claude Desktop β€” without running Qdrant, Postgres, or cloud services.
75
+
32
76
  ---
33
77
 
34
78
  ## Quick Start (Zero Config β€” Local Mode)
@@ -121,25 +165,6 @@ The dashboard auto-discovers all your projects and updates in real time.
121
165
 
122
166
  ---
123
167
 
124
- ## How Prism MCP Compares
125
-
126
- | Capability | **Prism MCP** | **Mem0** | **Zep** | **Basic Memory** |
127
- |---|---|---|---|---|
128
- | **Architecture** | MCP-native (single npm package) | Standalone service + MCP adapter | Standalone service + API | MCP-native (local files) |
129
- | **Storage** | SQLite (local) or Supabase (cloud) | Hybrid (vector + graph DBs) | PostgreSQL + Neo4j | Local markdown files |
130
- | **Local-First** | βœ… Full SQLite mode, zero cloud | ❌ Requires cloud/Docker | ❌ Requires PostgreSQL | βœ… Local files |
131
- | **Visual Dashboard** | βœ… Mind Palace UI at localhost:3000 | ❌ No UI | ❌ No UI | ❌ No UI |
132
- | **Time Travel** | βœ… Version history + checkout | ❌ No versioning | ❌ No versioning | ❌ No versioning |
133
- | **Multi-Agent Sync** | βœ… Telepathy (realtime IPC/CDC) | ❌ Siloed | ❌ Siloed | ❌ Single user |
134
- | **Auto-Capture** | βœ… HTML snapshots of dev server | ❌ Text only | ❌ Text only | ❌ Text only |
135
- | **Cold Start Fix** | βœ… MCP Prompts + Resources | ❌ Requires tool call | ❌ Requires tool call | ❌ Requires tool call |
136
- | **Progressive Loading** | βœ… quick / standard / deep | ❌ All-or-nothing | ❌ Fixed window | ❌ All-or-nothing |
137
- | **Semantic Search** | βœ… F32_BLOB vectors (local) or pgvector (cloud) | βœ… Qdrant/Chroma | βœ… Built-in | ❌ None |
138
- | **Concurrency Control** | βœ… OCC with version tracking | ❌ Last write wins | ❌ Last write wins | ❌ Single user |
139
- | **Setup Complexity** | Zero config (local mode) | Docker + API keys + vector DB | Docker + PostgreSQL + Neo4j | No setup needed |
140
-
141
- ---
142
-
143
168
  ## Integration Examples
144
169
 
145
170
  Copy-paste configs for popular MCP clients. All configs use the `npx` method.
@@ -301,7 +326,7 @@ graph TB
301
326
  | `knowledge_search` | Semantic search across accumulated knowledge |
302
327
  | `knowledge_forget` | Prune outdated or incorrect memories (4 modes + dry_run) |
303
328
  | `session_search_memory` | Vector similarity search across all sessions |
304
- | `backfill_embeddings` | Retroactively generate embeddings for existing entries |
329
+ | `session_compact_ledger` | Auto-compact old ledger entries via Gemini-powered summarization |
305
330
 
306
331
  ### v2.0 Advanced Memory Tools
307
332
 
@@ -312,6 +337,12 @@ graph TB
312
337
  | `session_save_image` | Save a screenshot/image to the visual memory vault |
313
338
  | `session_view_image` | Retrieve and display a saved image from the vault |
314
339
 
340
+ ### v2.2 Brain Health Tools
341
+
342
+ | Tool | Purpose | Key Args | Returns |
343
+ |------|---------|----------|---------|
344
+ | `session_health_check` | Scan brain for integrity issues (`fsck`) | `auto_fix` (boolean) | Health report & auto-repairs |
345
+
315
346
  ### Code Mode Templates (v2.1)
316
347
 
317
348
  Instead of writing custom JavaScript, pass a `template` name for instant extraction:
@@ -560,6 +591,8 @@ See [`vertex-ai/`](vertex-ai/) for setup and benchmarks.
560
591
  β”‚ β”œβ”€β”€ googleAi.ts # Gemini SDK wrapper
561
592
  β”‚ β”œβ”€β”€ executor.ts # QuickJS sandbox executor
562
593
  β”‚ β”œβ”€β”€ autoCapture.ts # Dev server HTML snapshot utility
594
+ β”‚ β”œβ”€β”€ healthCheck.ts # Brain integrity engine (v2.2.0) + security scanner (v2.3.0)
595
+ β”‚ β”œβ”€β”€ factMerger.ts # Async LLM contradiction resolution (v2.3.0)
563
596
  β”‚ β”œβ”€β”€ git.ts # Git state capture + drift detection
564
597
  β”‚ β”œβ”€β”€ embeddingApi.ts # Embedding generation (Gemini)
565
598
  β”‚ └── keywordExtractor.ts # Zero-dependency NLP keyword extraction
package/dist/config.js CHANGED
@@ -22,13 +22,12 @@
22
22
  // multi-tenant Row Level Security (RLS) for production hosting.
23
23
  export const SERVER_CONFIG = {
24
24
  name: "prism-mcp",
25
- version: "1.5.0",
25
+ version: "2.3.1",
26
26
  };
27
27
  // ─── Required: Brave Search API Key ───────────────────────────
28
28
  export const BRAVE_API_KEY = process.env.BRAVE_API_KEY;
29
29
  if (!BRAVE_API_KEY) {
30
- console.error("Error: BRAVE_API_KEY environment variable is required");
31
- process.exit(1);
30
+ console.error("Warning: BRAVE_API_KEY environment variable is missing. Search tools will return errors when called.");
32
31
  }
33
32
  // ─── Optional: Google Gemini API Key ──────────────────────────
34
33
  // Used by the gemini_research_paper_analysis tool.
@@ -68,6 +68,90 @@ export async function startDashboardServer() {
68
68
  res.writeHead(200, { "Content-Type": "application/json" });
69
69
  return res.end(JSON.stringify({ context, ledger, history }));
70
70
  }
71
+ // ─── API: Brain Health Check (v2.2.0) ───
72
+ if (url.pathname === "/api/health") {
73
+ try {
74
+ const { runHealthCheck } = await import("../utils/healthCheck.js");
75
+ const stats = await storage.getHealthStats(PRISM_USER_ID);
76
+ const report = runHealthCheck(stats);
77
+ res.writeHead(200, { "Content-Type": "application/json" });
78
+ return res.end(JSON.stringify(report));
79
+ }
80
+ catch (err) {
81
+ console.error("[Dashboard] Health check error:", err);
82
+ res.writeHead(200, { "Content-Type": "application/json" });
83
+ return res.end(JSON.stringify({
84
+ status: "unknown",
85
+ summary: "Health check unavailable",
86
+ issues: [],
87
+ counts: { errors: 0, warnings: 0, infos: 0 },
88
+ totals: { activeEntries: 0, handoffs: 0, rollups: 0 },
89
+ timestamp: new Date().toISOString(),
90
+ }));
91
+ }
92
+ }
93
+ // ─── API: Knowledge Graph Data (v2.3.0) ───
94
+ if (url.pathname === "/api/graph") {
95
+ // Fetch recent ledger entries to build the graph
96
+ // We look at the last 100 entries to keep the graph relevant but performant
97
+ const entries = await storage.getLedgerEntries({
98
+ limit: "100",
99
+ order: "created_at.desc",
100
+ select: "project,keywords",
101
+ });
102
+ // Deduplication sets for nodes and edges
103
+ const nodes = [];
104
+ const edges = [];
105
+ const nodeIds = new Set(); // track unique node IDs
106
+ const edgeIds = new Set(); // track unique edges
107
+ // Helper: add a node only if it doesn't already exist
108
+ const addNode = (id, group, label) => {
109
+ if (!nodeIds.has(id)) {
110
+ nodes.push({ id, label: label || id, group });
111
+ nodeIds.add(id);
112
+ }
113
+ };
114
+ // Helper: add an edge only if it doesn't already exist
115
+ const addEdge = (from, to) => {
116
+ const id = `${from}-${to}`; // deterministic edge ID
117
+ if (!edgeIds.has(id)) {
118
+ edges.push({ from, to });
119
+ edgeIds.add(id);
120
+ }
121
+ };
122
+ // Transform relational data into graph nodes & edges
123
+ entries.forEach(row => {
124
+ if (!row.project)
125
+ return; // skip rows without project
126
+ // 1. Project node (hub β€” large purple dot)
127
+ addNode(row.project, "project");
128
+ // 2. Keyword nodes (spokes β€” small dots)
129
+ let keywords = [];
130
+ // Handle SQLite (JSON string) vs Supabase (native array)
131
+ if (Array.isArray(row.keywords)) {
132
+ keywords = row.keywords;
133
+ }
134
+ else if (typeof row.keywords === "string") {
135
+ try {
136
+ keywords = JSON.parse(row.keywords);
137
+ }
138
+ catch { /* skip malformed */ }
139
+ }
140
+ // Create nodes + edges for each keyword
141
+ keywords.forEach((kw) => {
142
+ if (kw.length < 3)
143
+ return; // skip noise like "a", "is"
144
+ // Handle categories (cat:debugging) vs raw keywords
145
+ const isCat = kw.startsWith("cat:");
146
+ const group = isCat ? "category" : "keyword";
147
+ const label = isCat ? kw.replace("cat:", "") : kw;
148
+ addNode(kw, group, label); // keyword/category node
149
+ addEdge(row.project, kw); // edge: project β†’ keyword
150
+ });
151
+ });
152
+ res.writeHead(200, { "Content-Type": "application/json" });
153
+ return res.end(JSON.stringify({ nodes, edges }));
154
+ }
71
155
  // ─── 404 ───
72
156
  res.writeHead(404, { "Content-Type": "text/plain" });
73
157
  res.end("Not found");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Mind Palace Dashboard β€” UI Renderer (v2.0 β€” Step 8)
2
+ * Mind Palace Dashboard β€” UI Renderer (v2.3.1)
3
3
  *
4
4
  * Pure CSS + Vanilla JS single-page dashboard.
5
5
  * No build step, no Tailwind, no framework β€” served as a template literal.
@@ -22,6 +22,8 @@ export function renderDashboardHTML() {
22
22
  <title>Prism MCP β€” Mind Palace</title>
23
23
  <link rel="preconnect" href="https://fonts.googleapis.com">
24
24
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
25
+ <!-- Vis.js for Neural Graph (v2.3.0) -->
26
+ <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
25
27
  <style>
26
28
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
27
29
 
@@ -224,6 +226,50 @@ export function renderDashboardHTML() {
224
226
  /* ─── Fade in animation ─── */
225
227
  @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
226
228
  .fade-in { animation: fadeIn 0.4s ease-out forwards; }
229
+
230
+ /* ─── Brain Health Indicator (v2.2.0) ─── */
231
+ .health-status {
232
+ display: flex; align-items: center; gap: 0.75rem;
233
+ padding: 0.75rem 1rem; border-radius: var(--radius-sm);
234
+ background: rgba(15,23,42,0.6); margin-bottom: 1rem;
235
+ }
236
+ .health-dot {
237
+ width: 12px; height: 12px; border-radius: 50%;
238
+ flex-shrink: 0; position: relative;
239
+ }
240
+ .health-dot::after {
241
+ content: ''; position: absolute; inset: -3px;
242
+ border-radius: 50%; animation: healthPulse 2s ease-in-out infinite;
243
+ }
244
+ .health-dot.healthy { background: var(--accent-green); }
245
+ .health-dot.healthy::after { border: 2px solid rgba(16,185,129,0.3); }
246
+ .health-dot.degraded { background: var(--accent-amber); }
247
+ .health-dot.degraded::after { border: 2px solid rgba(245,158,11,0.3); }
248
+ .health-dot.unhealthy { background: var(--accent-rose); }
249
+ .health-dot.unhealthy::after { border: 2px solid rgba(244,63,94,0.3); }
250
+ .health-dot.unknown { background: var(--text-muted); }
251
+ .health-dot.unknown::after { border: 2px solid rgba(100,116,139,0.3); }
252
+ @keyframes healthPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
253
+ .health-label { font-size: 0.8rem; font-weight: 500; }
254
+ .health-summary { font-size: 0.75rem; color: var(--text-muted); }
255
+ .health-issues { font-size: 0.8rem; color: var(--text-secondary); margin-top: 0.5rem; }
256
+ .health-issues .issue-row {
257
+ padding: 0.3rem 0; display: flex; gap: 0.5rem; align-items: flex-start;
258
+ }
259
+
260
+ /* ─── Neural Graph (v2.3.0) ─── */
261
+ #network-container {
262
+ width: 100%; height: 300px;
263
+ border-radius: var(--radius);
264
+ background: rgba(0,0,0,0.2);
265
+ border: 1px solid var(--border-glass);
266
+ }
267
+ .refresh-btn {
268
+ margin-left: auto; background: none; border: none;
269
+ color: var(--text-muted); cursor: pointer; font-size: 0.85rem;
270
+ transition: color 0.2s;
271
+ }
272
+ .refresh-btn:hover { color: var(--accent-purple); }
227
273
  </style>
228
274
  </head>
229
275
  <body>
@@ -233,7 +279,7 @@ export function renderDashboardHTML() {
233
279
  <div class="logo">
234
280
  <span class="logo-icon">🧠</span>
235
281
  Prism Mind Palace
236
- <span class="version-badge">v2.0</span>
282
+ <span class="version-badge">v2.3.1</span>
237
283
  </div>
238
284
  <div class="selector">
239
285
  <select id="projectSelect">
@@ -271,6 +317,19 @@ export function renderDashboardHTML() {
271
317
  <div class="git-row"><span class="git-label">Key Context</span><span class="git-value" id="keyContext" style="font-family:var(--font-sans);max-width:200px;text-align:right">β€”</span></div>
272
318
  </div>
273
319
 
320
+ <!-- Brain Health (v2.2.0) -->
321
+ <div class="card" id="healthCard" style="display:none">
322
+ <div class="card-title"><span class="dot" style="background:var(--accent-green)"></span> Brain Health 🩺</div>
323
+ <div class="health-status">
324
+ <div class="health-dot unknown" id="healthDot"></div>
325
+ <div>
326
+ <div class="health-label" id="healthLabel">Scanning...</div>
327
+ <div class="health-summary" id="healthSummary"></div>
328
+ </div>
329
+ </div>
330
+ <div class="health-issues" id="healthIssues"></div>
331
+ </div>
332
+
274
333
  <!-- Morning Briefing -->
275
334
  <div class="card" id="briefingCard" style="display:none">
276
335
  <div class="card-title"><span class="dot" style="background:var(--accent-amber)"></span> Morning Briefing πŸŒ…</div>
@@ -286,6 +345,17 @@ export function renderDashboardHTML() {
286
345
 
287
346
  <!-- Right Column -->
288
347
  <div class="grid" style="align-content: start;">
348
+
349
+ <!-- Neural Graph (v2.3.0) -->
350
+ <div class="card">
351
+ <div class="card-title">
352
+ <span class="dot" style="background:var(--accent-blue)"></span>
353
+ Neural Graph πŸ•ΈοΈ
354
+ <button onclick="loadGraph()" class="refresh-btn">↻</button>
355
+ </div>
356
+ <div id="network-container">Loading nodes...</div>
357
+ </div>
358
+
289
359
  <!-- Time Travel -->
290
360
  <div class="card">
291
361
  <div class="card-title"><span class="dot" style="background:var(--accent-purple)"></span> Time Travel History πŸ•°οΈ</div>
@@ -413,6 +483,49 @@ export function renderDashboardHTML() {
413
483
  ledgerEl.innerHTML = '<div style="color:var(--text-muted);font-size:0.85rem;padding:1rem;text-align:center">No ledger entries yet.</div>';
414
484
  }
415
485
 
486
+ // ─── Brain Health (v2.2.0) ───
487
+ try {
488
+ var healthRes = await fetch('/api/health');
489
+ var healthData = await healthRes.json();
490
+ var healthCard = document.getElementById('healthCard');
491
+ var healthDot = document.getElementById('healthDot');
492
+ var healthLabel = document.getElementById('healthLabel');
493
+ var healthSummary = document.getElementById('healthSummary');
494
+ var healthIssues = document.getElementById('healthIssues');
495
+
496
+ // Set the dot color based on status
497
+ healthDot.className = 'health-dot ' + (healthData.status || 'unknown');
498
+
499
+ // Map status to emoji + label
500
+ var statusMap = { healthy: 'βœ… Healthy', degraded: '⚠️ Degraded', unhealthy: 'πŸ”΄ Unhealthy' };
501
+ healthLabel.textContent = statusMap[healthData.status] || '❓ Unknown';
502
+
503
+ // Stats summary line
504
+ var t = healthData.totals || {};
505
+ healthSummary.textContent = (t.activeEntries || 0) + ' entries Β· ' +
506
+ (t.handoffs || 0) + ' handoffs Β· ' +
507
+ (t.rollups || 0) + ' rollups';
508
+
509
+ // Issue rows
510
+ var issues = healthData.issues || [];
511
+ if (issues.length > 0) {
512
+ var sevIcons = { error: 'πŸ”΄', warning: '🟑', info: 'πŸ”΅' };
513
+ healthIssues.innerHTML = issues.map(function(i) {
514
+ return '<div class="issue-row">' +
515
+ '<span>' + (sevIcons[i.severity] || '❓') + '</span>' +
516
+ '<span>' + escapeHtml(i.message) + '</span>' +
517
+ '</div>';
518
+ }).join('');
519
+ } else {
520
+ healthIssues.innerHTML = '<div style="color:var(--accent-green);font-size:0.8rem">πŸŽ‰ No issues found</div>';
521
+ }
522
+
523
+ healthCard.style.display = 'block';
524
+ } catch(he) {
525
+ // Health check not available β€” silently skip
526
+ console.warn('Health check unavailable:', he);
527
+ }
528
+
416
529
  document.getElementById('content').className = 'grid grid-main fade-in';
417
530
  document.getElementById('content').style.display = 'grid';
418
531
  } catch(e) {
@@ -438,6 +551,73 @@ export function renderDashboardHTML() {
438
551
 
439
552
  // Allow Enter key in select to trigger load
440
553
  document.getElementById('projectSelect').addEventListener('change', loadProject);
554
+
555
+ // ─── Neural Graph (v2.3.0) ───
556
+ // Renders a force-directed graph of projects ↔ keywords ↔ categories
557
+ async function loadGraph() {
558
+ var container = document.getElementById('network-container');
559
+ if (!container) return;
560
+
561
+ try {
562
+ var res = await fetch('/api/graph');
563
+ var data = await res.json();
564
+
565
+ // Empty state β€” no ledger entries yet
566
+ if (data.nodes.length === 0) {
567
+ container.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:0.85rem">No knowledge associations found yet.</div>';
568
+ return;
569
+ }
570
+
571
+ // Vis.js dark-theme config matching the glassmorphism palette
572
+ var options = {
573
+ nodes: {
574
+ shape: 'dot', // all nodes are circles
575
+ borderWidth: 0, // no borders for clean look
576
+ font: { color: '#94a3b8', face: 'Inter', size: 12 }
577
+ },
578
+ edges: {
579
+ width: 1, // thin edges for subtlety
580
+ color: { color: 'rgba(139,92,246,0.15)', highlight: '#8b5cf6' },
581
+ smooth: { type: 'continuous' } // smooth curves
582
+ },
583
+ groups: {
584
+ project: { // Hub nodes β€” large purple
585
+ color: { background: '#8b5cf6', border: '#7c3aed' },
586
+ size: 20,
587
+ font: { size: 14, color: '#f1f5f9', face: 'Inter' }
588
+ },
589
+ category: { // Category nodes β€” cyan diamonds
590
+ color: { background: '#06b6d4', border: '#0891b2' },
591
+ size: 10,
592
+ shape: 'diamond'
593
+ },
594
+ keyword: { // Keyword nodes β€” small dark dots
595
+ color: { background: '#1e293b', border: '#334155' },
596
+ size: 6,
597
+ font: { size: 10, color: '#64748b' }
598
+ }
599
+ },
600
+ physics: {
601
+ stabilization: false, // animate on load for visual pop
602
+ barnesHut: {
603
+ gravitationalConstant: -3000, // spread nodes apart
604
+ springConstant: 0.04, // gentle spring force
605
+ springLength: 80 // default edge length
606
+ }
607
+ },
608
+ interaction: { hover: true } // highlight on hover
609
+ };
610
+
611
+ // Create the network visualization
612
+ new vis.Network(container, data, options);
613
+ } catch (e) {
614
+ console.error('Graph error', e);
615
+ container.innerHTML = '<div style="padding:1rem;color:var(--accent-rose)">Graph failed to load</div>';
616
+ }
617
+ }
618
+
619
+ // Initialize the graph on page load
620
+ loadGraph();
441
621
  </script>
442
622
  </body>
443
623
  </html>`;
package/dist/server.js CHANGED
@@ -72,17 +72,21 @@ import { WEB_SEARCH_TOOL, BRAVE_WEB_SEARCH_CODE_MODE_TOOL, LOCAL_SEARCH_TOOL, BR
72
72
  // Session memory tools β€” only used if Supabase is configured
73
73
  import { SESSION_SAVE_LEDGER_TOOL, SESSION_SAVE_HANDOFF_TOOL, SESSION_LOAD_CONTEXT_TOOL, KNOWLEDGE_SEARCH_TOOL, KNOWLEDGE_FORGET_TOOL,
74
74
  // ─── v0.4.0: New tool definitions (Enhancements #2 and #4) ───
75
- SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL, SESSION_BACKFILL_EMBEDDINGS_TOOL,
75
+ SESSION_COMPACT_LEDGER_TOOL, SESSION_SEARCH_MEMORY_TOOL,
76
76
  // ─── v2.0: Time Travel tool definitions ───
77
77
  MEMORY_HISTORY_TOOL, MEMORY_CHECKOUT_TOOL,
78
78
  // ─── v2.0: Visual Memory tool definitions ───
79
- SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL, sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler,
79
+ SESSION_SAVE_IMAGE_TOOL, SESSION_VIEW_IMAGE_TOOL,
80
+ // ─── v2.2.0: Health Check tool definition ───
81
+ SESSION_HEALTH_CHECK_TOOL, sessionSaveLedgerHandler, sessionSaveHandoffHandler, sessionLoadContextHandler, knowledgeSearchHandler, knowledgeForgetHandler,
80
82
  // ─── v0.4.0: New tool handlers ───
81
- compactLedgerHandler, sessionSearchMemoryHandler, backfillEmbeddingsHandler,
83
+ compactLedgerHandler, sessionSearchMemoryHandler,
82
84
  // ─── v2.0: Time Travel handlers ───
83
85
  memoryHistoryHandler, memoryCheckoutHandler,
84
86
  // ─── v2.0: Visual Memory handlers ───
85
- sessionSaveImageHandler, sessionViewImageHandler, } from "./tools/index.js";
87
+ sessionSaveImageHandler, sessionViewImageHandler,
88
+ // ─── v2.2.0: Health Check handler ───
89
+ sessionHealthCheckHandler, } from "./tools/index.js";
86
90
  // ─── Dynamic Tool Registration ───────────────────────────────────
87
91
  // Base tools: always available regardless of configuration
88
92
  const BASE_TOOLS = [
@@ -106,12 +110,13 @@ const SESSION_MEMORY_TOOLS = [
106
110
  KNOWLEDGE_FORGET_TOOL, // knowledge_forget β€” prune bad/old memories
107
111
  SESSION_COMPACT_LEDGER_TOOL, // session_compact_ledger β€” auto-compact old ledger entries (v0.4.0)
108
112
  SESSION_SEARCH_MEMORY_TOOL, // session_search_memory β€” semantic search via embeddings (v0.4.0)
109
- SESSION_BACKFILL_EMBEDDINGS_TOOL, // session_backfill_embeddings β€” repair missing embeddings
110
113
  MEMORY_HISTORY_TOOL, // memory_history β€” view version timeline (v2.0)
111
114
  MEMORY_CHECKOUT_TOOL, // memory_checkout β€” revert to past version (v2.0)
112
115
  // ─── v2.0: Visual Memory tools ───
113
116
  SESSION_SAVE_IMAGE_TOOL, // session_save_image β€” save image to media vault (v2.0)
114
117
  SESSION_VIEW_IMAGE_TOOL, // session_view_image β€” retrieve image from vault (v2.0)
118
+ // ─── v2.2.0: Health Check tool ───
119
+ SESSION_HEALTH_CHECK_TOOL, // session_health_check β€” brain integrity checker (v2.2.0)
115
120
  ];
116
121
  // Combine: if session memory is enabled, add those tools too
117
122
  const ALL_TOOLS = [
@@ -477,10 +482,6 @@ export function createServer() {
477
482
  if (!SESSION_MEMORY_ENABLED)
478
483
  throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
479
484
  return await sessionSearchMemoryHandler(args);
480
- case "session_backfill_embeddings":
481
- if (!SESSION_MEMORY_ENABLED)
482
- throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
483
- return await backfillEmbeddingsHandler(args);
484
485
  // ─── v2.0: Time Travel Tools ───
485
486
  case "memory_history":
486
487
  if (!SESSION_MEMORY_ENABLED)
@@ -499,6 +500,11 @@ export function createServer() {
499
500
  if (!SESSION_MEMORY_ENABLED)
500
501
  throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
501
502
  return await sessionViewImageHandler(args);
503
+ // ─── v2.2.0: Health Check Tool ───
504
+ case "session_health_check":
505
+ if (!SESSION_MEMORY_ENABLED)
506
+ throw new Error("Session memory not configured. Set SUPABASE_URL and SUPABASE_KEY.");
507
+ return await sessionHealthCheckHandler(args);
502
508
  default:
503
509
  return {
504
510
  content: [{ type: "text", text: `Unknown tool: ${name}` }],
@@ -521,11 +527,65 @@ export function createServer() {
521
527
  });
522
528
  return server;
523
529
  }
524
- // ─── Smithery Sandbox Export ─────────────────────────────────────
525
- // Smithery uses this to scan capabilities (tools, prompts, resources)
526
- // without requiring real credentials or starting a transport.
530
+ // ─── Smithery/Glama Sandbox Export ───────────────────────────────
531
+ // Scanners (Smithery, Glama) use this to enumerate capabilities
532
+ // (tools, prompts, resources) without requiring real credentials.
533
+ // Unlike createServer(), this always exposes ALL capabilities
534
+ // regardless of whether SESSION_MEMORY_ENABLED is true.
527
535
  export function createSandboxServer() {
528
- return createServer();
536
+ const server = new Server({
537
+ name: SERVER_CONFIG.name,
538
+ version: SERVER_CONFIG.version,
539
+ }, {
540
+ capabilities: {
541
+ tools: {
542
+ tools: [...BASE_TOOLS, ...SESSION_MEMORY_TOOLS],
543
+ },
544
+ prompts: {},
545
+ resources: { subscribe: true },
546
+ },
547
+ });
548
+ // Register all tool listings unconditionally
549
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
550
+ tools: [...BASE_TOOLS, ...SESSION_MEMORY_TOOLS],
551
+ }));
552
+ // Register prompts listing so scanners see resume_session
553
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
554
+ prompts: [{
555
+ name: "resume_session",
556
+ description: "Load previous session context for a project. " +
557
+ "Automatically fetches handoff state and injects it before " +
558
+ "the LLM starts thinking β€” no tool call needed. " +
559
+ "Includes version tracking for concurrency control.",
560
+ arguments: [
561
+ {
562
+ name: "project",
563
+ description: "Project identifier to resume (e.g., 'prism-mcp')",
564
+ required: true,
565
+ },
566
+ {
567
+ name: "level",
568
+ description: "Context depth: 'quick' (~50 tokens), " +
569
+ "'standard' (~200 tokens, recommended), " +
570
+ "'deep' (full history, ~1000+ tokens)",
571
+ required: false,
572
+ },
573
+ ],
574
+ }],
575
+ }));
576
+ // Register resource templates so scanners see memory://{project}/handoff
577
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
578
+ resourceTemplates: [{
579
+ uriTemplate: "memory://{project}/handoff",
580
+ name: "Session Handoff State",
581
+ description: "Current handoff state for a project β€” includes " +
582
+ "last summary, pending TODOs, active decisions, keywords, " +
583
+ "and version number for concurrency control. " +
584
+ "Attach this to inject session context without a tool call.",
585
+ mimeType: "application/json",
586
+ }],
587
+ }));
588
+ return server;
529
589
  }
530
590
  // ─── Server Startup ─────────────────────────────────────────────
531
591
  /**