@productbrain/mcp 0.0.1-beta.191 → 0.0.1-beta.1912

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,1052 @@
1
+ // src/analytics.ts
2
+ import { userInfo } from "os";
3
+ import { PostHog } from "posthog-node";
4
+ var client = null;
5
+ var distinctId = "anonymous";
6
+ var POSTHOG_HOST = "https://eu.i.posthog.com";
7
+ function log(msg) {
8
+ if (process.env.MCP_DEBUG === "1") {
9
+ process.stderr.write(msg);
10
+ }
11
+ }
12
+ function getBuildTimeKey() {
13
+ try {
14
+ return "";
15
+ } catch {
16
+ return "";
17
+ }
18
+ }
19
+ function initAnalytics() {
20
+ const apiKey = process.env.POSTHOG_MCP_KEY || getBuildTimeKey();
21
+ if (!apiKey) {
22
+ log("[MCP-ANALYTICS] No PostHog key \u2014 tracking disabled (set SYNERGYOS_POSTHOG_KEY at build time for publish)\n");
23
+ return;
24
+ }
25
+ client = new PostHog(apiKey, {
26
+ host: POSTHOG_HOST,
27
+ flushAt: 1,
28
+ flushInterval: 5e3,
29
+ featureFlagsPollingInterval: 3e4
30
+ });
31
+ distinctId = process.env.MCP_USER_ID || fallbackDistinctId();
32
+ log(`[MCP-ANALYTICS] Initialized \u2014 host=${POSTHOG_HOST} distinctId=${distinctId}
33
+ `);
34
+ }
35
+ function fallbackDistinctId() {
36
+ try {
37
+ return userInfo().username;
38
+ } catch {
39
+ return `os-${process.pid}`;
40
+ }
41
+ }
42
+ function trackSessionStarted(workspaceId, serverVersion) {
43
+ if (!client) return;
44
+ client.capture({
45
+ distinctId,
46
+ event: "mcp_session_started",
47
+ properties: {
48
+ workspace_id: workspaceId,
49
+ server_version: serverVersion,
50
+ source: "mcp-server",
51
+ $groups: { workspace: workspaceId }
52
+ }
53
+ });
54
+ }
55
+ function trackToolCall(fn, status, durationMs, workspaceId, errorMsg) {
56
+ const properties = {
57
+ tool: fn,
58
+ status,
59
+ duration_ms: durationMs,
60
+ workspace_id: workspaceId,
61
+ source: "mcp-server",
62
+ $groups: { workspace: workspaceId }
63
+ };
64
+ if (errorMsg) properties.error = errorMsg;
65
+ if (!client) return;
66
+ client.capture({
67
+ distinctId,
68
+ event: "mcp_tool_called",
69
+ properties
70
+ });
71
+ }
72
+ function trackSetupStarted() {
73
+ if (!client) return;
74
+ client.capture({
75
+ distinctId,
76
+ event: "mcp_setup_started",
77
+ properties: {
78
+ source: "mcp-server",
79
+ platform: process.platform
80
+ }
81
+ });
82
+ }
83
+ function trackSetupCompleted(chosenClient, outcome) {
84
+ if (!client) return;
85
+ client.capture({
86
+ distinctId,
87
+ event: "mcp_setup_completed",
88
+ properties: {
89
+ client: chosenClient,
90
+ outcome,
91
+ source: "mcp-server",
92
+ platform: process.platform
93
+ }
94
+ });
95
+ }
96
+ function trackQualityVerdict(workspaceId, props) {
97
+ if (!client) return;
98
+ client.capture({
99
+ distinctId,
100
+ event: "quality_verdict_generated",
101
+ properties: {
102
+ ...props,
103
+ workspace_id: workspaceId,
104
+ source_system: "mcp-server",
105
+ $groups: { workspace: workspaceId }
106
+ }
107
+ });
108
+ }
109
+ function trackQualityCheck(workspaceId, props) {
110
+ if (!client) return;
111
+ client.capture({
112
+ distinctId,
113
+ event: "quality_verdict_checked",
114
+ properties: {
115
+ ...props,
116
+ workspace_id: workspaceId,
117
+ source_system: "mcp-server",
118
+ $groups: { workspace: workspaceId }
119
+ }
120
+ });
121
+ }
122
+ function trackCaptureClassifierEvent(event, workspaceId, props) {
123
+ if (!client) return;
124
+ try {
125
+ client.capture({
126
+ distinctId,
127
+ event,
128
+ properties: {
129
+ ...props,
130
+ workspace_id: workspaceId,
131
+ source_system: "mcp-server",
132
+ $groups: { workspace: workspaceId }
133
+ }
134
+ });
135
+ } catch {
136
+ }
137
+ }
138
+ function trackCaptureClassifierEvaluated(workspaceId, props) {
139
+ trackCaptureClassifierEvent("mcp_capture_classifier_evaluated", workspaceId, props);
140
+ }
141
+ function trackCaptureClassifierAutoRouted(workspaceId, props) {
142
+ trackCaptureClassifierEvent("mcp_capture_classifier_auto_routed", workspaceId, props);
143
+ }
144
+ function trackCaptureClassifierFallback(workspaceId, props) {
145
+ trackCaptureClassifierEvent("mcp_capture_classifier_fallback", workspaceId, props);
146
+ }
147
+ function trackChainEntryCommitted(workspaceId, props) {
148
+ if (!client) return;
149
+ try {
150
+ client.capture({
151
+ distinctId,
152
+ event: "chain_entry_committed",
153
+ properties: {
154
+ workspace_id: workspaceId,
155
+ source_system: "mcp-server",
156
+ $groups: { workspace: workspaceId },
157
+ ...props
158
+ }
159
+ });
160
+ } catch {
161
+ }
162
+ }
163
+ function trackKnowledgeGap(workspaceId, props) {
164
+ if (!client) return;
165
+ try {
166
+ client.capture({
167
+ distinctId,
168
+ event: "knowledge_gap_detected",
169
+ properties: {
170
+ ...props,
171
+ query: props.query.slice(0, 200),
172
+ workspace_id: workspaceId,
173
+ source_system: "mcp-server",
174
+ $groups: { workspace: workspaceId }
175
+ }
176
+ });
177
+ } catch {
178
+ }
179
+ }
180
+ function trackCaptureQualityHints(workspaceId, props) {
181
+ if (!client) return;
182
+ try {
183
+ client.capture({
184
+ distinctId,
185
+ event: "mcp_capture_quality_hints",
186
+ properties: {
187
+ ...props,
188
+ workspace_id: workspaceId,
189
+ source_system: "mcp-server",
190
+ $groups: { workspace: workspaceId }
191
+ }
192
+ });
193
+ } catch {
194
+ }
195
+ }
196
+ function trackCaptureRelationSuggestions(workspaceId, props) {
197
+ if (!client) return;
198
+ try {
199
+ client.capture({
200
+ distinctId,
201
+ event: "mcp_capture_relation_suggestions",
202
+ properties: {
203
+ ...props,
204
+ workspace_id: workspaceId,
205
+ source_system: "mcp-server",
206
+ $groups: { workspace: workspaceId }
207
+ }
208
+ });
209
+ } catch {
210
+ }
211
+ }
212
+ function trackCollectionClassified(workspaceId, props) {
213
+ if (!client) return;
214
+ try {
215
+ client.capture({
216
+ distinctId,
217
+ event: "collection_classified",
218
+ properties: {
219
+ ...props,
220
+ workspace_id: workspaceId,
221
+ source_system: "mcp-server",
222
+ $groups: { workspace: workspaceId }
223
+ }
224
+ });
225
+ } catch {
226
+ }
227
+ }
228
+ function trackFieldGuidanceApplied(workspaceId, props) {
229
+ if (!client) return;
230
+ try {
231
+ client.capture({
232
+ distinctId,
233
+ event: "field_guidance_applied",
234
+ properties: {
235
+ ...props,
236
+ workspace_id: workspaceId,
237
+ source_system: "mcp-server",
238
+ $groups: { workspace: workspaceId }
239
+ }
240
+ });
241
+ } catch {
242
+ }
243
+ }
244
+ function trackFieldQualityWarning(workspaceId, props) {
245
+ if (!client) return;
246
+ try {
247
+ client.capture({
248
+ distinctId,
249
+ event: "field_quality_warning",
250
+ properties: {
251
+ ...props,
252
+ workspace_id: workspaceId,
253
+ source_system: "mcp-server",
254
+ $groups: { workspace: workspaceId }
255
+ }
256
+ });
257
+ } catch {
258
+ }
259
+ }
260
+ function trackSessionCaptureRate(workspaceId, props) {
261
+ if (!client) return;
262
+ try {
263
+ client.capture({
264
+ distinctId,
265
+ event: "session_capture_rate",
266
+ properties: {
267
+ ...props,
268
+ workspace_id: workspaceId,
269
+ source_system: "mcp-server",
270
+ $groups: { workspace: workspaceId }
271
+ }
272
+ });
273
+ } catch {
274
+ }
275
+ }
276
+ function trackZeroCaptureAuditFired(workspaceId, props) {
277
+ if (!client) return;
278
+ try {
279
+ client.capture({
280
+ distinctId,
281
+ event: "zero_capture_audit_fired",
282
+ properties: {
283
+ ...props,
284
+ workspace_id: workspaceId,
285
+ source_system: "mcp-server",
286
+ $groups: { workspace: workspaceId }
287
+ }
288
+ });
289
+ } catch {
290
+ }
291
+ }
292
+ function trackCaptureContractMiss(workspaceId, props) {
293
+ if (!client) return;
294
+ try {
295
+ client.capture({
296
+ distinctId,
297
+ event: "capture_contract_miss",
298
+ properties: {
299
+ ...props,
300
+ workspace_id: workspaceId,
301
+ source_system: "mcp-server",
302
+ $groups: { workspace: workspaceId }
303
+ }
304
+ });
305
+ } catch {
306
+ }
307
+ }
308
+ function trackWriteBackHintServed(workspaceId, props) {
309
+ if (!client) return;
310
+ try {
311
+ client.capture({
312
+ distinctId,
313
+ event: "write_back_hint_served",
314
+ properties: {
315
+ ...props,
316
+ workspace_id: workspaceId,
317
+ source_system: "mcp-server",
318
+ $groups: { workspace: workspaceId }
319
+ }
320
+ });
321
+ } catch {
322
+ }
323
+ }
324
+ function trackCommitErrorByCode(workspaceId, props) {
325
+ if (!client) return;
326
+ try {
327
+ client.capture({
328
+ distinctId,
329
+ event: "commit_error_by_code",
330
+ properties: {
331
+ ...props,
332
+ workspace_id: workspaceId,
333
+ source_system: "mcp-server",
334
+ $groups: { workspace: workspaceId }
335
+ }
336
+ });
337
+ } catch {
338
+ }
339
+ }
340
+ function trackClassifierDivergence(workspaceId, props) {
341
+ if (!client) return;
342
+ try {
343
+ client.capture({
344
+ distinctId,
345
+ event: "classifier_divergence",
346
+ properties: {
347
+ ...props,
348
+ workspace_id: workspaceId,
349
+ source_system: "mcp-server",
350
+ $groups: { workspace: workspaceId }
351
+ }
352
+ });
353
+ } catch {
354
+ }
355
+ }
356
+ function getPostHogClient() {
357
+ return client;
358
+ }
359
+ async function shutdownAnalytics() {
360
+ await client?.shutdown();
361
+ }
362
+
363
+ // src/auth.ts
364
+ import { AsyncLocalStorage } from "async_hooks";
365
+ import { createHash } from "crypto";
366
+ function hashKey(key) {
367
+ return createHash("sha256").update(key).digest("hex").slice(0, 16);
368
+ }
369
+ var requestStore = new AsyncLocalStorage();
370
+ function runWithAuth(auth, fn) {
371
+ return requestStore.run(auth, fn);
372
+ }
373
+ function getRequestApiKey() {
374
+ return requestStore.getStore()?.apiKey;
375
+ }
376
+ var SESSION_TTL_MS = 30 * 60 * 1e3;
377
+ var MAX_KEYS = 100;
378
+ var keyStateMap = /* @__PURE__ */ new Map();
379
+ function newKeyState() {
380
+ return {
381
+ workspaceId: null,
382
+ workspaceSlug: null,
383
+ workspaceName: null,
384
+ workspaceCreatedAt: null,
385
+ workspaceGovernanceMode: null,
386
+ agentSessionId: null,
387
+ apiKeyId: null,
388
+ apiKeyScope: "readwrite",
389
+ sessionOriented: false,
390
+ sessionClosed: false,
391
+ lastAccess: Date.now(),
392
+ deploymentUrl: null
393
+ };
394
+ }
395
+ function getKeyState(apiKey) {
396
+ let s = keyStateMap.get(apiKey);
397
+ if (!s) {
398
+ s = newKeyState();
399
+ keyStateMap.set(apiKey, s);
400
+ evictStale();
401
+ }
402
+ s.lastAccess = Date.now();
403
+ return s;
404
+ }
405
+ function evictStale() {
406
+ if (keyStateMap.size <= MAX_KEYS) return;
407
+ const now = Date.now();
408
+ for (const [key, s] of keyStateMap) {
409
+ if (now - s.lastAccess > SESSION_TTL_MS) keyStateMap.delete(key);
410
+ }
411
+ if (keyStateMap.size > MAX_KEYS) {
412
+ const sorted = [...keyStateMap.entries()].sort((a, b) => a[1].lastAccess - b[1].lastAccess);
413
+ for (let i = 0; i < sorted.length - MAX_KEYS; i++) {
414
+ keyStateMap.delete(sorted[i][0]);
415
+ }
416
+ }
417
+ }
418
+
419
+ // src/cli/config-writer.ts
420
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
421
+ import { join, dirname } from "path";
422
+ import { homedir, platform } from "os";
423
+ var SERVER_ENTRY_KEY = "Product Brain";
424
+ var LEGACY_ENTRY_KEY = "productbrain";
425
+ var MCP_NPX_PACKAGE = "@productbrain/mcp@beta";
426
+ function buildServerEntry(apiKey) {
427
+ return {
428
+ command: "npx",
429
+ args: ["-y", MCP_NPX_PACKAGE],
430
+ env: { PRODUCTBRAIN_API_KEY: apiKey }
431
+ };
432
+ }
433
+ function getCursorConfigPath() {
434
+ return join(process.cwd(), ".cursor", "mcp.json");
435
+ }
436
+ function getClaudeDesktopConfigPath() {
437
+ const os = platform();
438
+ if (os === "darwin") {
439
+ return join(
440
+ homedir(),
441
+ "Library",
442
+ "Application Support",
443
+ "Claude",
444
+ "claude_desktop_config.json"
445
+ );
446
+ }
447
+ if (os === "win32") {
448
+ const appData = process.env.APPDATA ?? join(homedir(), "AppData", "Roaming");
449
+ return join(appData, "Claude", "claude_desktop_config.json");
450
+ }
451
+ return null;
452
+ }
453
+ function resolveClient(name) {
454
+ if (name === "Cursor") {
455
+ return { name, configPath: getCursorConfigPath() };
456
+ }
457
+ const configPath = getClaudeDesktopConfigPath();
458
+ return configPath ? { name, configPath } : null;
459
+ }
460
+ function readJsonSafe(path) {
461
+ if (!existsSync(path)) return {};
462
+ try {
463
+ return JSON.parse(readFileSync(path, "utf-8"));
464
+ } catch {
465
+ return {};
466
+ }
467
+ }
468
+ async function writeClientConfig(client2, apiKey) {
469
+ const config = readJsonSafe(client2.configPath);
470
+ const serversKey = "mcpServers";
471
+ if (!config[serversKey]) config[serversKey] = {};
472
+ if (config[serversKey][LEGACY_ENTRY_KEY]) {
473
+ const legacy = config[serversKey][LEGACY_ENTRY_KEY];
474
+ config[serversKey][SERVER_ENTRY_KEY] = {
475
+ ...buildServerEntry(apiKey),
476
+ env: { ...legacy.env, PRODUCTBRAIN_API_KEY: legacy.env?.PRODUCTBRAIN_API_KEY ?? apiKey }
477
+ };
478
+ delete config[serversKey][LEGACY_ENTRY_KEY];
479
+ } else {
480
+ const existing = config[serversKey][SERVER_ENTRY_KEY];
481
+ config[serversKey][SERVER_ENTRY_KEY] = existing ? { ...existing, env: { ...existing.env, PRODUCTBRAIN_API_KEY: apiKey } } : buildServerEntry(apiKey);
482
+ }
483
+ const dir = dirname(client2.configPath);
484
+ if (!existsSync(dir)) {
485
+ mkdirSync(dir, { recursive: true });
486
+ }
487
+ writeFileSync(client2.configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
488
+ return true;
489
+ }
490
+
491
+ // src/client.ts
492
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
493
+ var toolContextStore = new AsyncLocalStorage2();
494
+ function runWithToolContext(ctx, fn) {
495
+ return toolContextStore.run(ctx, fn);
496
+ }
497
+ function getToolContext() {
498
+ return toolContextStore.getStore() ?? null;
499
+ }
500
+ var DEFAULT_CLOUD_URL = "https://gateway.productbrain.io";
501
+ var KernelCallError = class extends Error {
502
+ status;
503
+ code;
504
+ /** WP-316 S1a: Structured commit validation — required field keys missing from entry.data. */
505
+ missingRequiredFields;
506
+ /** WP-316 S1a: Structured commit validation — field-level data errors. */
507
+ fieldErrors;
508
+ /**
509
+ * WP-465 slice ⑤: structured diagnostics carried by an `ok:false` kernel envelope
510
+ * (e.g. `coherencyRefusals`, `blockers`). The gateway forwards these verbatim at
511
+ * HTTP 200; without preserving them here a refused/blocked envelope would collapse
512
+ * into a bare code+message and the caller could not surface the per-offender routes.
513
+ */
514
+ diagnostics;
515
+ constructor(message, status, code, missingRequiredFields, fieldErrors, diagnostics) {
516
+ super(message);
517
+ this.name = "KernelCallError";
518
+ this.status = status;
519
+ this.code = code;
520
+ this.missingRequiredFields = missingRequiredFields;
521
+ this.fieldErrors = fieldErrors;
522
+ this.diagnostics = diagnostics;
523
+ }
524
+ };
525
+ var CACHE_TTL_MS = 6e4;
526
+ var CACHEABLE_FNS = [
527
+ "chain.getOrientEntries",
528
+ "chain.gatherContext",
529
+ "chain.graphGatherContext",
530
+ "chain.taskAwareGatherContext",
531
+ "chain.journeyAwareGatherContext",
532
+ "chain.assembleBuildContext"
533
+ ];
534
+ function isCacheable(fn) {
535
+ return CACHEABLE_FNS.includes(fn);
536
+ }
537
+ var READ_PATTERN = /^(chain\.(get|list|search|batchGet|gather|graph|task|journey|assemble|workspace|score|absence)|chainwork\.(get|list|score)|maps\.(get|list)|gitchain\.(get|list|diff|history|runGate))/i;
538
+ function isWrite(fn) {
539
+ if (fn.startsWith("agent.")) return false;
540
+ return !READ_PATTERN.test(fn);
541
+ }
542
+ var readCache = /* @__PURE__ */ new Map();
543
+ function cacheKey(fn, args) {
544
+ return `${fn}:${JSON.stringify(args)}`;
545
+ }
546
+ function getCached(fn, args) {
547
+ if (!isCacheable(fn)) return void 0;
548
+ const key = cacheKey(fn, args);
549
+ const entry = readCache.get(key);
550
+ if (!entry || Date.now() > entry.expiresAt) {
551
+ if (entry) readCache.delete(key);
552
+ return void 0;
553
+ }
554
+ return entry.data;
555
+ }
556
+ function setCached(fn, args, data) {
557
+ if (!isCacheable(fn)) return;
558
+ const key = cacheKey(fn, args);
559
+ readCache.set(key, { data, expiresAt: Date.now() + CACHE_TTL_MS });
560
+ }
561
+ function invalidateReadCache() {
562
+ readCache.clear();
563
+ }
564
+ var _stdioState = {
565
+ workspaceId: null,
566
+ workspaceSlug: null,
567
+ workspaceName: null,
568
+ workspaceCreatedAt: null,
569
+ workspaceGovernanceMode: null,
570
+ agentSessionId: null,
571
+ apiKeyId: null,
572
+ apiKeyScope: "readwrite",
573
+ sessionOriented: false,
574
+ sessionClosed: false,
575
+ lastAccess: 0,
576
+ deploymentUrl: null
577
+ };
578
+ function state() {
579
+ const reqKey = getRequestApiKey();
580
+ if (reqKey) return getKeyState(reqKey);
581
+ return _stdioState;
582
+ }
583
+ function cacheScope() {
584
+ const key = getRequestApiKey();
585
+ return key ? hashKey(key) : "stdio";
586
+ }
587
+ function getActiveApiKey() {
588
+ const fromRequest = getRequestApiKey();
589
+ if (fromRequest) return fromRequest;
590
+ const fromEnv = process.env.PRODUCTBRAIN_API_KEY;
591
+ if (!fromEnv) throw new Error("No API key available \u2014 set PRODUCTBRAIN_API_KEY or provide Bearer token");
592
+ return fromEnv;
593
+ }
594
+ function getAgentSessionId() {
595
+ return state().agentSessionId;
596
+ }
597
+ function isSessionOriented() {
598
+ return state().sessionOriented;
599
+ }
600
+ function setSessionOriented(value) {
601
+ state().sessionOriented = value;
602
+ }
603
+ function getApiKeyScope() {
604
+ return state().apiKeyScope;
605
+ }
606
+ async function startAgentSession() {
607
+ const workspaceId = await getWorkspaceId();
608
+ const s = state();
609
+ if (!s.apiKeyId) {
610
+ throw new Error("Cannot start session: API key ID not resolved. Ensure workspace resolution completed.");
611
+ }
612
+ const result = await kernelCall("agent.startSession", {
613
+ workspaceId,
614
+ apiKeyId: s.apiKeyId,
615
+ clientKind: "mcp"
616
+ });
617
+ if (s.agentSessionId) {
618
+ resetTouchThrottle(s.agentSessionId);
619
+ }
620
+ s.agentSessionId = result.sessionId;
621
+ s.apiKeyScope = result.toolsScope;
622
+ s.sessionOriented = false;
623
+ s.sessionClosed = false;
624
+ resetTouchThrottle(result.sessionId);
625
+ return result;
626
+ }
627
+ async function closeAgentSession() {
628
+ const s = state();
629
+ if (!s.agentSessionId) return;
630
+ const sessionId = s.agentSessionId;
631
+ try {
632
+ await kernelCall("agent.closeSession", {
633
+ sessionId,
634
+ status: "closed"
635
+ });
636
+ } finally {
637
+ resetTouchThrottle(sessionId);
638
+ s.sessionClosed = true;
639
+ s.agentSessionId = null;
640
+ s.sessionOriented = false;
641
+ }
642
+ }
643
+ async function orphanAgentSession() {
644
+ const s = state();
645
+ if (!s.agentSessionId) return;
646
+ const sessionId = s.agentSessionId;
647
+ try {
648
+ await kernelCall("agent.closeSession", {
649
+ sessionId,
650
+ status: "orphaned"
651
+ });
652
+ } catch {
653
+ } finally {
654
+ resetTouchThrottle(sessionId);
655
+ s.agentSessionId = null;
656
+ s.sessionOriented = false;
657
+ }
658
+ }
659
+ var _lastTouchAtBySession = /* @__PURE__ */ new Map();
660
+ var TOUCH_THROTTLE_MS = 5e3;
661
+ function touchSessionActivity() {
662
+ const s = state();
663
+ const sessionId = s.agentSessionId;
664
+ if (!sessionId) return;
665
+ const now = Date.now();
666
+ const lastTouchAt = _lastTouchAtBySession.get(sessionId) ?? 0;
667
+ if (now - lastTouchAt < TOUCH_THROTTLE_MS) return;
668
+ _lastTouchAtBySession.set(sessionId, now);
669
+ kernelCall("agent.touchSession", {
670
+ sessionId
671
+ }).catch(() => {
672
+ });
673
+ }
674
+ function resetTouchThrottle(sessionId) {
675
+ if (sessionId) {
676
+ _lastTouchAtBySession.delete(sessionId);
677
+ return;
678
+ }
679
+ _lastTouchAtBySession.clear();
680
+ }
681
+ async function recordSessionActivity(activity) {
682
+ const s = state();
683
+ if (!s.agentSessionId) return;
684
+ try {
685
+ await kernelCall("agent.recordActivity", {
686
+ sessionId: s.agentSessionId,
687
+ ...activity
688
+ });
689
+ } catch {
690
+ }
691
+ }
692
+ var AUDIT_BUFFER_SIZE = 50;
693
+ var auditBuffer = [];
694
+ function bootstrap() {
695
+ const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;
696
+ process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;
697
+ warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });
698
+ const pbKey = process.env.PRODUCTBRAIN_API_KEY;
699
+ if (!pbKey?.startsWith("pb_sk_")) {
700
+ process.stderr.write(
701
+ "[MCP] Warning: PRODUCTBRAIN_API_KEY is not set or invalid. Tool calls will fail until a valid key is provided.\n"
702
+ );
703
+ }
704
+ }
705
+ function bootstrapHttp() {
706
+ const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;
707
+ process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;
708
+ warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });
709
+ }
710
+ function parseFallbackUrls() {
711
+ const raw = process.env.CONVEX_FALLBACK_URLS;
712
+ if (!raw) return [];
713
+ return raw.split(",").map((u) => u.trim()).filter(Boolean);
714
+ }
715
+ async function resolveDeploymentUrl() {
716
+ const s = state();
717
+ if (s.deploymentUrl) return s.deploymentUrl;
718
+ const primaryUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_CLOUD_URL).replace(/\/$/, "");
719
+ const fallbacks = parseFallbackUrls();
720
+ if (fallbacks.length === 0) {
721
+ return primaryUrl;
722
+ }
723
+ const candidates = [primaryUrl, ...fallbacks.map((u) => u.replace(/\/$/, ""))];
724
+ let apiKey;
725
+ try {
726
+ apiKey = getActiveApiKey();
727
+ } catch {
728
+ return primaryUrl;
729
+ }
730
+ for (const candidate of candidates) {
731
+ try {
732
+ const probeRes = await fetch(`${candidate}/api/key-check`, {
733
+ method: "POST",
734
+ headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
735
+ signal: AbortSignal.timeout(3e3)
736
+ });
737
+ if (probeRes.ok) {
738
+ const data = await probeRes.json();
739
+ if (data.ok) {
740
+ s.deploymentUrl = candidate;
741
+ return candidate;
742
+ }
743
+ }
744
+ } catch {
745
+ }
746
+ }
747
+ return candidates[0];
748
+ }
749
+ function shouldLogAudit(status) {
750
+ return status === "error" || process.env.MCP_DEBUG === "1";
751
+ }
752
+ function audit(fn, status, durationMs, errorMsg) {
753
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
754
+ const workspace = state().workspaceId ?? "unresolved";
755
+ const toolCtx = getToolContext();
756
+ const entry = { ts, fn, workspace, status, durationMs };
757
+ if (errorMsg) entry.error = errorMsg;
758
+ if (toolCtx) entry.toolContext = toolCtx;
759
+ auditBuffer.push(entry);
760
+ if (auditBuffer.length > AUDIT_BUFFER_SIZE) auditBuffer.shift();
761
+ trackToolCall(fn, status, durationMs, workspace, errorMsg);
762
+ if (!shouldLogAudit(status)) return;
763
+ const base = `[MCP-AUDIT] ${ts} fn=${fn} workspace=${workspace} status=${status} duration=${durationMs}ms`;
764
+ if (status === "error" && errorMsg) {
765
+ process.stderr.write(`${base} error=${JSON.stringify(errorMsg)}
766
+ `);
767
+ } else {
768
+ process.stderr.write(`${base}
769
+ `);
770
+ }
771
+ }
772
+ function getAuditLog() {
773
+ return auditBuffer;
774
+ }
775
+ var TOUCH_EXCLUDED = /* @__PURE__ */ new Set([
776
+ "agent.touchSession",
777
+ "agent.startSession",
778
+ "agent.markOriented",
779
+ "agent.recordActivity",
780
+ "agent.recordWrapup",
781
+ "agent.closeSession",
782
+ // WP-376 α.3: orient byte report is itself a heartbeat-equivalent observability
783
+ // write that already patches the same agentSessions row. A follow-up touchSession
784
+ // would create a redundant second write per orient call (DEC-50 OCC anti-pattern).
785
+ "agent.reportOrientMetric"
786
+ ]);
787
+ var MCP_TELEMETRY_SOURCE = "mcp";
788
+ async function callGateway(fn, args) {
789
+ const siteUrl = await resolveDeploymentUrl();
790
+ const apiKey = getActiveApiKey();
791
+ const start = Date.now();
792
+ let res;
793
+ try {
794
+ res = await fetch(`${siteUrl}/api/aki`, {
795
+ method: "POST",
796
+ signal: AbortSignal.timeout(1e4),
797
+ headers: {
798
+ "Content-Type": "application/json",
799
+ Authorization: `Bearer ${apiKey}`,
800
+ // DEC-1207: attribute this connector for the gateway's context.served seam. MCP is
801
+ // the dominant agent surface (INS-1706); without this header it was emitting nothing.
802
+ "x-pb-source": MCP_TELEMETRY_SOURCE
803
+ },
804
+ body: JSON.stringify({ fn, args })
805
+ });
806
+ } catch (err) {
807
+ audit(fn, "error", Date.now() - start, err.message);
808
+ throw new Error(`MCP call "${fn}" network error: ${err.message}`);
809
+ }
810
+ const json = await res.json();
811
+ if (!res.ok || json.ok === false) {
812
+ const errJson = json;
813
+ const msg = errJson.error ?? errJson.message ?? "unknown error";
814
+ audit(fn, "error", Date.now() - start, errJson.code ? `${msg} [${errJson.code}]` : msg);
815
+ throw new KernelCallError(
816
+ `MCP call "${fn}" failed (${res.status}): ${msg}`,
817
+ res.status,
818
+ errJson.code,
819
+ Array.isArray(errJson.missingRequiredFields) ? errJson.missingRequiredFields : void 0,
820
+ Array.isArray(errJson.fieldErrors) ? errJson.fieldErrors : void 0,
821
+ errJson.diagnostics && typeof errJson.diagnostics === "object" ? errJson.diagnostics : void 0
822
+ );
823
+ }
824
+ audit(fn, "ok", Date.now() - start);
825
+ const { data, summary, next, _meta } = json;
826
+ return {
827
+ data,
828
+ summary: summary || fn,
829
+ next,
830
+ _meta
831
+ };
832
+ }
833
+ async function kernelCall(fn, args = {}) {
834
+ const cached = getCached(fn, args);
835
+ if (cached !== void 0) {
836
+ return cached;
837
+ }
838
+ const { data } = await callGateway(fn, args);
839
+ if (isWrite(fn)) {
840
+ invalidateReadCache();
841
+ } else {
842
+ setCached(fn, args, data);
843
+ }
844
+ const s = state();
845
+ if (s.agentSessionId && !TOUCH_EXCLUDED.has(fn)) {
846
+ touchSessionActivity();
847
+ }
848
+ return data;
849
+ }
850
+ async function kernelCallEnvelope(fn, args = {}) {
851
+ const { data, summary, next, _meta } = await callGateway(fn, args);
852
+ const s = state();
853
+ if (s.agentSessionId && !TOUCH_EXCLUDED.has(fn)) {
854
+ touchSessionActivity();
855
+ }
856
+ return { ok: true, summary, data, next, _meta };
857
+ }
858
+ var resolveInFlightMap = /* @__PURE__ */ new Map();
859
+ async function getWorkspaceId() {
860
+ const s = state();
861
+ if (s.workspaceId) return s.workspaceId;
862
+ const apiKey = getActiveApiKey();
863
+ const existing = resolveInFlightMap.get(apiKey);
864
+ if (existing) return existing;
865
+ const promise = resolveWorkspaceWithRetry().finally(() => resolveInFlightMap.delete(apiKey));
866
+ resolveInFlightMap.set(apiKey, promise);
867
+ return promise;
868
+ }
869
+ async function resolveWorkspaceWithRetry(maxRetries = 2) {
870
+ let lastError = null;
871
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
872
+ try {
873
+ const workspace = await kernelCall("resolveWorkspace", {});
874
+ if (!workspace) {
875
+ throw new Error(
876
+ `API key is valid but no workspace is associated. Run \`npx ${MCP_NPX_PACKAGE} setup\` or regenerate your key.`
877
+ );
878
+ }
879
+ const s = state();
880
+ s.workspaceId = workspace._id;
881
+ s.workspaceSlug = workspace.slug;
882
+ s.workspaceName = workspace.name;
883
+ s.workspaceCreatedAt = workspace.createdAt ?? null;
884
+ s.workspaceGovernanceMode = workspace.governanceMode ?? "open";
885
+ if (workspace.keyScope) s.apiKeyScope = workspace.keyScope;
886
+ if (workspace.keyId) s.apiKeyId = workspace.keyId;
887
+ return s.workspaceId;
888
+ } catch (err) {
889
+ lastError = err;
890
+ const isTransient = /network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(err.message);
891
+ if (!isTransient || attempt === maxRetries) break;
892
+ const delay = 1e3 * (attempt + 1);
893
+ process.stderr.write(
894
+ `[MCP] Workspace resolution failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms...
895
+ `
896
+ );
897
+ await new Promise((r) => setTimeout(r, delay));
898
+ }
899
+ }
900
+ throw lastError;
901
+ }
902
+ async function getWorkspaceContext() {
903
+ const workspaceId = await getWorkspaceId();
904
+ const s = state();
905
+ return {
906
+ workspaceId,
907
+ workspaceSlug: s.workspaceSlug ?? "unknown",
908
+ workspaceName: s.workspaceName ?? "unknown",
909
+ createdAt: s.workspaceCreatedAt,
910
+ governanceMode: s.workspaceGovernanceMode ?? "open"
911
+ };
912
+ }
913
+ async function refreshWorkspaceGovernanceMode() {
914
+ const workspace = await kernelCall("resolveWorkspace", {});
915
+ const mode = workspace?.governanceMode ?? "open";
916
+ const s = state();
917
+ s.workspaceGovernanceMode = mode;
918
+ return mode;
919
+ }
920
+ async function kernelQuery(fn, args = {}) {
921
+ const workspaceId = await getWorkspaceId();
922
+ return kernelCall(fn, { ...args, workspaceId });
923
+ }
924
+ async function kernelMutation(fn, args = {}) {
925
+ const workspaceId = await getWorkspaceId();
926
+ return kernelCall(fn, { ...args, workspaceId });
927
+ }
928
+ function requireActiveSession() {
929
+ const s = state();
930
+ if (!s.agentSessionId) {
931
+ throw new Error(
932
+ "Active session required (SOS-iszqu7). Call `session action=start` then `orient` first."
933
+ );
934
+ }
935
+ if (s.sessionClosed) {
936
+ throw new Error(
937
+ "Session has been closed (SOS-iszqu7). Start a new session with `session action=start`."
938
+ );
939
+ }
940
+ if (!s.sessionOriented) {
941
+ throw new Error(
942
+ "Orientation required before accessing build context (SOS-iszqu7). Call `orient` first."
943
+ );
944
+ }
945
+ }
946
+ function requireWriteAccess() {
947
+ const s = state();
948
+ if (!s.agentSessionId) {
949
+ throw new Error(
950
+ "Agent session required for write operations. Call `session action=start` first."
951
+ );
952
+ }
953
+ if (s.sessionClosed) {
954
+ throw new Error(
955
+ "Agent session has been closed. Write tools are no longer available."
956
+ );
957
+ }
958
+ if (!s.sessionOriented) {
959
+ throw new Error(
960
+ "Orientation required before writing to the Chain. Call 'orient' first."
961
+ );
962
+ }
963
+ if (s.apiKeyScope === "read") {
964
+ throw new Error(
965
+ "This API key has read-only scope. Write tools are not available."
966
+ );
967
+ }
968
+ }
969
+ async function recoverSessionState() {
970
+ const s = state();
971
+ if (!s.workspaceId) return;
972
+ try {
973
+ const session = await kernelCall("agent.getActiveSession", { workspaceId: s.workspaceId });
974
+ if (session && session.status === "active") {
975
+ s.agentSessionId = session._id;
976
+ s.sessionOriented = session.oriented;
977
+ s.apiKeyScope = session.toolsScope;
978
+ s.sessionClosed = false;
979
+ }
980
+ } catch {
981
+ }
982
+ }
983
+
984
+ // src/prod-fallthrough.ts
985
+ function warnOnProdFallthrough(resolved, opts) {
986
+ if (opts.explicit) return;
987
+ if (resolved.replace(/\/$/, "") !== DEFAULT_CLOUD_URL.replace(/\/$/, "")) return;
988
+ process.stderr.write(
989
+ `[MCP] No deployment URL configured \u2014 defaulting to the production gateway ${DEFAULT_CLOUD_URL}. Set CONVEX_SITE_URL or PRODUCTBRAIN_URL to target a different deployment.
990
+ `
991
+ );
992
+ }
993
+
994
+ export {
995
+ initAnalytics,
996
+ trackSessionStarted,
997
+ trackSetupStarted,
998
+ trackSetupCompleted,
999
+ trackQualityVerdict,
1000
+ trackQualityCheck,
1001
+ trackCaptureClassifierEvaluated,
1002
+ trackCaptureClassifierAutoRouted,
1003
+ trackCaptureClassifierFallback,
1004
+ trackChainEntryCommitted,
1005
+ trackKnowledgeGap,
1006
+ trackCaptureQualityHints,
1007
+ trackCaptureRelationSuggestions,
1008
+ trackCollectionClassified,
1009
+ trackFieldGuidanceApplied,
1010
+ trackFieldQualityWarning,
1011
+ trackSessionCaptureRate,
1012
+ trackZeroCaptureAuditFired,
1013
+ trackCaptureContractMiss,
1014
+ trackWriteBackHintServed,
1015
+ trackCommitErrorByCode,
1016
+ trackClassifierDivergence,
1017
+ getPostHogClient,
1018
+ shutdownAnalytics,
1019
+ hashKey,
1020
+ runWithAuth,
1021
+ getRequestApiKey,
1022
+ getKeyState,
1023
+ MCP_NPX_PACKAGE,
1024
+ resolveClient,
1025
+ writeClientConfig,
1026
+ warnOnProdFallthrough,
1027
+ runWithToolContext,
1028
+ DEFAULT_CLOUD_URL,
1029
+ cacheScope,
1030
+ getAgentSessionId,
1031
+ isSessionOriented,
1032
+ setSessionOriented,
1033
+ getApiKeyScope,
1034
+ startAgentSession,
1035
+ closeAgentSession,
1036
+ orphanAgentSession,
1037
+ recordSessionActivity,
1038
+ bootstrap,
1039
+ bootstrapHttp,
1040
+ getAuditLog,
1041
+ kernelCall,
1042
+ kernelCallEnvelope,
1043
+ getWorkspaceId,
1044
+ getWorkspaceContext,
1045
+ refreshWorkspaceGovernanceMode,
1046
+ kernelQuery,
1047
+ kernelMutation,
1048
+ requireActiveSession,
1049
+ requireWriteAccess,
1050
+ recoverSessionState
1051
+ };
1052
+ //# sourceMappingURL=chunk-6JNK5DIZ.js.map