prism-mcp-server 9.1.0 → 9.2.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 +74 -0
- package/dist/cli.js +108 -2
- package/dist/config.js +3 -3
- package/dist/storage/index.js +25 -3
- package/dist/sync/factory.js +5 -11
- package/dist/utils/supabaseApi.js +12 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ https://github.com/dcostenco/prism-mcp/raw/main/docs/prism_mcp_demo.mp4
|
|
|
35
35
|
- [Use Cases](#use-cases)
|
|
36
36
|
- [What's New](#whats-new)
|
|
37
37
|
- [How Prism Compares](#how-prism-compares)
|
|
38
|
+
- [CLI Reference](#-cli-reference)
|
|
38
39
|
- [Tool Reference](#tool-reference)
|
|
39
40
|
- [Environment Variables](#environment-variables)
|
|
40
41
|
- [Architecture](#architecture)
|
|
@@ -248,6 +249,16 @@ When wrapping up, always call `mcp__prism-mcp__session_save_ledger` and `mcp__pr
|
|
|
248
249
|
|
|
249
250
|
> **Format Note:** Claude automatically wraps MCP tools with double underscores (`mcp__prism-mcp__...`), while most other clients use single underscores (`mcp_prism-mcp_...`). Prism's backend natively handles both formats seamlessly.
|
|
250
251
|
|
|
252
|
+
**CLI Alternative:** If MCP tools aren't available or you're scripting around Claude Code:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# Load context before a session
|
|
256
|
+
prism load my-project --level deep
|
|
257
|
+
|
|
258
|
+
# Machine-readable JSON for parsing in scripts
|
|
259
|
+
prism load my-project --level deep --json
|
|
260
|
+
```
|
|
261
|
+
|
|
251
262
|
</details>
|
|
252
263
|
|
|
253
264
|
<details id="antigravity-auto-load">
|
|
@@ -255,6 +266,46 @@ When wrapping up, always call `mcp__prism-mcp__session_save_ledger` and `mcp__pr
|
|
|
255
266
|
|
|
256
267
|
See the [Gemini Setup Guide](docs/SETUP_GEMINI.md) for the proven three-layer prompt architecture to ensure reliable session auto-loading.
|
|
257
268
|
|
|
269
|
+
Antigravity doesn't expose MCP tools to the model. Use the `prism load` CLI as a fallback:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# From a shell or run_command tool
|
|
273
|
+
prism load my-project --level standard --json
|
|
274
|
+
|
|
275
|
+
# Or via the wrapper script
|
|
276
|
+
bash ~/.gemini/antigravity/scratch/prism_session_loader.sh my-project
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
The CLI uses the same storage layer as the MCP tool (SQLite or Supabase).
|
|
280
|
+
|
|
281
|
+
</details>
|
|
282
|
+
|
|
283
|
+
<details>
|
|
284
|
+
<summary><strong>Bash / CI/CD / Scripts</strong></summary>
|
|
285
|
+
|
|
286
|
+
Use the `prism load` CLI to access session context from any shell environment:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Quick check — human-readable
|
|
290
|
+
prism load my-project
|
|
291
|
+
|
|
292
|
+
# Parse JSON in scripts
|
|
293
|
+
CONTEXT=$(prism load my-project --level quick --json)
|
|
294
|
+
SUMMARY=$(echo "$CONTEXT" | jq -r '.handoff[0].last_summary')
|
|
295
|
+
VERSION=$(echo "$CONTEXT" | jq -r '.handoff[0].version')
|
|
296
|
+
echo "Project at v$VERSION: $SUMMARY"
|
|
297
|
+
|
|
298
|
+
# Role-scoped loading
|
|
299
|
+
prism load my-project --role qa --json
|
|
300
|
+
|
|
301
|
+
# Use in CI/CD to verify context exists before deploying
|
|
302
|
+
if ! prism load my-project --level quick --json | jq -e '.handoff[0].version' > /dev/null 2>&1; then
|
|
303
|
+
echo "No Prism context found — skipping context-aware deploy"
|
|
304
|
+
fi
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
> 📦 **Install:** `npm install -g prism-mcp-server` makes the `prism` CLI available globally. For local builds: `node /path/to/prism/dist/cli.js load`.
|
|
308
|
+
|
|
258
309
|
</details>
|
|
259
310
|
|
|
260
311
|
<details>
|
|
@@ -791,6 +842,29 @@ Every other AI coding pipeline has a fatal flaw: it asks the same model that wro
|
|
|
791
842
|
|
|
792
843
|
---
|
|
793
844
|
|
|
845
|
+
## 💻 CLI Reference
|
|
846
|
+
|
|
847
|
+
Prism includes a CLI for environments where MCP tools aren't available (CI/CD pipelines, Bash scripts, non-MCP IDEs like Antigravity):
|
|
848
|
+
|
|
849
|
+
```bash
|
|
850
|
+
# Load session context (same output as session_load_context MCP tool)
|
|
851
|
+
prism load my-project # Human-readable, standard depth
|
|
852
|
+
prism load my-project --level deep # Full context
|
|
853
|
+
prism load my-project --level quick --json # Machine-readable JSON
|
|
854
|
+
prism load my-project --role dev --json # Role-scoped loading
|
|
855
|
+
|
|
856
|
+
# Verification harness
|
|
857
|
+
prism verify status # Check verification state
|
|
858
|
+
prism verify status --json # Machine-readable output
|
|
859
|
+
prism verify generate # Bless current rubric as canonical
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
> 💡 **When to use the CLI vs MCP tools:** If your environment supports MCP (Claude Desktop, Cursor, Windsurf, Cline), always use the MCP tools — they integrate seamlessly with the agent's tool-calling flow. Use the CLI when you need session context in scripts, CI/CD, or non-MCP IDEs.
|
|
863
|
+
|
|
864
|
+
> 📦 **Installation:** The CLI is available as `prism` when installed globally (`npm install -g prism-mcp-server`), or via `node dist/cli.js` for local dev builds.
|
|
865
|
+
|
|
866
|
+
---
|
|
867
|
+
|
|
794
868
|
## 🔧 Tool Reference
|
|
795
869
|
|
|
796
870
|
Prism ships 30+ tools, but **90% of your workflow uses just three:**
|
package/dist/cli.js
CHANGED
|
@@ -3,11 +3,117 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { SqliteStorage } from './storage/sqlite.js';
|
|
4
4
|
import { handleVerifyStatus, handleGenerateHarness } from './verification/cliHandler.js';
|
|
5
5
|
import * as path from 'path';
|
|
6
|
+
import { getStorage, closeStorage } from './storage/index.js';
|
|
7
|
+
import { getSetting } from './storage/configStorage.js';
|
|
8
|
+
import { PRISM_USER_ID, SERVER_CONFIG } from './config.js';
|
|
9
|
+
import { getCurrentGitState } from './utils/git.js';
|
|
6
10
|
const program = new Command();
|
|
7
11
|
program
|
|
8
12
|
.name('prism')
|
|
9
|
-
.description('Prism
|
|
10
|
-
.version(
|
|
13
|
+
.description('Prism — The Mind Palace for AI Agents')
|
|
14
|
+
.version(SERVER_CONFIG.version);
|
|
15
|
+
// ─── prism load <project> ─────────────────────────────────────
|
|
16
|
+
// Loads session context using the same storage layer as the MCP
|
|
17
|
+
// session_load_context tool. Works with both SQLite and Supabase.
|
|
18
|
+
// Designed for environments that cannot use MCP tools directly
|
|
19
|
+
// (Antigravity, Bash scripts, CI/CD pipelines).
|
|
20
|
+
program
|
|
21
|
+
.command('load <project>')
|
|
22
|
+
.description('Load session context for a project (same output as session_load_context MCP tool)')
|
|
23
|
+
.option('-l, --level <level>', 'Context depth: quick, standard, deep', 'standard')
|
|
24
|
+
.option('-r, --role <role>', 'Role scope for context loading')
|
|
25
|
+
.option('--json', 'Emit machine-readable JSON instead of formatted text')
|
|
26
|
+
.action(async (project, options) => {
|
|
27
|
+
try {
|
|
28
|
+
const { level, role, json: jsonOutput } = options;
|
|
29
|
+
const validLevels = ['quick', 'standard', 'deep'];
|
|
30
|
+
if (!validLevels.includes(level)) {
|
|
31
|
+
console.error(`Error: Invalid level "${level}". Must be one of: ${validLevels.join(', ')}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// Use the shared storage singleton (respects PRISM_STORAGE, dashboard config, etc.)
|
|
35
|
+
const storage = await getStorage();
|
|
36
|
+
const effectiveRole = role || await getSetting('default_role', '') || undefined;
|
|
37
|
+
const data = await storage.loadContext(project, level, PRISM_USER_ID, effectiveRole);
|
|
38
|
+
if (!data) {
|
|
39
|
+
if (jsonOutput) {
|
|
40
|
+
console.log(JSON.stringify({ error: `No session context found for project "${project}"` }));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.log(`No session context found for project "${project}" at level ${level}.`);
|
|
44
|
+
console.log('This project has no previous session history. Starting fresh.');
|
|
45
|
+
}
|
|
46
|
+
await closeStorage();
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
const d = data;
|
|
50
|
+
// Gather git state for enrichment
|
|
51
|
+
const gitState = getCurrentGitState();
|
|
52
|
+
if (jsonOutput) {
|
|
53
|
+
// Machine-readable JSON envelope — matches what prism_session_loader.sh produced
|
|
54
|
+
const output = {
|
|
55
|
+
handoff: [{
|
|
56
|
+
project,
|
|
57
|
+
role: effectiveRole || d.role || 'global',
|
|
58
|
+
last_summary: d.last_summary || null,
|
|
59
|
+
pending_todo: d.pending_todo || null,
|
|
60
|
+
active_decisions: d.active_decisions || null,
|
|
61
|
+
keywords: d.keywords || null,
|
|
62
|
+
key_context: d.key_context || null,
|
|
63
|
+
active_branch: d.active_branch || null,
|
|
64
|
+
version: d.version ?? null,
|
|
65
|
+
updated_at: d.updated_at || null,
|
|
66
|
+
}],
|
|
67
|
+
recent_ledger: (d.recent_sessions || []).map((s) => ({
|
|
68
|
+
summary: s.summary || null,
|
|
69
|
+
decisions: s.decisions || null,
|
|
70
|
+
keywords: s.keywords || null,
|
|
71
|
+
created_at: s.session_date || s.created_at || null,
|
|
72
|
+
})),
|
|
73
|
+
git_hash: gitState.commitSha ? gitState.commitSha.substring(0, 7) : null,
|
|
74
|
+
git_branch: gitState.branch || null,
|
|
75
|
+
pkg_version: SERVER_CONFIG.version,
|
|
76
|
+
};
|
|
77
|
+
console.log(JSON.stringify(output, null, 2));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Human-readable formatted output (same format as MCP tool)
|
|
81
|
+
let output = `📋 Session context for "${project}" (${level}):\n\n`;
|
|
82
|
+
if (d.last_summary)
|
|
83
|
+
output += `📝 Last Summary: ${d.last_summary}\n`;
|
|
84
|
+
if (d.active_branch)
|
|
85
|
+
output += `🌿 Active Branch: ${d.active_branch}\n`;
|
|
86
|
+
if (d.key_context)
|
|
87
|
+
output += `💡 Key Context: ${d.key_context}\n`;
|
|
88
|
+
if (d.pending_todo?.length) {
|
|
89
|
+
output += `\n✅ Open TODOs:\n` + d.pending_todo.map((t) => ` - ${t}`).join('\n') + '\n';
|
|
90
|
+
}
|
|
91
|
+
if (d.active_decisions?.length) {
|
|
92
|
+
output += `\n⚖️ Active Decisions:\n` + d.active_decisions.map((dec) => ` - ${dec}`).join('\n') + '\n';
|
|
93
|
+
}
|
|
94
|
+
if (d.keywords?.length) {
|
|
95
|
+
output += `\n🔑 Keywords: ${d.keywords.join(', ')}\n`;
|
|
96
|
+
}
|
|
97
|
+
if (d.recent_sessions?.length) {
|
|
98
|
+
output += `\n⏳ Recent Sessions:\n` + d.recent_sessions.map((s) => ` [${s.session_date?.split('T')[0]}] ${s.summary}`).join('\n') + '\n';
|
|
99
|
+
}
|
|
100
|
+
if (d.version != null) {
|
|
101
|
+
output += `\n🔑 Session version: ${d.version}. Pass expected_version: ${d.version} when saving handoff.`;
|
|
102
|
+
}
|
|
103
|
+
if (gitState.isRepo) {
|
|
104
|
+
output += `\n\n🔧 Git: ${gitState.branch} @ ${gitState.commitSha?.substring(0, 7)} (Prism v${SERVER_CONFIG.version})`;
|
|
105
|
+
}
|
|
106
|
+
console.log(output);
|
|
107
|
+
}
|
|
108
|
+
await closeStorage();
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
console.error(`Error loading context: ${err instanceof Error ? err.message : String(err)}`);
|
|
112
|
+
await closeStorage().catch(() => { });
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// ─── prism verify ─────────────────────────────────────────────
|
|
11
117
|
const verifyCmd = program
|
|
12
118
|
.command('verify')
|
|
13
119
|
.description('Manage the verification harness');
|
package/dist/config.js
CHANGED
|
@@ -46,21 +46,21 @@ export const SERVER_CONFIG = {
|
|
|
46
46
|
};
|
|
47
47
|
// ─── Required: Brave Search API Key ───────────────────────────
|
|
48
48
|
export const BRAVE_API_KEY = process.env.BRAVE_API_KEY;
|
|
49
|
-
if (!BRAVE_API_KEY) {
|
|
49
|
+
if (!BRAVE_API_KEY && process.env.PRISM_DEBUG_LOGGING === "true") {
|
|
50
50
|
console.error("Warning: BRAVE_API_KEY environment variable is missing. Search tools will return errors when called.");
|
|
51
51
|
}
|
|
52
52
|
// ─── Optional: Google Gemini API Key ──────────────────────────
|
|
53
53
|
// Used by the gemini_research_paper_analysis tool.
|
|
54
54
|
// Without this, the tool will still appear but will error when called.
|
|
55
55
|
export const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
|
|
56
|
-
if (!GOOGLE_API_KEY) {
|
|
56
|
+
if (!GOOGLE_API_KEY && process.env.PRISM_DEBUG_LOGGING === "true") {
|
|
57
57
|
console.error("Warning: GOOGLE_API_KEY environment variable is missing. Gemini research features will be unavailable.");
|
|
58
58
|
}
|
|
59
59
|
// ─── Optional: Brave Answers API Key ──────────────────────────
|
|
60
60
|
// Used by the brave_answers tool for AI-grounded answers.
|
|
61
61
|
// This is a separate API key from the main Brave Search key.
|
|
62
62
|
export const BRAVE_ANSWERS_API_KEY = process.env.BRAVE_ANSWERS_API_KEY;
|
|
63
|
-
if (!BRAVE_ANSWERS_API_KEY) {
|
|
63
|
+
if (!BRAVE_ANSWERS_API_KEY && process.env.PRISM_DEBUG_LOGGING === "true") {
|
|
64
64
|
console.error("Warning: BRAVE_ANSWERS_API_KEY environment variable is missing. Brave Answers tool will be unavailable.");
|
|
65
65
|
}
|
|
66
66
|
// ─── Optional: Voyage AI API Key ──────────────────────────────
|
package/dist/storage/index.js
CHANGED
|
@@ -16,9 +16,31 @@ export async function getStorage() {
|
|
|
16
16
|
// Use environment variable if explicitly set, otherwise fall back to db config
|
|
17
17
|
const envStorage = process.env.PRISM_STORAGE;
|
|
18
18
|
const requestedBackend = (envStorage || await getSetting("PRISM_STORAGE", ENV_PRISM_STORAGE));
|
|
19
|
-
// Guardrail: if Supabase is requested but credentials are
|
|
20
|
-
//
|
|
21
|
-
|
|
19
|
+
// Guardrail: if Supabase is requested but env-var credentials are missing,
|
|
20
|
+
// check the dashboard config DB (prism-config.db) as a fallback before
|
|
21
|
+
// giving up. Dashboard settings take precedence over absent env vars.
|
|
22
|
+
let supabaseReady = SUPABASE_CONFIGURED;
|
|
23
|
+
if (!supabaseReady && requestedBackend === "supabase") {
|
|
24
|
+
const dashUrl = await getSetting("SUPABASE_URL");
|
|
25
|
+
const dashKey = await getSetting("SUPABASE_KEY");
|
|
26
|
+
if (dashUrl && dashKey) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = new URL(dashUrl);
|
|
29
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
30
|
+
supabaseReady = true;
|
|
31
|
+
// Inject into process.env so downstream consumers (SupabaseStorage,
|
|
32
|
+
// SyncBus) pick them up without needing their own dashboard lookups.
|
|
33
|
+
process.env.SUPABASE_URL = dashUrl;
|
|
34
|
+
process.env.SUPABASE_KEY = dashKey;
|
|
35
|
+
debugLog("[Prism Storage] Using Supabase credentials from dashboard config");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Invalid URL — fall through to local fallback
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (requestedBackend === "supabase" && !supabaseReady) {
|
|
22
44
|
activeStorageBackend = "local";
|
|
23
45
|
console.error("[Prism Storage] Supabase backend requested but SUPABASE_URL/SUPABASE_KEY are invalid or unresolved. Falling back to local storage.");
|
|
24
46
|
}
|
package/dist/sync/factory.js
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SyncBus Factory — Routes to the correct sync implementation (v2.0 — Step 6)
|
|
3
|
-
*
|
|
4
|
-
* Returns SqliteSyncBus for local mode, SupabaseSyncBus for cloud mode.
|
|
5
|
-
* Uses the same PRISM_STORAGE env var as the storage factory.
|
|
6
|
-
*
|
|
7
|
-
* Singleton pattern — only one bus per process (prevents duplicate watchers).
|
|
8
|
-
*/
|
|
9
1
|
import { PRISM_STORAGE } from "../config.js";
|
|
10
2
|
import { debugLog } from "../utils/logger.js";
|
|
3
|
+
import { getSetting } from "../storage/configStorage.js";
|
|
11
4
|
let _bus = null;
|
|
12
5
|
export async function getSyncBus() {
|
|
13
6
|
if (_bus)
|
|
@@ -18,10 +11,11 @@ export async function getSyncBus() {
|
|
|
18
11
|
}
|
|
19
12
|
else {
|
|
20
13
|
const { SupabaseSyncBus } = await import("./supabaseSync.js");
|
|
21
|
-
|
|
22
|
-
const
|
|
14
|
+
// Check env vars first, then fall back to dashboard config (prism-config.db)
|
|
15
|
+
const url = process.env.SUPABASE_URL || await getSetting("SUPABASE_URL");
|
|
16
|
+
const key = process.env.SUPABASE_KEY || process.env.SUPABASE_ANON_KEY || await getSetting("SUPABASE_KEY");
|
|
23
17
|
if (!url || !key) {
|
|
24
|
-
|
|
18
|
+
debugLog("[SyncBus] Supabase credentials not found in env or dashboard — falling back to local sync bus");
|
|
25
19
|
const { SqliteSyncBus } = await import("./sqliteSync.js");
|
|
26
20
|
_bus = new SqliteSyncBus();
|
|
27
21
|
}
|
|
@@ -1,26 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
// SUPABASE_URL / SUPABASE_KEY are read at call-time from process.env
|
|
2
|
+
// (not import-time) so dashboard-injected credentials are picked up.
|
|
2
3
|
/**
|
|
3
4
|
* Makes a single authenticated HTTP request to the Supabase REST API.
|
|
4
5
|
* All public functions below delegate to this.
|
|
5
6
|
*/
|
|
6
7
|
async function supabaseRequest(opts) {
|
|
7
|
-
//
|
|
8
|
-
|
|
8
|
+
// Read credentials at call time (not import time) so that dashboard-injected
|
|
9
|
+
// values in process.env are picked up after storage/index.ts resolves them.
|
|
10
|
+
const url = process.env.SUPABASE_URL;
|
|
11
|
+
const key = process.env.SUPABASE_KEY;
|
|
12
|
+
if (!url || !key) {
|
|
9
13
|
throw new Error("Supabase not configured (SUPABASE_URL / SUPABASE_KEY missing)");
|
|
10
14
|
}
|
|
11
15
|
// Build the full URL with any query parameters
|
|
12
|
-
const
|
|
16
|
+
const reqUrl = new URL(`${url}${opts.path}`);
|
|
13
17
|
if (opts.params) {
|
|
14
18
|
for (const [k, v] of Object.entries(opts.params)) {
|
|
15
|
-
|
|
19
|
+
reqUrl.searchParams.set(k, v);
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
// Make the HTTP request with authentication headers
|
|
19
|
-
const response = await fetch(
|
|
23
|
+
const response = await fetch(reqUrl.toString(), {
|
|
20
24
|
method: opts.method,
|
|
21
25
|
headers: {
|
|
22
|
-
"apikey":
|
|
23
|
-
"Authorization": `Bearer ${
|
|
26
|
+
"apikey": key, // Supabase API key (required for all requests)
|
|
27
|
+
"Authorization": `Bearer ${key}`, // Also passed as Bearer token
|
|
24
28
|
"Content-Type": "application/json",
|
|
25
29
|
// "Prefer" header controls response behavior:
|
|
26
30
|
// - "return=representation" means return the inserted/updated row in the response
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.2.0",
|
|
4
4
|
"mcpName": "io.github.dcostenco/prism-mcp",
|
|
5
5
|
"description": "The Mind Palace for AI Agents — a true Cognitive Architecture with Hebbian learning (episodic→semantic consolidation), ACT-R spreading activation (multi-hop causal reasoning), uncertainty-aware rejection gates (agents that know when they don't know), adversarial evaluation (anti-sycophancy), fail-closed Dark Factory pipelines, persistent memory (SQLite/Supabase), multi-agent Hivemind, time travel & visual dashboard. Zero-config local mode.",
|
|
6
6
|
"module": "index.ts",
|