prism-mcp-server 4.2.0 → 4.6.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.
@@ -0,0 +1,174 @@
1
+ /**
2
+ * OpenTelemetry Telemetry Initializer (v4.6.0)
3
+ * ─────────────────────────────────────────────────────────────────────────────
4
+ * PURPOSE:
5
+ * Singleton NodeTracerProvider that wires Prism's instrumentation to an OTLP
6
+ * collector (Jaeger, Zipkin, Grafana Tempo, Datadog, etc.).
7
+ *
8
+ * DESIGN PRINCIPLES:
9
+ *
10
+ * 1. DASHBOARD-DRIVEN CONFIG (no .env required)
11
+ * All three OTel settings are read from configStorage — the same mechanism
12
+ * used for AI provider keys and storage backends. Users toggle OTel from
13
+ * the Dashboard "Observability" tab.
14
+ * otel_enabled → "true" | "false" (default: "false")
15
+ * otel_endpoint → e.g. "http://localhost:4318/v1/traces"
16
+ * otel_service_name → e.g. "prism-mcp-server"
17
+ *
18
+ * 2. GRACEFUL NO-OP (zero guards in caller code)
19
+ * When otel_enabled is false, getTracer() returns the SDK's built-in no-op
20
+ * tracer — every span call becomes a zero-cost no-op. No `if (otelEnabled)`
21
+ * guards are needed in any instrumented file.
22
+ *
23
+ * 3. RESTART REQUIRED FOR CHANGES
24
+ * initTelemetry() runs once at startup after initConfigStorage() has
25
+ * populated the settings cache. Toggling OTel requires a server restart
26
+ * (same behavior as changing PRISM_STORAGE). The Dashboard Observability
27
+ * tab shows a restart banner when these settings change.
28
+ *
29
+ * 4. SHUTDOWN FLUSH (Data integrity on SIGTERM/disconnect)
30
+ * BatchSpanProcessor holds spans in memory for up to 5 seconds before
31
+ * exporting. A SIGTERM or MCP client disconnect would silently lose
32
+ * buffered spans without explicit flushing. shutdownTelemetry() is called
33
+ * FIRST in lifecycle.ts shutdown() — before any DB connections close —
34
+ * so spans referencing DB operations are exported while context is intact.
35
+ *
36
+ * 5. BRIDGE TO EXISTING TRACING (src/utils/tracing.ts)
37
+ * The Phase-1 MemoryTrace system (per-search explainability, content[1])
38
+ * is orthogonal to OTel. Both coexist. OTel answers "latency waterfall";
39
+ * MemoryTrace answers "why was this memory returned?". Exactly as
40
+ * tracing.ts lines 26-29 predicted: "OTel integration layers on top in
41
+ * a follow-up without any code changes to the MemoryTrace types."
42
+ *
43
+ * STARTUP (server.ts startServer()):
44
+ * await initConfigStorage(); // warm the settings cache first
45
+ * initTelemetry(); // synchronous read from warm cache
46
+ * const server = createServer();
47
+ *
48
+ * SHUTDOWN (lifecycle.ts shutdown()):
49
+ * await shutdownTelemetry(); // flush BatchSpanProcessor buffer FIRST
50
+ * closeConfigStorage(); // then close DB connections
51
+ *
52
+ * API:
53
+ * initTelemetry() — call once at startup, idempotent
54
+ * getTracer() — returns the active tracer (or no-op if disabled)
55
+ * shutdownTelemetry() — flushes and shuts down the provider
56
+ * ─────────────────────────────────────────────────────────────────────────────
57
+ */
58
+ import { trace } from "@opentelemetry/api";
59
+ import { NodeTracerProvider, BatchSpanProcessor, } from "@opentelemetry/sdk-trace-node";
60
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
61
+ import { resourceFromAttributes } from "@opentelemetry/resources";
62
+ import { SEMRESATTRS_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
63
+ import { getSettingSync } from "../storage/configStorage.js";
64
+ // ─── Module-level singleton ───────────────────────────────────────────────────
65
+ // Null when OTel is disabled or before initTelemetry() is called.
66
+ let _provider = null;
67
+ // Tracer name — appears in every span's instrumentation.name field in the UI.
68
+ const TRACER_NAME = "prism-mcp";
69
+ // ─────────────────────────────────────────────────────────────────────────────
70
+ // initTelemetry()
71
+ // ─────────────────────────────────────────────────────────────────────────────
72
+ /**
73
+ * Initialize the OpenTelemetry SDK.
74
+ *
75
+ * Must be called after initConfigStorage() so the settings cache is warm.
76
+ * Idempotent — calling more than once is a safe no-op.
77
+ * Errors are caught and logged; the server always continues normally.
78
+ */
79
+ export function initTelemetry() {
80
+ if (_provider)
81
+ return; // Already initialized
82
+ const enabled = getSettingSync("otel_enabled", "false");
83
+ const endpoint = getSettingSync("otel_endpoint", "http://localhost:4318/v1/traces");
84
+ const serviceName = getSettingSync("otel_service_name", "prism-mcp-server");
85
+ if (enabled !== "true") {
86
+ // OTel is disabled. getTracer() will return the SDK global no-op tracer.
87
+ // No NodeTracerProvider is created; no OTLP connections are attempted.
88
+ return;
89
+ }
90
+ try {
91
+ // REVIEWER NOTE: resourceFromAttributes() is the v2 API for Resource.
92
+ // SEMRESATTRS_SERVICE_NAME resolves to the stable "service.name" string
93
+ // which is the primary label in Jaeger/Zipkin/Grafana Tempo trace UIs.
94
+ const resource = resourceFromAttributes({
95
+ [SEMRESATTRS_SERVICE_NAME]: serviceName,
96
+ });
97
+ // REVIEWER NOTE: BatchSpanProcessor is mandatory for production.
98
+ // SimpleSpanProcessor (sync, one-by-one) would block the Node.js event
99
+ // loop during each 500ms OTLP export — freezing MCP request handling.
100
+ // BatchSpanProcessor runs on a timer in the background.
101
+ const exporter = new OTLPTraceExporter({ url: endpoint });
102
+ const processor = new BatchSpanProcessor(exporter, {
103
+ // Flush at most 512 spans per batch — prevents memory blow-up when the
104
+ // collector is temporarily unreachable (backpressure safety).
105
+ maxExportBatchSize: 512,
106
+ // 10s timeout per export batch — balances reliability vs shutdown delay.
107
+ exportTimeoutMillis: 10_000,
108
+ // Export every 5 seconds — good balance of latency visibility vs I/O.
109
+ scheduledDelayMillis: 5_000,
110
+ });
111
+ // REVIEWER NOTE: v2 API — spanProcessors passed in constructor config,
112
+ // not via addSpanProcessor(). resource + spanProcessors are both config
113
+ // properties. The provider must be .register()'d to become the global
114
+ // tracer provider, enabling AsyncLocalStorage context propagation.
115
+ _provider = new NodeTracerProvider({
116
+ resource,
117
+ spanProcessors: [processor],
118
+ });
119
+ _provider.register();
120
+ console.error(`[Telemetry] OpenTelemetry initialized. ` +
121
+ `Service: "${serviceName}", Endpoint: "${endpoint}"`);
122
+ }
123
+ catch (err) {
124
+ // OTel init errors must NEVER crash the server. Log and continue.
125
+ console.error(`[Telemetry] Failed to initialize OTel (non-fatal): ${err}`);
126
+ _provider = null; // Clean state so getTracer() returns no-op
127
+ }
128
+ }
129
+ // ─────────────────────────────────────────────────────────────────────────────
130
+ // getTracer()
131
+ // ─────────────────────────────────────────────────────────────────────────────
132
+ /**
133
+ * Returns the active OTel Tracer.
134
+ *
135
+ * When _provider is null (OTel disabled or init failed), trace.getTracer()
136
+ * returns the SDK's built-in no-op tracer — every span call is zero-cost.
137
+ * Callers never need `if (otelEnabled)` guards.
138
+ *
139
+ * @example
140
+ * const span = getTracer().startSpan("mcp.call_tool", { attributes: { "tool.name": name } });
141
+ * span.end();
142
+ */
143
+ export function getTracer() {
144
+ return trace.getTracer(TRACER_NAME);
145
+ }
146
+ // ─────────────────────────────────────────────────────────────────────────────
147
+ // shutdownTelemetry()
148
+ // ─────────────────────────────────────────────────────────────────────────────
149
+ /**
150
+ * Flush all buffered spans and shut down the NodeTracerProvider.
151
+ *
152
+ * CRITICAL ORDERING — Must be called BEFORE closing any DB connections.
153
+ * BatchSpanProcessor holds spans that may reference DB operations. If the DB
154
+ * closes first, those spans' context becomes incomplete in the trace UI.
155
+ *
156
+ * In lifecycle.ts registerShutdownHandlers():
157
+ * await shutdownTelemetry(); // ← step 0: flush spans FIRST
158
+ * closeConfigStorage(); // ← step 1: close config DB
159
+ * await storage.close(); // ← step 2: close ledger DB
160
+ *
161
+ * When OTel is disabled (_provider === null), this resolves immediately.
162
+ */
163
+ export async function shutdownTelemetry() {
164
+ if (!_provider)
165
+ return;
166
+ try {
167
+ await _provider.shutdown();
168
+ console.error("[Telemetry] Flushed remaining spans and shut down.");
169
+ }
170
+ catch (err) {
171
+ // Log but don't rethrow — shutdown errors must not prevent DB cleanup.
172
+ console.error(`[Telemetry] Error during shutdown (non-fatal): ${err}`);
173
+ }
174
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "prism-mcp-server",
3
- "version": "4.2.0",
3
+ "version": "4.6.0",
4
4
  "mcpName": "io.github.dcostenco/prism-mcp",
5
- "description": "The Mind Palace for AI Agents local-first MCP server with persistent memory (SQLite/Supabase), visual dashboard, time travel, multi-agent sync, Morning Briefings, reality drift detection, code mode templates, semantic vector search, and Brave Search + Gemini analysis. Zero-config local mode.",
5
+ "description": "The Mind Palace for AI Agents \u2014 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",
7
7
  "type": "module",
8
8
  "main": "dist/server.js",
@@ -81,13 +81,20 @@
81
81
  "typescript": "^5.0.0"
82
82
  },
83
83
  "dependencies": {
84
+ "@anthropic-ai/sdk": "^0.80.0",
84
85
  "@google-cloud/discoveryengine": "^2.5.3",
85
86
  "@google/generative-ai": "^0.24.1",
86
87
  "@libsql/client": "^0.17.2",
87
88
  "@modelcontextprotocol/sdk": "^1.27.1",
89
+ "@opentelemetry/api": "^1.9.1",
90
+ "@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
91
+ "@opentelemetry/resources": "^2.6.1",
92
+ "@opentelemetry/sdk-trace-node": "^2.6.1",
93
+ "@opentelemetry/semantic-conventions": "^1.40.0",
88
94
  "@supabase/supabase-js": "^2.99.3",
89
95
  "dotenv": "^16.5.0",
90
96
  "fflate": "^0.8.2",
97
+ "openai": "^6.32.0",
91
98
  "quickjs-emscripten": "^0.32.0"
92
99
  }
93
100
  }