prism-mcp-server 7.6.0 → 7.7.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.
@@ -20,6 +20,8 @@ import * as http from "http";
20
20
  import * as path from "path";
21
21
  import * as os from "os";
22
22
  import * as fs from "fs";
23
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
24
+ import { createServer, getAllPossibleTools } from "../server.js";
23
25
  import { getStorage } from "../storage/index.js";
24
26
  import { PRISM_USER_ID, SERVER_CONFIG } from "../config.js";
25
27
  import { renderDashboardHTML } from "./ui.js";
@@ -89,6 +91,8 @@ export async function startDashboardServer() {
89
91
  maxAttempts: 5,
90
92
  windowMs: 60 * 1000,
91
93
  });
94
+ // Track active SSE transports for MCP
95
+ const activeSSETransports = new Map();
92
96
  /** Render a styled login page matching the Mind Palace theme */
93
97
  function renderLoginPage() {
94
98
  return `<!DOCTYPE html>
@@ -201,10 +205,106 @@ return false;}
201
205
  });
202
206
  return res.end(JSON.stringify({ ok: true }));
203
207
  }
208
+ // ─── API: Smithery MCP Server Card (v7.4) ───
209
+ // Smithery explicitly requests this file to skip slow stdio scanning.
210
+ // MUST BE PLACED HERE (above Auth Gate) so it is a fully public manifest.
211
+ if (reqUrl.pathname === "/.well-known/mcp/server-card.json" && req.method === "GET") {
212
+ try {
213
+ const tools = getAllPossibleTools();
214
+ res.writeHead(200, {
215
+ "Content-Type": "application/json",
216
+ "Access-Control-Allow-Origin": "*",
217
+ "Cache-Control": "public, max-age=3600"
218
+ });
219
+ return res.end(JSON.stringify({
220
+ $schema: "https://smithery.ai/schema/server-card.json",
221
+ serverInfo: {
222
+ name: SERVER_CONFIG.name,
223
+ version: SERVER_CONFIG.version,
224
+ },
225
+ authentication: {
226
+ required: false
227
+ },
228
+ configSchema: {
229
+ type: "object",
230
+ required: [],
231
+ properties: {
232
+ braveApiKey: {
233
+ type: "string",
234
+ title: "Brave Search API Key",
235
+ description: "API key for Brave Search (powers web search and research tools). Get one at https://brave.com/search/api/"
236
+ },
237
+ googleApiKey: {
238
+ type: "string",
239
+ title: "Google AI API Key",
240
+ description: "API key for Google AI Studio / Gemini (powers synthesis, embeddings, paper analysis). Get one at https://aistudio.google.com/apikey"
241
+ },
242
+ storage: {
243
+ type: "string",
244
+ title: "Storage Backend",
245
+ description: "Storage backend: 'local' (SQLite, zero-config) or 'supabase' (PostgreSQL, multi-device)",
246
+ default: "local",
247
+ enum: ["local", "supabase"]
248
+ },
249
+ supabaseUrl: {
250
+ type: "string",
251
+ title: "Supabase URL",
252
+ description: "Your Supabase project URL (required if storage is 'supabase')"
253
+ },
254
+ supabaseServiceKey: {
255
+ type: "string",
256
+ title: "Supabase Service Key",
257
+ description: "Your Supabase service role key (required if storage is 'supabase')"
258
+ },
259
+ firecrawlApiKey: {
260
+ type: "string",
261
+ title: "Firecrawl API Key",
262
+ description: "Optional: API key for Firecrawl (enables Web Scholar pipeline). Get one at https://www.firecrawl.dev/"
263
+ },
264
+ braveAnswersApiKey: {
265
+ type: "string",
266
+ title: "Brave Answers API Key",
267
+ description: "Optional: Separate key for AI-grounded answers (brave_answers tool)"
268
+ },
269
+ prismEnableHivemind: {
270
+ type: "boolean",
271
+ title: "Enable Multi-Agent Hivemind",
272
+ description: "Set to true to enable multi-agent coordination tools.",
273
+ default: false
274
+ },
275
+ prismDarkFactoryEnabled: {
276
+ type: "boolean",
277
+ title: "Enable Dark Factory",
278
+ description: "Set to true to enable autonomous evaluation pipelines.",
279
+ default: false
280
+ },
281
+ prismTaskRouterEnabled: {
282
+ type: "boolean",
283
+ title: "Enable Task Router",
284
+ description: "Set to true to allow Prism to route tasks to local Ollama agents.",
285
+ default: false
286
+ }
287
+ }
288
+ },
289
+ tools: tools.map(t => ({
290
+ name: t.name,
291
+ description: t.description,
292
+ inputSchema: t.inputSchema
293
+ })),
294
+ prompts: [],
295
+ resources: []
296
+ }));
297
+ }
298
+ catch (err) {
299
+ console.error("[Dashboard] Error generating server card:", err);
300
+ res.writeHead(500, { "Content-Type": "application/json" });
301
+ return res.end(JSON.stringify({ error: err.message || "Failed to generate server card" }));
302
+ }
303
+ }
204
304
  // ─── v5.1: Auth gate — block unauthenticated requests ───
205
305
  if (AUTH_ENABLED && !isAuthenticated(req, authConfig)) {
206
306
  // For API calls, return 401 JSON
207
- if (reqUrl.pathname.startsWith("/api/")) {
307
+ if (reqUrl.pathname.startsWith("/api/") || reqUrl.pathname === "/sse" || reqUrl.pathname === "/messages") {
208
308
  res.writeHead(401, { "Content-Type": "application/json" });
209
309
  return res.end(JSON.stringify({ error: "Authentication required" }));
210
310
  }
@@ -214,6 +314,39 @@ return false;}
214
314
  }
215
315
  try {
216
316
  const url = new URL(req.url || "/", `http://${req.headers.host}`);
317
+ // ─── SSE: MCP Transport Endpoint ───
318
+ if (url.pathname === "/sse" && req.method === "GET") {
319
+ const transport = new SSEServerTransport("/messages", res);
320
+ await transport.start();
321
+ activeSSETransports.set(transport.sessionId, transport);
322
+ transport.onclose = () => {
323
+ activeSSETransports.delete(transport.sessionId);
324
+ };
325
+ try {
326
+ const mcpServer = createServer();
327
+ await mcpServer.connect(transport);
328
+ }
329
+ catch (err) {
330
+ console.error("[Dashboard] SSE Connection failed:", err);
331
+ activeSSETransports.delete(transport.sessionId);
332
+ }
333
+ return; // SSEServerTransport handles keeping the response open
334
+ }
335
+ // ─── SSE: MCP Message Receiver ───
336
+ if (url.pathname === "/messages" && req.method === "POST") {
337
+ const sessionId = url.searchParams.get("sessionId");
338
+ if (!sessionId) {
339
+ res.writeHead(400, { "Content-Type": "text/plain" });
340
+ return res.end("Missing sessionId");
341
+ }
342
+ const transport = activeSSETransports.get(sessionId);
343
+ if (!transport) {
344
+ res.writeHead(404, { "Content-Type": "text/plain" });
345
+ return res.end("Session not found");
346
+ }
347
+ await transport.handlePostMessage(req, res);
348
+ return;
349
+ }
217
350
  // ─── Serve the Dashboard UI ───
218
351
  if (url.pathname === "/" || url.pathname === "/index.html") {
219
352
  res.writeHead(200, {
package/dist/server.js CHANGED
@@ -254,7 +254,18 @@ export function notifyResourceUpdate(project, server) {
254
254
  * - resources: memory://{project}/handoff — paperclip-attachable session state
255
255
  * with subscribe support for live refresh
256
256
  */
257
- export function createServer() {
257
+ export function getAllPossibleTools() {
258
+ return [
259
+ ...BASE_TOOLS,
260
+ ...buildSessionMemoryTools([]),
261
+ ...AGENT_REGISTRY_TOOLS,
262
+ SESSION_TASK_ROUTE_TOOL,
263
+ SESSION_START_PIPELINE_TOOL,
264
+ SESSION_CHECK_PIPELINE_STATUS_TOOL,
265
+ SESSION_ABORT_PIPELINE_TOOL
266
+ ];
267
+ }
268
+ export function getAvailableTools() {
258
269
  // ─── v4.1 FIX: Auto-Load via Dynamic Tool Descriptions ────────
259
270
  // Read auto-load projects EXCLUSIVELY from dashboard config
260
271
  // (available after initConfigStorage() in startServer).
@@ -273,21 +284,17 @@ export function createServer() {
273
284
  if (autoloadList.length > 0) {
274
285
  console.error(`[Prism] Auto-load projects (dashboard): ${autoloadList.join(', ')}`);
275
286
  }
276
- // Build the dynamic tool list with auto-load instruction injected
277
287
  const SESSION_MEMORY_TOOLS = buildSessionMemoryTools(autoloadList);
278
- // Combine: always list ALL tools so scanners (Glama, Smithery, MCP Registry)
279
- // can enumerate the full capability set. Runtime guards in the CallTool handler
280
- // still prevent execution without valid Supabase credentials.
281
- const ALL_TOOLS = [
288
+ return [
282
289
  ...BASE_TOOLS,
283
290
  ...SESSION_MEMORY_TOOLS,
284
- // v3.0: Agent Hivemind tools — only when PRISM_ENABLE_HIVEMIND=true
285
291
  ...(PRISM_ENABLE_HIVEMIND ? AGENT_REGISTRY_TOOLS : []),
286
- // v7.1: Task Router tool — only when PRISM_TASK_ROUTER_ENABLED=true
287
292
  ...(getSettingSync("task_router_enabled", String(PRISM_TASK_ROUTER_ENABLED_ENV)) === "true" ? [SESSION_TASK_ROUTE_TOOL] : []),
288
- // v7.3: Dark Factory pipeline tools — only when PRISM_DARK_FACTORY_ENABLED=true
289
293
  ...(PRISM_DARK_FACTORY_ENABLED ? [SESSION_START_PIPELINE_TOOL, SESSION_CHECK_PIPELINE_STATUS_TOOL, SESSION_ABORT_PIPELINE_TOOL] : []),
290
294
  ];
295
+ }
296
+ export function createServer() {
297
+ const ALL_TOOLS = getAvailableTools();
291
298
  const server = new Server({
292
299
  name: SERVER_CONFIG.name,
293
300
  version: SERVER_CONFIG.version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "7.6.0",
3
+ "version": "7.7.1",
4
4
  "mcpName": "io.github.dcostenco/prism-mcp",
5
5
  "description": "The Mind Palace for AI Agents — adversarial evaluation (PLAN_CONTRACT→EVALUATE anti-sycophancy), fail-closed Dark Factory autonomous pipelines (3-gate parse→type→scope), persistent memory (SQLite/Supabase), ACT-R cognitive retrieval, behavioral learning & IDE rules sync, multi-agent Hivemind, time travel, visual dashboard. Zero-config local mode.",
6
6
  "module": "index.ts",