prism-mcp-server 6.1.8 → 6.1.9
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 +9 -3
- package/dist/config.js +4 -2
- package/dist/scholar/webScholar.js +71 -41
- package/dist/tools/sessionMemoryDefinitions.js +60 -35
- package/dist/utils/tavilyApi.js +70 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ Add to your MCP client config (`claude_desktop_config.json`, `.cursor/mcp.json`,
|
|
|
77
77
|
| Semantic vector search | ❌ | ✅ `GOOGLE_API_KEY` |
|
|
78
78
|
| Morning Briefings | ❌ | ✅ `GOOGLE_API_KEY` |
|
|
79
79
|
| Auto-compaction | ❌ | ✅ `GOOGLE_API_KEY` |
|
|
80
|
-
| Web Scholar research | ❌ | ✅ `BRAVE_API_KEY` + `FIRECRAWL_API_KEY` |
|
|
80
|
+
| Web Scholar research | ❌ | ✅ `BRAVE_API_KEY` + `FIRECRAWL_API_KEY` (or `TAVILY_API_KEY`) |
|
|
81
81
|
| VLM image captioning | ❌ | ✅ Provider key |
|
|
82
82
|
|
|
83
83
|
> 🔑 The core Mind Palace works **100% offline** with zero API keys. Cloud keys unlock intelligence features. See [Environment Variables](#environment-variables).
|
|
@@ -382,7 +382,7 @@ Soft/hard delete (Art. 17), full export in JSON, Markdown, or Obsidian vault `.z
|
|
|
382
382
|
## 🆕 What's New
|
|
383
383
|
|
|
384
384
|
### v6.1 — Prism-Port, Cognitive Load & Semantic Search ✅
|
|
385
|
-
> **Current stable release (v6.1.
|
|
385
|
+
> **Current stable release (v6.1.9).** Data sovereignty meets active memory intelligence.
|
|
386
386
|
|
|
387
387
|
- 📦 **Prism-Port Vault Export** — New `vault` format for `session_export_memory`. Generates a `.zip` of interlinked Markdown files with YAML frontmatter, `[[Wikilinks]]`, and auto-generated `Keywords/` backlink indices. Drop into Obsidian or Logseq for instant knowledge graph.
|
|
388
388
|
- 🏛️ **Dashboard Export Vault Button** — "🏛️ Export Vault" button in the Mind Palace UI exports the full Prism-Port vault ZIP directly from the browser. Both `/api/export` and `/api/export/vault` now use the unified `buildVaultDirectory` path — same rich format as the MCP tool.
|
|
@@ -392,6 +392,11 @@ Soft/hard delete (Art. 17), full export in JSON, Markdown, or Obsidian vault `.z
|
|
|
392
392
|
- 📊 **Deep Purge Visualization** — A zero-overhead "Memory Density" analytic providing instant signal-to-noise ratio visibility (Graduated ideas vs raw concepts).
|
|
393
393
|
- 🛡️ **Context-Boosted Search** — Biases semantic queries by intelligently interleaving your current project workspace.
|
|
394
394
|
|
|
395
|
+
#### v6.1.9 — Web Scholar Tavily Integration
|
|
396
|
+
- 🌐 **Tavily Core** — The Web Scholar now supports `@tavily/core` as an all-in-one search and extraction alternative to Brave+Firecrawl.
|
|
397
|
+
- 📦 **API Chunking Limits** — Advanced looping logic transparently works around `tavily.extract` 20-URL boundaries.
|
|
398
|
+
- 🛡️ **Network Resilience** — Handled promise rejections prevent data loss out-of-bounds due to chunk extraction or upstream network timeouts.
|
|
399
|
+
|
|
395
400
|
#### v6.1.8 — Type Guard Hardening (Production Safety)
|
|
396
401
|
- 🛡️ **Missing Guard Added** — `isSessionCompactLedgerArgs` was absent; an LLM passing `{threshold: "many"}` would reach the handler as a string. Added full validation for all four optional fields.
|
|
397
402
|
- ✅ **Array Field Validation** — `isSessionSaveLedgerArgs` now guards `todos`, `files_changed`, and `decisions` with `Array.isArray` checks — prevents a hallucinated `{todos: "string"}` from bypassing the type system.
|
|
@@ -568,7 +573,8 @@ Requires `PRISM_ENABLE_HIVEMIND=true`.
|
|
|
568
573
|
| Variable | Required | Description |
|
|
569
574
|
|----------|----------|-------------|
|
|
570
575
|
| `BRAVE_API_KEY` | No | Brave Search Pro API key |
|
|
571
|
-
| `FIRECRAWL_API_KEY` | No | Firecrawl API key — required for Web Scholar |
|
|
576
|
+
| `FIRECRAWL_API_KEY` | No | Firecrawl API key — required for Web Scholar (unless using Tavily) |
|
|
577
|
+
| `TAVILY_API_KEY` | No | Tavily Search API key — alternative to Brave+Firecrawl for Web Scholar |
|
|
572
578
|
| `PRISM_STORAGE` | No | `"local"` (default) or `"supabase"` — restart required |
|
|
573
579
|
| `PRISM_ENABLE_HIVEMIND` | No | `"true"` to enable multi-agent tools — restart required |
|
|
574
580
|
| `PRISM_INSTANCE` | No | Instance name for multi-server PID isolation |
|
package/dist/config.js
CHANGED
|
@@ -145,11 +145,13 @@ export const PRISM_SCHEDULER_INTERVAL_MS = parseInt(process.env.PRISM_SCHEDULER_
|
|
|
145
145
|
);
|
|
146
146
|
// ─── v5.4: Autonomous Web Scholar ─────────────────────────────
|
|
147
147
|
// Background LLM research pipeline powered by Brave Search + Firecrawl.
|
|
148
|
+
// Tavily can be used as an alternative when TAVILY_API_KEY is set.
|
|
148
149
|
// Defaults are conservative to prevent runaway API costs.
|
|
149
150
|
export const FIRECRAWL_API_KEY = process.env.FIRECRAWL_API_KEY;
|
|
151
|
+
export const TAVILY_API_KEY = process.env.TAVILY_API_KEY;
|
|
150
152
|
export const PRISM_SCHOLAR_ENABLED = process.env.PRISM_SCHOLAR_ENABLED === "true"; // Opt-in
|
|
151
|
-
if (PRISM_SCHOLAR_ENABLED && !FIRECRAWL_API_KEY) {
|
|
152
|
-
console.error("Warning: FIRECRAWL_API_KEY
|
|
153
|
+
if (PRISM_SCHOLAR_ENABLED && !FIRECRAWL_API_KEY && !TAVILY_API_KEY) {
|
|
154
|
+
console.error("Warning: Neither FIRECRAWL_API_KEY nor TAVILY_API_KEY is set. Web Scholar will fall back to free search.");
|
|
153
155
|
}
|
|
154
156
|
export const PRISM_SCHOLAR_INTERVAL_MS = parseInt(process.env.PRISM_SCHOLAR_INTERVAL_MS || "0", 10 // Default manual-only
|
|
155
157
|
);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { BRAVE_API_KEY, FIRECRAWL_API_KEY, PRISM_SCHOLAR_MAX_ARTICLES_PER_RUN, PRISM_USER_ID, PRISM_SCHOLAR_TOPICS, PRISM_ENABLE_HIVEMIND } from "../config.js";
|
|
1
|
+
import { BRAVE_API_KEY, FIRECRAWL_API_KEY, TAVILY_API_KEY, PRISM_SCHOLAR_MAX_ARTICLES_PER_RUN, PRISM_USER_ID, PRISM_SCHOLAR_TOPICS, PRISM_ENABLE_HIVEMIND } from "../config.js";
|
|
2
2
|
import { getStorage } from "../storage/index.js";
|
|
3
3
|
import { debugLog } from "../utils/logger.js";
|
|
4
4
|
import { getLLMProvider } from "../utils/llm/factory.js";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { performWebSearchRaw } from "../utils/braveApi.js";
|
|
7
|
+
import { performTavilySearch, performTavilyExtract } from "../utils/tavilyApi.js";
|
|
7
8
|
import { getTracer } from "../utils/telemetry.js";
|
|
8
9
|
import { searchYahooFree, scrapeArticleLocal } from "./freeSearch.js";
|
|
9
10
|
// ─── Hivemind Integration Helpers ────────────────────────────
|
|
@@ -129,7 +130,13 @@ export async function runWebScholar() {
|
|
|
129
130
|
const tracer = getTracer();
|
|
130
131
|
const span = tracer.startSpan("background.web_scholar");
|
|
131
132
|
try {
|
|
132
|
-
|
|
133
|
+
// Pipeline priority:
|
|
134
|
+
// 1. Brave + Firecrawl (when both keys present)
|
|
135
|
+
// 2. Tavily Search + Extract (when TAVILY_API_KEY set but Brave/Firecrawl missing)
|
|
136
|
+
// 3. Yahoo + Readability free fallback (no paid keys)
|
|
137
|
+
const useBraveFirecrawl = !!(BRAVE_API_KEY && FIRECRAWL_API_KEY);
|
|
138
|
+
const useTavily = !useBraveFirecrawl && !!TAVILY_API_KEY;
|
|
139
|
+
const useFreeFallback = !useBraveFirecrawl && !useTavily;
|
|
133
140
|
if (!PRISM_SCHOLAR_TOPICS || PRISM_SCHOLAR_TOPICS.length === 0) {
|
|
134
141
|
debugLog("[WebScholar] Skipped: No topics configured in PRISM_SCHOLAR_TOPICS");
|
|
135
142
|
span.setAttribute("scholar.skipped_reason", "no_topics");
|
|
@@ -148,16 +155,21 @@ export async function runWebScholar() {
|
|
|
148
155
|
// 3. Search for articles
|
|
149
156
|
await hivemindHeartbeat(`Searching for: ${topic}`);
|
|
150
157
|
let urls = [];
|
|
151
|
-
if (
|
|
152
|
-
debugLog("[WebScholar] API keys missing, falling back to Local Free Search (Yahoo + Readability)");
|
|
153
|
-
const ddgResults = await searchYahooFree(topic, PRISM_SCHOLAR_MAX_ARTICLES_PER_RUN);
|
|
154
|
-
urls = ddgResults.map(r => r.url).filter(Boolean);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
158
|
+
if (useBraveFirecrawl) {
|
|
157
159
|
const braveResponse = await performWebSearchRaw(topic, PRISM_SCHOLAR_MAX_ARTICLES_PER_RUN);
|
|
158
160
|
const braveData = JSON.parse(braveResponse);
|
|
159
161
|
urls = (braveData.web?.results || []).map((r) => r.url).filter(Boolean);
|
|
160
162
|
}
|
|
163
|
+
else if (useTavily) {
|
|
164
|
+
debugLog("[WebScholar] Using Tavily Search for URL discovery");
|
|
165
|
+
const tavilyResults = await performTavilySearch(TAVILY_API_KEY, topic, PRISM_SCHOLAR_MAX_ARTICLES_PER_RUN);
|
|
166
|
+
urls = tavilyResults.map(r => r.url).filter(Boolean);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
debugLog("[WebScholar] API keys missing, falling back to Local Free Search (Yahoo + Readability)");
|
|
170
|
+
const ddgResults = await searchYahooFree(topic, PRISM_SCHOLAR_MAX_ARTICLES_PER_RUN);
|
|
171
|
+
urls = ddgResults.map(r => r.url).filter(Boolean);
|
|
172
|
+
}
|
|
161
173
|
if (urls.length === 0) {
|
|
162
174
|
debugLog(`[WebScholar] No articles found for "${topic}"`);
|
|
163
175
|
span.setAttribute("scholar.skipped_reason", "no_search_results");
|
|
@@ -168,44 +180,62 @@ export async function runWebScholar() {
|
|
|
168
180
|
// 4. Scrape each URL
|
|
169
181
|
await hivemindHeartbeat(`Scraping ${urls.length} articles on: ${topic}`);
|
|
170
182
|
const scrapedTexts = [];
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
if (useTavily) {
|
|
184
|
+
// Tavily Extract: batch all URLs at once (up to 20)
|
|
185
|
+
try {
|
|
186
|
+
debugLog(`[WebScholar] Extracting ${urls.length} URLs via Tavily Extract`);
|
|
187
|
+
const extracted = await performTavilyExtract(TAVILY_API_KEY, urls);
|
|
188
|
+
for (const item of extracted) {
|
|
189
|
+
if (item.rawContent) {
|
|
190
|
+
const trimmed = item.rawContent.slice(0, 15_000);
|
|
191
|
+
scrapedTexts.push(`Source: ${item.url}\n\n${trimmed}\n\n---\n`);
|
|
192
|
+
}
|
|
181
193
|
}
|
|
182
194
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
})
|
|
196
|
-
});
|
|
197
|
-
if (!scrapeRes.ok) {
|
|
198
|
-
console.error(`[WebScholar] Firecrawl failed for ${url}: ${scrapeRes.status}`);
|
|
199
|
-
continue;
|
|
195
|
+
catch (err) {
|
|
196
|
+
console.error("[WebScholar] Tavily Extract failed:", err);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
for (const url of urls) {
|
|
201
|
+
if (useFreeFallback) {
|
|
202
|
+
try {
|
|
203
|
+
debugLog(`[WebScholar] Scraping local fallback: ${url}`);
|
|
204
|
+
const article = await scrapeArticleLocal(url);
|
|
205
|
+
const trimmed = article.content.slice(0, 15_000);
|
|
206
|
+
scrapedTexts.push(`Source: ${url}\nTitle: ${article.title}\n\n${trimmed}\n\n---\n`);
|
|
200
207
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const trimmed = result.data.markdown.slice(0, 15_000);
|
|
204
|
-
scrapedTexts.push(`Source: ${url}\n\n${trimmed}\n\n---\n`);
|
|
208
|
+
catch (err) {
|
|
209
|
+
console.error(`[WebScholar] Failed to locally scrape ${url}:`, err);
|
|
205
210
|
}
|
|
206
211
|
}
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
else {
|
|
213
|
+
try {
|
|
214
|
+
debugLog(`[WebScholar] Scraping Firecrawl: ${url}`);
|
|
215
|
+
const scrapeRes = await fetch("https://api.firecrawl.dev/v1/scrape", {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: {
|
|
218
|
+
"Content-Type": "application/json",
|
|
219
|
+
"Authorization": `Bearer ${FIRECRAWL_API_KEY}`
|
|
220
|
+
},
|
|
221
|
+
body: JSON.stringify({
|
|
222
|
+
url,
|
|
223
|
+
formats: ["markdown"],
|
|
224
|
+
})
|
|
225
|
+
});
|
|
226
|
+
if (!scrapeRes.ok) {
|
|
227
|
+
console.error(`[WebScholar] Firecrawl failed for ${url}: ${scrapeRes.status}`);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const result = (await scrapeRes.json());
|
|
231
|
+
if (result.success && result.data?.markdown) {
|
|
232
|
+
const trimmed = result.data.markdown.slice(0, 15_000);
|
|
233
|
+
scrapedTexts.push(`Source: ${url}\n\n${trimmed}\n\n---\n`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
console.error(`[WebScholar] Failed to scrape ${url}:`, err);
|
|
238
|
+
}
|
|
209
239
|
}
|
|
210
240
|
}
|
|
211
241
|
}
|
|
@@ -133,8 +133,16 @@ export const SESSION_LOAD_CONTEXT_TOOL = {
|
|
|
133
133
|
type: "integer",
|
|
134
134
|
description: "Maximum token budget for context response. Uses 1 token ≈ 4 chars heuristic. When set, the response is truncated to fit within the budget. Default: unlimited.",
|
|
135
135
|
},
|
|
136
|
+
toolAction: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Brief 2-5 word summary of what this tool is doing. Capitalize like a sentence.",
|
|
139
|
+
},
|
|
140
|
+
toolSummary: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "Brief 2-5 word noun phrase describing what this tool call is about.",
|
|
143
|
+
},
|
|
136
144
|
},
|
|
137
|
-
required: ["project"],
|
|
145
|
+
required: ["project", "toolAction", "toolSummary"],
|
|
138
146
|
},
|
|
139
147
|
};
|
|
140
148
|
// ─── Knowledge Search ─────────────────────────────────────────
|
|
@@ -181,6 +189,7 @@ export const KNOWLEDGE_SEARCH_TOOL = {
|
|
|
181
189
|
"latency breakdown, and scoring metadata for explainability. Default: false.",
|
|
182
190
|
},
|
|
183
191
|
},
|
|
192
|
+
required: ["query"],
|
|
184
193
|
},
|
|
185
194
|
};
|
|
186
195
|
// ─── Knowledge Forget ─────────────────────────────────────────
|
|
@@ -421,9 +430,9 @@ export function isKnowledgeSearchArgs(args) {
|
|
|
421
430
|
if (typeof args !== "object" || args === null)
|
|
422
431
|
return false;
|
|
423
432
|
const a = args;
|
|
424
|
-
if (
|
|
433
|
+
if (typeof a.query !== "string")
|
|
425
434
|
return false;
|
|
426
|
-
if (a.
|
|
435
|
+
if (a.project !== undefined && typeof a.project !== "string")
|
|
427
436
|
return false;
|
|
428
437
|
if (a.category !== undefined && typeof a.category !== "string")
|
|
429
438
|
return false;
|
|
@@ -444,12 +453,12 @@ export function isSessionSaveLedgerArgs(args) {
|
|
|
444
453
|
return false;
|
|
445
454
|
if (typeof a.summary !== "string")
|
|
446
455
|
return false;
|
|
447
|
-
// Optional array fields — guard against LLM passing a string instead of string[]
|
|
448
|
-
if (a.todos !== undefined && !Array.isArray(a.todos))
|
|
456
|
+
// Optional array fields — guard against LLM passing a string instead of string[] and check elements
|
|
457
|
+
if (a.todos !== undefined && (!Array.isArray(a.todos) || !a.todos.every(t => typeof t === "string")))
|
|
449
458
|
return false;
|
|
450
|
-
if (a.files_changed !== undefined && !Array.isArray(a.files_changed))
|
|
459
|
+
if (a.files_changed !== undefined && (!Array.isArray(a.files_changed) || !a.files_changed.every(t => typeof t === "string")))
|
|
451
460
|
return false;
|
|
452
|
-
if (a.decisions !== undefined && !Array.isArray(a.decisions))
|
|
461
|
+
if (a.decisions !== undefined && (!Array.isArray(a.decisions) || !a.decisions.every(t => typeof t === "string")))
|
|
453
462
|
return false;
|
|
454
463
|
if (a.role !== undefined && typeof a.role !== "string")
|
|
455
464
|
return false;
|
|
@@ -466,7 +475,7 @@ export function isSessionSaveHandoffArgs(args) {
|
|
|
466
475
|
return false;
|
|
467
476
|
if (a.expected_version !== undefined && typeof a.expected_version !== "number")
|
|
468
477
|
return false;
|
|
469
|
-
if (a.open_todos !== undefined && !Array.isArray(a.open_todos))
|
|
478
|
+
if (a.open_todos !== undefined && (!Array.isArray(a.open_todos) || !a.open_todos.every(t => typeof t === "string")))
|
|
470
479
|
return false;
|
|
471
480
|
if (a.active_branch !== undefined && typeof a.active_branch !== "string")
|
|
472
481
|
return false;
|
|
@@ -516,10 +525,12 @@ export function isBackfillEmbeddingsArgs(args) {
|
|
|
516
525
|
return true;
|
|
517
526
|
}
|
|
518
527
|
export function isBackfillLinksArgs(args) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
528
|
+
if (typeof args !== "object" || args === null)
|
|
529
|
+
return false;
|
|
530
|
+
const a = args;
|
|
531
|
+
if (typeof a.project !== "string")
|
|
532
|
+
return false;
|
|
533
|
+
return true;
|
|
523
534
|
}
|
|
524
535
|
export function isSessionLoadContextArgs(args) {
|
|
525
536
|
if (typeof args !== "object" || args === null)
|
|
@@ -533,6 +544,10 @@ export function isSessionLoadContextArgs(args) {
|
|
|
533
544
|
return false;
|
|
534
545
|
if (a.max_tokens !== undefined && typeof a.max_tokens !== "number")
|
|
535
546
|
return false;
|
|
547
|
+
if (a.toolAction !== undefined && typeof a.toolAction !== "string")
|
|
548
|
+
return false;
|
|
549
|
+
if (a.toolSummary !== undefined && typeof a.toolSummary !== "string")
|
|
550
|
+
return false;
|
|
536
551
|
return true;
|
|
537
552
|
}
|
|
538
553
|
// ─── v2.0: Time Travel Tool Definitions ──────────────────────
|
|
@@ -659,12 +674,14 @@ export function isSessionSaveImageArgs(args) {
|
|
|
659
674
|
return true;
|
|
660
675
|
}
|
|
661
676
|
export function isSessionViewImageArgs(args) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
677
|
+
if (typeof args !== "object" || args === null)
|
|
678
|
+
return false;
|
|
679
|
+
const a = args;
|
|
680
|
+
if (typeof a.project !== "string")
|
|
681
|
+
return false;
|
|
682
|
+
if (typeof a.image_id !== "string")
|
|
683
|
+
return false;
|
|
684
|
+
return true;
|
|
668
685
|
}
|
|
669
686
|
// ─── v2.2.0: Health Check (fsck) Tool Definition ─────────────
|
|
670
687
|
/**
|
|
@@ -675,12 +692,12 @@ export function isSessionViewImageArgs(args) {
|
|
|
675
692
|
export const SESSION_HEALTH_CHECK_TOOL = {
|
|
676
693
|
name: "session_health_check",
|
|
677
694
|
description: "Run integrity checks on the agent's memory (like fsck for filesystems). " +
|
|
678
|
-
"Scans for missing embeddings, duplicate entries, orphaned handoffs, and stale rollups
|
|
679
|
-
"Checks performed
|
|
680
|
-
"1. **Missing embeddings** — entries that can't be found via semantic search
|
|
681
|
-
"2. **Duplicate entries** — near-identical summaries wasting context tokens
|
|
682
|
-
"3. **Orphaned handoffs** — handoff state with no backing ledger entries
|
|
683
|
-
"4. **Stale rollups** — compaction artifacts with no archived originals
|
|
695
|
+
"Scans for missing embeddings, duplicate entries, orphaned handoffs, and stale rollups.\n\n" +
|
|
696
|
+
"Checks performed:\n" +
|
|
697
|
+
"1. **Missing embeddings** — entries that can't be found via semantic search\n" +
|
|
698
|
+
"2. **Duplicate entries** — near-identical summaries wasting context tokens\n" +
|
|
699
|
+
"3. **Orphaned handoffs** — handoff state with no backing ledger entries\n" +
|
|
700
|
+
"4. **Stale rollups** — compaction artifacts with no archived originals\n\n" +
|
|
684
701
|
"Use auto_fix=true to automatically repair missing embeddings and clean up orphans.",
|
|
685
702
|
inputSchema: {
|
|
686
703
|
type: "object",
|
|
@@ -864,12 +881,14 @@ export const KNOWLEDGE_SET_RETENTION_TOOL = {
|
|
|
864
881
|
},
|
|
865
882
|
};
|
|
866
883
|
export function isKnowledgeSetRetentionArgs(args) {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
884
|
+
if (typeof args !== "object" || args === null)
|
|
885
|
+
return false;
|
|
886
|
+
const a = args;
|
|
887
|
+
if (typeof a.project !== "string")
|
|
888
|
+
return false;
|
|
889
|
+
if (typeof a.ttl_days !== "number")
|
|
890
|
+
return false;
|
|
891
|
+
return true;
|
|
873
892
|
}
|
|
874
893
|
// ─── v4.0: Active Behavioral Memory Tools ────────────────────
|
|
875
894
|
export const SESSION_SAVE_EXPERIENCE_TOOL = {
|
|
@@ -929,7 +948,11 @@ export function isSessionSaveExperienceArgs(args) {
|
|
|
929
948
|
const a = args;
|
|
930
949
|
if (typeof a.project !== "string")
|
|
931
950
|
return false;
|
|
932
|
-
if (typeof a.event_type !== "string"
|
|
951
|
+
if (typeof a.event_type !== "string" ||
|
|
952
|
+
(a.event_type !== "correction" &&
|
|
953
|
+
a.event_type !== "success" &&
|
|
954
|
+
a.event_type !== "failure" &&
|
|
955
|
+
a.event_type !== "learning"))
|
|
933
956
|
return false;
|
|
934
957
|
if (typeof a.context !== "string")
|
|
935
958
|
return false;
|
|
@@ -977,10 +1000,12 @@ export const KNOWLEDGE_DOWNVOTE_TOOL = {
|
|
|
977
1000
|
},
|
|
978
1001
|
};
|
|
979
1002
|
export function isKnowledgeVoteArgs(args) {
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1003
|
+
if (typeof args !== "object" || args === null)
|
|
1004
|
+
return false;
|
|
1005
|
+
const a = args;
|
|
1006
|
+
if (typeof a.id !== "string")
|
|
1007
|
+
return false;
|
|
1008
|
+
return true;
|
|
984
1009
|
}
|
|
985
1010
|
// ─── v4.2: Knowledge Sync Rules Tool ─────────────────────────
|
|
986
1011
|
export const KNOWLEDGE_SYNC_RULES_TOOL = {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tavily API Client
|
|
3
|
+
*
|
|
4
|
+
* This module provides Tavily Search and Extract helpers for the Web Scholar
|
|
5
|
+
* pipeline. It serves as an additive alternative to Brave Search + Firecrawl
|
|
6
|
+
* when TAVILY_API_KEY is set.
|
|
7
|
+
*
|
|
8
|
+
* 1. performTavilySearch — Web search returning URLs (mirrors Brave web search)
|
|
9
|
+
* 2. performTavilyExtract — URL content extraction returning markdown (mirrors Firecrawl scrape)
|
|
10
|
+
*/
|
|
11
|
+
import { tavily } from "@tavily/core";
|
|
12
|
+
function getClient(apiKey) {
|
|
13
|
+
return tavily({ apiKey });
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Searches the web via Tavily and returns an array of result objects.
|
|
17
|
+
*/
|
|
18
|
+
export async function performTavilySearch(apiKey, query, maxResults = 10) {
|
|
19
|
+
try {
|
|
20
|
+
const client = getClient(apiKey);
|
|
21
|
+
const response = await client.search(query, {
|
|
22
|
+
maxResults,
|
|
23
|
+
searchDepth: "advanced",
|
|
24
|
+
topic: "general",
|
|
25
|
+
});
|
|
26
|
+
return (response.results || []).map((r) => ({
|
|
27
|
+
title: r.title || "",
|
|
28
|
+
url: r.url || "",
|
|
29
|
+
content: r.content || "",
|
|
30
|
+
score: r.score ?? 0,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
console.error(`[Tavily Search] Error performing search for query "${query}":`, error);
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extracts article content from URLs via Tavily Extract.
|
|
40
|
+
* Returns markdown content for each successfully extracted URL.
|
|
41
|
+
*/
|
|
42
|
+
export async function performTavilyExtract(apiKey, urls) {
|
|
43
|
+
if (urls.length === 0)
|
|
44
|
+
return [];
|
|
45
|
+
const client = getClient(apiKey);
|
|
46
|
+
const allResults = [];
|
|
47
|
+
// Tavily extract accepts up to 20 URLs at once
|
|
48
|
+
for (let i = 0; i < urls.length; i += 20) {
|
|
49
|
+
const batch = urls.slice(i, i + 20);
|
|
50
|
+
try {
|
|
51
|
+
const response = await client.extract(batch, {
|
|
52
|
+
extractDepth: "basic",
|
|
53
|
+
});
|
|
54
|
+
// Optionally log failed URLs from this batch
|
|
55
|
+
if (response.failedResults && response.failedResults.length > 0) {
|
|
56
|
+
console.warn(`[Tavily Extract] Failed to extract ${response.failedResults.length} URLs in this batch.`, response.failedResults);
|
|
57
|
+
}
|
|
58
|
+
const mapped = (response.results || []).map((r) => ({
|
|
59
|
+
url: r.url || "",
|
|
60
|
+
rawContent: r.rawContent || "",
|
|
61
|
+
}));
|
|
62
|
+
allResults.push(...mapped);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// Log the error but continue to the next batch to prevent total data loss
|
|
66
|
+
console.error(`[Tavily Extract] Error extracting batch ${i} to ${Math.min(i + 20, urls.length)}:`, error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return allResults;
|
|
70
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prism-mcp-server",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.9",
|
|
4
4
|
"mcpName": "io.github.dcostenco/prism-mcp",
|
|
5
5
|
"description": "The Mind Palace for AI Agents — persistent memory (SQLite/Supabase), behavioral learning & IDE rules sync, multimodal VLM image captioning, pluggable LLM providers (OpenAI/Anthropic/Gemini/Ollama), OpenTelemetry distributed tracing, GDPR export, multi-agent Hivemind sync, time travel, visual Mind Palace dashboard. Zero-config local mode.",
|
|
6
6
|
"module": "index.ts",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@anthropic-ai/sdk": "^0.80.0",
|
|
90
|
+
"@tavily/core": "^0.6.0",
|
|
90
91
|
"@google-cloud/discoveryengine": "^2.5.3",
|
|
91
92
|
"@google/generative-ai": "^0.24.1",
|
|
92
93
|
"@libsql/client": "^0.17.2",
|