prism-mcp-server 20.2.3 → 20.2.4

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
@@ -57,6 +57,19 @@ features.
57
57
  <details>
58
58
  <summary>Release history (optional)</summary>
59
59
 
60
+ ## What's New in v20.2.4
61
+
62
+ ### Reliable Session Memory That Shows Work, Not Greetings
63
+ Greeting-only assistant replies are skipped before ledger writes. Existing
64
+ greeting rows are filtered at read time across native startup, MCP context, and
65
+ `prism load --json`, while entries containing decisions, TODOs, changed files,
66
+ or non-session events remain visible. Historical rows are not destructively
67
+ deleted. If Synalux has a transient startup failure, Prism displays one bounded
68
+ local last-good snapshot and clearly labels it; permanent authorization or
69
+ validation failures still fail loud, and later writes remain cloud-routed.
70
+
71
+ ---
72
+
60
73
  ## What's New in v20.2.2
61
74
 
62
75
  ### One Local-First Workflow Across Every Agent
package/dist/cli.js CHANGED
@@ -10,22 +10,25 @@ import { getCurrentGitState } from './utils/git.js';
10
10
  import { sessionBootstrapHandler, sessionLoadContextHandler, sessionSaveLedgerHandler, sessionSaveHandoffHandler, } from './tools/ledgerHandlers.js';
11
11
  import { configureClaudeAgentPolicy, configureClaudeNativeStartup, configureCodexAgentPolicy, configureCodexNativeStartup, configureGeminiAgentPolicy, configureGeminiNativeStartup, connectHosts, migrateLegacyClaudeHooks, migrateLegacyClaudeInstructions, migrateLegacyClaudeManagedStartup, migrateLegacyClaudeProjectMcp, normalizeHostName, } from './connect.js';
12
12
  import { runBrowserCli } from './browserCli.js';
13
+ import { filterPrismMemoryContext } from './utils/memoryQuality.js';
14
+ import { isRecoverableStartupStorageError } from './utils/startupRecovery.js';
13
15
  const program = new Command();
14
16
  /** Build the stable `prism load --json` envelope from depth-specific context. */
15
17
  export function buildLoadJsonOutput(project, data, level, metadata) {
18
+ const filteredData = filterPrismMemoryContext(data);
16
19
  let history = [];
17
20
  let historyLimit = 0;
18
21
  if (level === 'standard') {
19
- history = Array.isArray(data.recent_sessions) ? data.recent_sessions : [];
22
+ history = Array.isArray(filteredData.recent_sessions) ? filteredData.recent_sessions : [];
20
23
  historyLimit = 5;
21
24
  }
22
25
  else if (level === 'deep') {
23
26
  // Canonical deep context uses session_history. Fall back only when that
24
27
  // field is absent so older portal responses remain consumable.
25
- history = Array.isArray(data.session_history)
26
- ? data.session_history
27
- : Array.isArray(data.recent_sessions)
28
- ? data.recent_sessions
28
+ history = Array.isArray(filteredData.session_history)
29
+ ? filteredData.session_history
30
+ : Array.isArray(filteredData.recent_sessions)
31
+ ? filteredData.recent_sessions
29
32
  : [];
30
33
  historyLimit = 50;
31
34
  }
@@ -33,15 +36,15 @@ export function buildLoadJsonOutput(project, data, level, metadata) {
33
36
  agent_name: metadata.agentName || null,
34
37
  handoff: [{
35
38
  project,
36
- role: metadata.effectiveRole || data.role || 'global',
37
- last_summary: data.last_summary || null,
38
- pending_todo: data.pending_todo || null,
39
- active_decisions: data.active_decisions || null,
40
- keywords: data.keywords || null,
41
- key_context: data.key_context || null,
42
- active_branch: data.active_branch || null,
43
- version: data.version ?? null,
44
- updated_at: data.updated_at || null,
39
+ role: metadata.effectiveRole || filteredData.role || 'global',
40
+ last_summary: filteredData.last_summary || null,
41
+ pending_todo: filteredData.pending_todo || null,
42
+ active_decisions: filteredData.active_decisions || null,
43
+ keywords: filteredData.keywords || null,
44
+ key_context: filteredData.key_context || null,
45
+ active_branch: filteredData.active_branch || null,
46
+ version: filteredData.version ?? null,
47
+ updated_at: filteredData.updated_at || null,
45
48
  }],
46
49
  // Keep the established key for callers while sourcing the canonical field
47
50
  // for the requested depth.
@@ -61,21 +64,46 @@ program
61
64
  .description('Prism — The Mind Palace for AI Agents')
62
65
  .version(SERVER_CONFIG.version);
63
66
  /** Print the canonical hook-free first-turn display used by the MCP tool. */
67
+ export const isRecoverableBootstrapStorageError = isRecoverableStartupStorageError;
68
+ async function printBootstrapDisplay() {
69
+ const result = await sessionBootstrapHandler({});
70
+ const output = result.content?.map((part) => part?.text).filter(Boolean).join('\n') || '';
71
+ if (!output)
72
+ throw new Error('session_bootstrap returned no display content');
73
+ console.log(output);
74
+ if (result.isError)
75
+ process.exitCode = 1;
76
+ }
64
77
  export async function runBootstrapCommand() {
78
+ const previousStorage = process.env.PRISM_STORAGE;
65
79
  try {
66
- const result = await sessionBootstrapHandler({});
67
- const output = result.content?.map((part) => part?.text).filter(Boolean).join('\n') || '';
68
- if (!output)
69
- throw new Error('session_bootstrap returned no display content');
70
- console.log(output);
71
- if (result.isError)
72
- process.exitCode = 1;
80
+ try {
81
+ await printBootstrapDisplay();
82
+ }
83
+ catch (err) {
84
+ if (!isRecoverableBootstrapStorageError(err))
85
+ throw err;
86
+ // Startup is a read-only display. If paid cloud memory is temporarily
87
+ // unavailable, retry this one read against the local last-good database
88
+ // instead of blocking the host's entire first turn. Do not persist the
89
+ // override: normal saves still use the configured subscription backend.
90
+ await closeStorage().catch(() => { });
91
+ process.env.PRISM_STORAGE = 'local';
92
+ console.error('Prism cloud startup unavailable; using local last-good context for this startup.');
93
+ await printBootstrapDisplay();
94
+ }
73
95
  }
74
96
  catch (err) {
75
97
  console.error(`Bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
76
98
  process.exitCode = 1;
77
99
  }
78
100
  finally {
101
+ if (previousStorage === undefined) {
102
+ delete process.env.PRISM_STORAGE;
103
+ }
104
+ else {
105
+ process.env.PRISM_STORAGE = previousStorage;
106
+ }
79
107
  await closeStorage().catch(() => { });
80
108
  }
81
109
  }
@@ -47,12 +47,16 @@ function resolveKnowledgeScope(callerScope) {
47
47
  }
48
48
  /** Refresh JWT this many ms before expiry to avoid edge-case 401s. */
49
49
  const JWT_REFRESH_LEEWAY_MS = 60_000;
50
+ function buildContextLoadKey(project, level, userId, role) {
51
+ return JSON.stringify({ project, level, userId, role: role ?? "" });
52
+ }
50
53
  export class SynaluxStorage extends SupabaseStorage {
51
54
  baseUrl;
52
55
  refreshToken;
53
56
  cachedJwt = null;
54
57
  cachedJwtExpiresAt = 0;
55
58
  inflightExchange = null;
59
+ inflightContextLoads = new Map();
56
60
  constructor() {
57
61
  super();
58
62
  const url = process.env.PRISM_SYNALUX_BASE_URL || PRISM_SYNALUX_BASE_URL;
@@ -203,31 +207,46 @@ export class SynaluxStorage extends SupabaseStorage {
203
207
  }
204
208
  // ─── Context ─────────────────────────────────────────────────
205
209
  async loadContext(project, level, userId, role) {
206
- const result = await this.portalPost("/api/v1/prism/memory", {
207
- action: "load_context",
208
- project,
209
- level,
210
- user_id: userId,
211
- role,
212
- });
213
- // Current portals return the canonical flat `context`. Older deployed
214
- // portals returned `{ handoff, recent_sessions }`; normalize that envelope
215
- // here so the formatter can still see last_summary, TODOs, version, and
216
- // session history during a rolling portal/client upgrade.
217
- if (result.context === null)
218
- return null;
219
- if (result.context && typeof result.context === "object" && !Array.isArray(result.context)) {
220
- return result.context;
210
+ const key = buildContextLoadKey(project, level, userId, role);
211
+ const existing = this.inflightContextLoads.get(key);
212
+ if (existing)
213
+ return existing;
214
+ const request = (async () => {
215
+ const result = await this.portalPost("/api/v1/prism/memory", {
216
+ action: "load_context",
217
+ project,
218
+ level,
219
+ user_id: userId,
220
+ role,
221
+ });
222
+ // Current portals return the canonical flat `context`. Older deployed
223
+ // portals returned `{ handoff, recent_sessions }`; normalize that envelope
224
+ // here so the formatter can still see last_summary, TODOs, version, and
225
+ // session history during a rolling portal/client upgrade.
226
+ if (result.context === null)
227
+ return null;
228
+ if (result.context && typeof result.context === "object" && !Array.isArray(result.context)) {
229
+ return result.context;
230
+ }
231
+ if (result.handoff === null)
232
+ return null;
233
+ if (result.handoff && typeof result.handoff === "object" && !Array.isArray(result.handoff)) {
234
+ return {
235
+ ...result.handoff,
236
+ recent_sessions: Array.isArray(result.recent_sessions) ? result.recent_sessions : [],
237
+ };
238
+ }
239
+ return result;
240
+ })();
241
+ this.inflightContextLoads.set(key, request);
242
+ try {
243
+ return await request;
221
244
  }
222
- if (result.handoff === null)
223
- return null;
224
- if (result.handoff && typeof result.handoff === "object" && !Array.isArray(result.handoff)) {
225
- return {
226
- ...result.handoff,
227
- recent_sessions: Array.isArray(result.recent_sessions) ? result.recent_sessions : [],
228
- };
245
+ finally {
246
+ if (this.inflightContextLoads.get(key) === request) {
247
+ this.inflightContextLoads.delete(key);
248
+ }
229
249
  }
230
- return result;
231
250
  }
232
251
  // ─── Forget memory (GDPR surgical deletion) ──────────────────
233
252
  // Phase 3 Tier A: route both soft and hard delete through the
@@ -31,6 +31,7 @@ import { getCurrentGitState, getGitDrift } from "../utils/git.js";
31
31
  import { getSetting, getAllSettings, refreshConfigStorageCache } from "../storage/configStorage.js";
32
32
  import { mergeHandoff, dbToHandoffSchema, sanitizeForMerge } from "../utils/crdtMerge.js";
33
33
  import { resolveProject } from "../utils/projectResolver.js";
34
+ import { isRecoverableStartupStorageError, LOCAL_STARTUP_FALLBACK_NOTICE, } from "../utils/startupRecovery.js";
34
35
  import { PRISM_USER_ID, PRISM_AUTO_CAPTURE, PRISM_CAPTURE_PORTS } from "../config.js";
35
36
  import { captureLocalEnvironment } from "../utils/autoCapture.js";
36
37
  import { fireCaptionAsync } from "../utils/imageCaptioner.js";
@@ -264,6 +265,7 @@ function compactWithOmissionCount(value, maxChars) {
264
265
  */
265
266
  import { computeEffectiveImportance, recordMemoryAccess } from "../utils/cognitiveMemory.js";
266
267
  import { formatInferenceMetrics, resetInferenceMetrics, getInferenceSnapshot } from "../utils/inferenceMetrics.js";
268
+ import { filterPrismMemoryContext, isGreetingOnlyMemoryEntry } from "../utils/memoryQuality.js";
267
269
  export async function sessionSaveLedgerHandler(args) {
268
270
  if (!isSessionSaveLedgerArgs(args)) {
269
271
  throw new Error("Invalid arguments for session_save_ledger");
@@ -289,6 +291,16 @@ export async function sessionSaveLedgerHandler(args) {
289
291
  const files_changed = args.files_changed ? sanitizeArray(args.files_changed) : undefined;
290
292
  const decisions = args.decisions ? sanitizeArray(args.decisions) : undefined;
291
293
  const role = args.role;
294
+ if (isGreetingOnlyMemoryEntry({ summary, todos, files_changed, decisions })) {
295
+ return {
296
+ content: [{
297
+ type: "text",
298
+ text: (_saveLedgerGateWarning ? `⚠️ ${_saveLedgerGateWarning}\n\n` : "") +
299
+ "ℹ️ Greeting-only turn skipped; no ledger entry was written.",
300
+ }],
301
+ isError: false,
302
+ };
303
+ }
292
304
  const storage = await getStorage();
293
305
  // ─── Project mismatch validation (v13 — hard-rejects on mismatch) ───
294
306
  // Replaces the old soft-warning behavior that allowed cross-project
@@ -817,7 +829,7 @@ export async function sessionLoadContextHandler(args, options = {}) {
817
829
  const protectedFallbackNames = new Set(protectedFallbackEntries.map((entry) => entry.name));
818
830
  const manifestSnapshot = await resolveNativeSkillManifestSnapshot(skillSyncResult);
819
831
  const entitledSkillNames = new Set(manifestSnapshot.names);
820
- const storage = await getStorage();
832
+ const storage = options.storageOverride ?? await getStorage();
821
833
  const effectiveRole = role || await getSetting("default_role", "") || undefined;
822
834
  const loadEntitledRoleSkill = async () => {
823
835
  if (!effectiveRole)
@@ -830,11 +842,11 @@ export async function sessionLoadContextHandler(args, options = {}) {
830
842
  }
831
843
  return await getSetting(`user_skill:${effectiveRole}`, "");
832
844
  };
833
- const data = await storage.loadContext(project, level, PRISM_USER_ID, effectiveRole); // v3.0: role with dashboard fallback
845
+ const loadedData = await storage.loadContext(project, level, PRISM_USER_ID, effectiveRole); // v3.0: role with dashboard fallback
834
846
  // F4 fix: inject protected skills even for fresh projects.
835
847
  // Previously this returned before skill injection, leaving new projects with zero
836
848
  // behavioral guardrails for the entire session. Now protected skills always load.
837
- if (!data) {
849
+ if (!loadedData) {
838
850
  let freshSkillBlock = "";
839
851
  const freshLoadedSkills = new Set();
840
852
  if (includeSkillContent) {
@@ -896,6 +908,7 @@ export async function sessionLoadContextHandler(args, options = {}) {
896
908
  isError: false,
897
909
  };
898
910
  }
911
+ const data = filterPrismMemoryContext(loadedData);
899
912
  const version = data?.version;
900
913
  const versionNote = version
901
914
  ? `\n\n🔑 Session version: ${version}. Pass expected_version: ${version} when saving handoff.`
@@ -1416,11 +1429,13 @@ export async function sessionLoadContextHandler(args, options = {}) {
1416
1429
  isError: false,
1417
1430
  };
1418
1431
  }
1419
- /**
1420
- * Hook-free first-turn entrypoint used by the native prism-startup skill.
1421
- * Configuration, rather than the host model, owns project and depth selection.
1422
- */
1423
- export async function sessionBootstrapHandler(args = {}) {
1432
+ async function createLocalStartupStorage() {
1433
+ const { SqliteStorage } = await import("../storage/sqlite.js");
1434
+ const storage = new SqliteStorage();
1435
+ await storage.initialize(true);
1436
+ return storage;
1437
+ }
1438
+ export async function sessionBootstrapHandler(args = {}, options = {}) {
1424
1439
  if (typeof args !== "object" || args === null || Array.isArray(args)) {
1425
1440
  throw new Error("Invalid arguments for session_bootstrap");
1426
1441
  }
@@ -1487,39 +1502,76 @@ export async function sessionBootstrapHandler(args = {}) {
1487
1502
  : "";
1488
1503
  const omissionLength = omittedProjectsText ? omittedProjectsText.length + 2 : 0;
1489
1504
  const separatorsLength = Math.max(0, renderedProjectCount - 1) * 2;
1490
- perProjectMaxChars = Math.floor((startupMaxChars - startupHeader.length - systemReadyBlock.length - 4 - omissionLength - separatorsLength) /
1505
+ perProjectMaxChars = Math.floor((startupMaxChars - startupHeader.length - systemReadyBlock.length - LOCAL_STARTUP_FALLBACK_NOTICE.length -
1506
+ 6 - omissionLength - separatorsLength) /
1491
1507
  renderedProjectCount);
1492
1508
  if (perProjectMaxChars >= 512 || renderedProjectCount === 1)
1493
1509
  break;
1494
1510
  renderedProjectCount -= 1;
1495
1511
  }
1496
- const loaded = [];
1497
- let hadError = false;
1498
- for (const project of projects.slice(0, renderedProjectCount)) {
1499
- const result = await sessionLoadContextHandler({
1500
- project,
1501
- level: depth,
1502
- role: defaultRole || undefined,
1503
- conversation_id: conversationId,
1504
- prompt: input.prompt,
1505
- }, { includeSkillContent: false, nativeMaxChars: perProjectMaxChars, skillSyncResult });
1506
- const text = result.content?.map((part) => part?.text).filter(Boolean).join("\n") ||
1507
- `No session context found for project "${project}".`;
1508
- loaded.push(text);
1509
- hadError ||= result.isError === true;
1512
+ const renderedProjects = projects.slice(0, renderedProjectCount);
1513
+ const loadProjects = async (storageOverride) => {
1514
+ const loaded = [];
1515
+ let hadError = false;
1516
+ for (const project of renderedProjects) {
1517
+ const result = await sessionLoadContextHandler({
1518
+ project,
1519
+ level: depth,
1520
+ role: defaultRole || undefined,
1521
+ conversation_id: conversationId,
1522
+ prompt: input.prompt,
1523
+ }, {
1524
+ includeSkillContent: false,
1525
+ nativeMaxChars: perProjectMaxChars,
1526
+ skillSyncResult,
1527
+ storageOverride,
1528
+ });
1529
+ const text = result.content?.map((part) => part?.text).filter(Boolean).join("\n") ||
1530
+ `No session context found for project "${project}".`;
1531
+ loaded.push(text);
1532
+ hadError ||= result.isError === true;
1533
+ }
1534
+ return { loaded, hadError };
1535
+ };
1536
+ let localStorage = null;
1537
+ let usedLocalFallback = false;
1538
+ let startupContext;
1539
+ try {
1540
+ try {
1541
+ startupContext = await loadProjects();
1542
+ }
1543
+ catch (error) {
1544
+ if (!isRecoverableStartupStorageError(error))
1545
+ throw error;
1546
+ localStorage = await (options.localStorageFactory ?? createLocalStartupStorage)();
1547
+ usedLocalFallback = true;
1548
+ startupContext = await loadProjects(localStorage);
1549
+ }
1550
+ }
1551
+ finally {
1552
+ if (localStorage) {
1553
+ try {
1554
+ await localStorage.close();
1555
+ }
1556
+ catch (error) {
1557
+ debugLog(`[session_bootstrap] Local fallback close failed: ${error instanceof Error ? error.message : String(error)}`);
1558
+ }
1559
+ }
1510
1560
  }
1561
+ const fallbackNotice = usedLocalFallback ? `${LOCAL_STARTUP_FALLBACK_NOTICE}\n\n` : "";
1511
1562
  return {
1512
1563
  content: [{
1513
1564
  type: "text",
1514
- text: `${startupHeader}\n\n${loaded.join("\n\n")}` +
1565
+ text: `${startupHeader}\n\n${fallbackNotice}${startupContext.loaded.join("\n\n")}` +
1515
1566
  (omittedProjectsText ? `\n\n${omittedProjectsText}` : "") +
1516
1567
  `\n\n${systemReadyBlock}`,
1517
1568
  }],
1518
- isError: hadError,
1569
+ isError: startupContext.hadError,
1519
1570
  structuredContent: {
1520
1571
  conversation_id: conversationId,
1521
- projects: projects.slice(0, renderedProjectCount),
1572
+ projects: renderedProjects,
1522
1573
  depth,
1574
+ context_source: usedLocalFallback ? "local-last-good" : activeStorageBackend,
1523
1575
  },
1524
1576
  };
1525
1577
  }
@@ -0,0 +1,51 @@
1
+ const SOURCE_LABEL_PATTERN = /^\s*\[[^\]]+\]\s*/u;
2
+ const GREETING_ONLY_PATTERN = /^(?:hi|hello|hey|ready)(?:[\s.!?,…—-]|[\u{1F300}-\u{1FAFF}]|\uFE0F)*$/iu;
3
+ const GREETING_OPENING_PATTERN = /^(?:hi(?:\s+there)?|hello|hey)\b/iu;
4
+ const ASSISTANCE_INVITATION_PATTERN = /\b(?:(?:how|what)\s+(?:can|may)\s+i\s+(?:help|assist)|let\s+me\s+know\s+what\s+you\s+need)\b/iu;
5
+ const SUBSTANTIVE_OUTCOME_PATTERN = /\b(?:added|built|changed|completed|configured|created|debugged|deployed|fixed|implemented|investigated|removed|repaired|resolved|tested|updated|verified|wrote)\b/iu;
6
+ const STRUCTURED_WORK_FIELDS = ["decisions", "todos", "files_changed"];
7
+ function hasStructuredWork(entry) {
8
+ return STRUCTURED_WORK_FIELDS.some((field) => {
9
+ const value = entry[field];
10
+ if (value === undefined || value === null)
11
+ return false;
12
+ return !Array.isArray(value) || value.length > 0;
13
+ });
14
+ }
15
+ /** Greeting-only assistant replies are presentation, not durable work. */
16
+ export function isGreetingOnlyMemoryEntry(entry) {
17
+ if (hasStructuredWork(entry))
18
+ return false;
19
+ if (typeof entry.event_type === "string" && entry.event_type !== "session")
20
+ return false;
21
+ if (typeof entry.summary !== "string")
22
+ return false;
23
+ const summary = entry.summary.replace(SOURCE_LABEL_PATTERN, "").trim();
24
+ if (!summary)
25
+ return false;
26
+ if (GREETING_ONLY_PATTERN.test(summary))
27
+ return true;
28
+ return GREETING_OPENING_PATTERN.test(summary)
29
+ && ASSISTANCE_INVITATION_PATTERN.test(summary)
30
+ && !SUBSTANTIVE_OUTCOME_PATTERN.test(summary);
31
+ }
32
+ export function filterGreetingOnlyMemoryEntries(entries) {
33
+ return entries.filter((entry) => !isGreetingOnlyMemoryEntry(entry));
34
+ }
35
+ /**
36
+ * Defensive local-storage fallback for the portal-owned memory policy.
37
+ * Returns a copy so storage responses remain immutable for other consumers.
38
+ */
39
+ export function filterPrismMemoryContext(data) {
40
+ const filtered = { ...data };
41
+ if (isGreetingOnlyMemoryEntry({ summary: data.last_summary })) {
42
+ filtered.last_summary = null;
43
+ }
44
+ if (Array.isArray(data.recent_sessions)) {
45
+ filtered.recent_sessions = filterGreetingOnlyMemoryEntries(data.recent_sessions);
46
+ }
47
+ if (Array.isArray(data.session_history)) {
48
+ filtered.session_history = filterGreetingOnlyMemoryEntries(data.session_history);
49
+ }
50
+ return filtered;
51
+ }
@@ -0,0 +1,13 @@
1
+ const RECOVERABLE_STARTUP_STORAGE_ERROR = /(?:rate limit|(?:HTTP|status)\s*(?:408|425|429|5\d{2})\b|network error|fetch failed|timed?\s*out|timeout|ECONN(?:RESET|REFUSED)|ENOTFOUND|EAI_AGAIN|socket hang up)/i;
2
+ const RECOVERABLE_ENTITLEMENT_PROBE_ERROR = /\[Prism Storage\]\s+Could not verify the Synalux cloud-memory entitlement\b/i;
3
+ export const LOCAL_STARTUP_FALLBACK_NOTICE = "⚠️ Synalux cloud context is temporarily unavailable; showing local last-good context for this startup only.";
4
+ /**
5
+ * Startup may degrade to the local last-good snapshot only for transient
6
+ * storage failures. Validation, formatting, and programmer errors must still
7
+ * fail loud instead of being hidden by an unrelated fallback.
8
+ */
9
+ export function isRecoverableStartupStorageError(error) {
10
+ const message = error instanceof Error ? error.message : String(error);
11
+ return RECOVERABLE_STARTUP_STORAGE_ERROR.test(message)
12
+ || RECOVERABLE_ENTITLEMENT_PROBE_ERROR.test(message);
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "20.2.3",
3
+ "version": "20.2.4",
4
4
  "mcpName": "io.github.dcostenco/prism-coder",
5
5
  "description": "Prism Coder — Cognitive memory + tool-calling intelligence for AI agents. Mind Palace persistent memory (BFCL Gold Certified, 100% Tool-Call Accuracy, 114 Agent Skills, PHI Guard, Tier Enforcement, Prompt-Based Skill Routing, Zero-Search HDC/HRR retrieval, HRR Semantic Drift Detection across BCBA/Coding/AAC domains, HIPAA-hardened local or subscription-gated Synalux storage, SLERP-optimized GRPO alignment) plus the prism-coder 1.7B–32B open-weights LLM fleet.",
6
6
  "module": "index.ts",