@vibekiln/cutline-mcp-cli 0.1.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.
Files changed (43) hide show
  1. package/Dockerfile +11 -0
  2. package/README.md +248 -0
  3. package/dist/auth/callback.d.ts +6 -0
  4. package/dist/auth/callback.js +97 -0
  5. package/dist/auth/keychain.d.ts +3 -0
  6. package/dist/auth/keychain.js +16 -0
  7. package/dist/commands/init.d.ts +4 -0
  8. package/dist/commands/init.js +309 -0
  9. package/dist/commands/login.d.ts +7 -0
  10. package/dist/commands/login.js +166 -0
  11. package/dist/commands/logout.d.ts +1 -0
  12. package/dist/commands/logout.js +25 -0
  13. package/dist/commands/serve.d.ts +1 -0
  14. package/dist/commands/serve.js +38 -0
  15. package/dist/commands/setup.d.ts +5 -0
  16. package/dist/commands/setup.js +278 -0
  17. package/dist/commands/status.d.ts +3 -0
  18. package/dist/commands/status.js +127 -0
  19. package/dist/commands/upgrade.d.ts +3 -0
  20. package/dist/commands/upgrade.js +112 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +64 -0
  23. package/dist/servers/chunk-DE7R7WKY.js +331 -0
  24. package/dist/servers/chunk-KMUSQOTJ.js +47 -0
  25. package/dist/servers/chunk-OP4EO6FV.js +454 -0
  26. package/dist/servers/chunk-UBBAYTW3.js +946 -0
  27. package/dist/servers/chunk-ZVWDXO6M.js +1063 -0
  28. package/dist/servers/cutline-server.js +10448 -0
  29. package/dist/servers/data-client-FPUZBUO3.js +160 -0
  30. package/dist/servers/exploration-server.js +930 -0
  31. package/dist/servers/graph-metrics-DCNR7JZN.js +12 -0
  32. package/dist/servers/integrations-server.js +107 -0
  33. package/dist/servers/output-server.js +107 -0
  34. package/dist/servers/premortem-server.js +971 -0
  35. package/dist/servers/tools-server.js +287 -0
  36. package/dist/utils/config-store.d.ts +8 -0
  37. package/dist/utils/config-store.js +35 -0
  38. package/dist/utils/config.d.ts +22 -0
  39. package/dist/utils/config.js +48 -0
  40. package/mcpb/manifest.json +77 -0
  41. package/package.json +76 -0
  42. package/server.json +42 -0
  43. package/smithery.yaml +10 -0
@@ -0,0 +1,1063 @@
1
+ // ../mcp/dist/mcp/src/utils.js
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import os from "os";
6
+ function decodeIdTokenLocally(idToken) {
7
+ const parts = idToken.split(".");
8
+ if (parts.length !== 3) {
9
+ throw new McpError(ErrorCode.InvalidRequest, "Malformed ID token");
10
+ }
11
+ try {
12
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
13
+ const payload = JSON.parse(Buffer.from(base64, "base64").toString("utf-8"));
14
+ const uid = payload.sub || payload.user_id;
15
+ if (!uid || typeof uid !== "string") {
16
+ throw new Error("Token missing uid (sub/user_id)");
17
+ }
18
+ return { uid, email: payload.email, ...payload };
19
+ } catch (e) {
20
+ if (e instanceof McpError)
21
+ throw e;
22
+ throw new McpError(ErrorCode.InvalidRequest, `Failed to decode token: ${(e.message || "").slice(0, 100)}`);
23
+ }
24
+ }
25
+ function getCloudFunctionsBaseUrl(environment) {
26
+ return environment === "staging" ? "https://us-central1-cutline-staging.cloudfunctions.net" : "https://us-central1-cutline-prod.cloudfunctions.net";
27
+ }
28
+ async function checkSubscriptionViaApi(idToken, environment) {
29
+ const baseUrl = getCloudFunctionsBaseUrl(environment);
30
+ try {
31
+ const res = await fetch(`${baseUrl}/mcpSubscriptionStatus`, {
32
+ method: "GET",
33
+ headers: { Authorization: `Bearer ${idToken}` },
34
+ signal: (() => {
35
+ const c = new AbortController();
36
+ setTimeout(() => c.abort(), 1e4);
37
+ return c.signal;
38
+ })()
39
+ });
40
+ if (!res.ok) {
41
+ console.error(`[MCP Auth] Subscription check failed: HTTP ${res.status}`);
42
+ return false;
43
+ }
44
+ const data = await res.json();
45
+ const validStatuses = ["active", "trialing"];
46
+ return validStatuses.includes(data.status ?? "");
47
+ } catch (e) {
48
+ console.error("[MCP Auth] Subscription API error:", (e.message || "").slice(0, 200));
49
+ return false;
50
+ }
51
+ }
52
+ var MAX_REQUEST_SIZE = 10 * 1024 * 1024;
53
+ var MAX_JSON_STRING_LENGTH = 50 * 1024 * 1024;
54
+ function validateRequestSize(args) {
55
+ if (!args || typeof args !== "object") {
56
+ return;
57
+ }
58
+ try {
59
+ const jsonString = JSON.stringify(args);
60
+ if (jsonString.length > MAX_JSON_STRING_LENGTH) {
61
+ throw new McpError(ErrorCode.InvalidParams, `Request payload too large. Maximum size is ${MAX_REQUEST_SIZE / (1024 * 1024)}MB`);
62
+ }
63
+ } catch (error) {
64
+ if (error instanceof McpError)
65
+ throw error;
66
+ console.error("[MCP] Failed to validate request size:", error);
67
+ }
68
+ }
69
+ function mapErrorToMcp(error, context) {
70
+ const errorId = `err-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
71
+ const isClientError = error instanceof McpError || error?.code && typeof error.code === "string" && error.code.startsWith("4");
72
+ console.error("Operation failed:", {
73
+ errorId,
74
+ tool: context?.tool,
75
+ operation: context?.operation,
76
+ error: error instanceof Error ? {
77
+ name: error.name,
78
+ message: error.message,
79
+ stack: error.stack
80
+ } : String(error)
81
+ });
82
+ if (error instanceof McpError) {
83
+ return error;
84
+ }
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ const sanitizedMessage = sanitizeErrorMessage(message);
87
+ if (message.includes("NOT_FOUND") || message.includes("404")) {
88
+ return new McpError(ErrorCode.InvalidRequest, `Resource not found`);
89
+ }
90
+ if (message.includes("PERMISSION_DENIED") || message.includes("403")) {
91
+ return new McpError(ErrorCode.InvalidRequest, `Permission denied`);
92
+ }
93
+ if (message.includes("UNAUTHENTICATED") || message.includes("401")) {
94
+ return new McpError(ErrorCode.InvalidRequest, `Unauthenticated`);
95
+ }
96
+ if (isClientError) {
97
+ return new McpError(ErrorCode.InvalidParams, sanitizedMessage);
98
+ }
99
+ return new McpError(ErrorCode.InternalError, `Internal error: ${sanitizedMessage}`);
100
+ }
101
+ function sanitizeErrorMessage(message) {
102
+ let sanitized = message.replace(/\/[^\s]+/g, "[path]").replace(/[^\s]+@[^\s]+/g, "[email]").replace(/[A-Za-z0-9_-]{40,}/g, "[token]").slice(0, 500);
103
+ return sanitized || "An error occurred";
104
+ }
105
+ async function validateAuth(authToken) {
106
+ if (!authToken)
107
+ return null;
108
+ try {
109
+ return decodeIdTokenLocally(authToken);
110
+ } catch (e) {
111
+ const errorMsg = e?.message || String(e);
112
+ console.error("[MCP Auth] Token decode failed:", errorMsg.slice(0, 200));
113
+ throw new McpError(ErrorCode.InvalidRequest, `Invalid auth token: ${errorMsg.slice(0, 100)}`);
114
+ }
115
+ }
116
+ async function validateSubscription(idToken, environment) {
117
+ return checkSubscriptionViaApi(idToken, environment);
118
+ }
119
+ async function requirePremium(authToken, environment) {
120
+ const decoded = await validateAuth(authToken);
121
+ if (!decoded) {
122
+ throw new McpError(ErrorCode.InvalidRequest, "Authentication required. Run 'cutline-mcp login' to authenticate.");
123
+ }
124
+ const hasSubscription = await validateSubscription(authToken, environment);
125
+ if (!hasSubscription) {
126
+ throw new McpError(ErrorCode.InvalidRequest, "Premium subscription required. Please upgrade at https://thecutline.ai/upgrade");
127
+ }
128
+ return decoded;
129
+ }
130
+ async function resolveAuthContext(authToken) {
131
+ const decoded = await requirePremiumWithAutoAuth(authToken);
132
+ const accountType = decoded.accountType;
133
+ const ownerUid = decoded.ownerUid;
134
+ if (accountType === "agent") {
135
+ if (!ownerUid) {
136
+ throw new McpError(ErrorCode.InvalidRequest, "Agent account is misconfigured: missing ownerUid claim.");
137
+ }
138
+ return { decoded, isAgent: true, effectiveUid: ownerUid };
139
+ }
140
+ return { decoded, isAgent: false, effectiveUid: decoded.uid };
141
+ }
142
+ var MAX_CACHE_SIZE = 100;
143
+ var CACHE_CLEANUP_INTERVAL = 5 * 60 * 1e3;
144
+ var tokenCache = /* @__PURE__ */ new Map();
145
+ var lastCleanup = Date.now();
146
+ function cleanupTokenCache() {
147
+ const now = Date.now();
148
+ for (const [key, value] of tokenCache.entries()) {
149
+ if (now >= value.expiresAt) {
150
+ tokenCache.delete(key);
151
+ }
152
+ }
153
+ if (tokenCache.size > MAX_CACHE_SIZE) {
154
+ const entriesToRemove = tokenCache.size - MAX_CACHE_SIZE;
155
+ const keysToRemove = [];
156
+ for (const key of tokenCache.keys()) {
157
+ if (keysToRemove.length >= entriesToRemove)
158
+ break;
159
+ keysToRemove.push(key);
160
+ }
161
+ keysToRemove.forEach((key) => tokenCache.delete(key));
162
+ }
163
+ lastCleanup = now;
164
+ }
165
+ function getCachedToken(refreshToken) {
166
+ if (Date.now() - lastCleanup > CACHE_CLEANUP_INTERVAL) {
167
+ cleanupTokenCache();
168
+ }
169
+ const cached = tokenCache.get(refreshToken);
170
+ if (!cached) {
171
+ return null;
172
+ }
173
+ if (Date.now() >= cached.expiresAt) {
174
+ tokenCache.delete(refreshToken);
175
+ return null;
176
+ }
177
+ cached.lastUsed = Date.now();
178
+ tokenCache.delete(refreshToken);
179
+ tokenCache.set(refreshToken, cached);
180
+ return cached.idToken;
181
+ }
182
+ function setCachedToken(refreshToken, idToken, expiresAt) {
183
+ if (tokenCache.size >= MAX_CACHE_SIZE) {
184
+ cleanupTokenCache();
185
+ }
186
+ tokenCache.delete(refreshToken);
187
+ tokenCache.set(refreshToken, {
188
+ idToken,
189
+ expiresAt,
190
+ lastUsed: Date.now()
191
+ });
192
+ }
193
+ var cachedApiKey = null;
194
+ var API_KEY_CACHE_TTL = 36e5;
195
+ async function getFirebaseApiKey(environment) {
196
+ const env = environment || "production";
197
+ if (env === "staging") {
198
+ if (process.env.FIREBASE_API_KEY_STAGING) {
199
+ return process.env.FIREBASE_API_KEY_STAGING;
200
+ }
201
+ }
202
+ const apiKey = process.env.FIREBASE_API_KEY || process.env.NEXT_PUBLIC_FIREBASE_API_KEY;
203
+ if (apiKey) {
204
+ return apiKey;
205
+ }
206
+ if (cachedApiKey && cachedApiKey.environment === env && Date.now() - cachedApiKey.fetchedAt < API_KEY_CACHE_TTL) {
207
+ return cachedApiKey.key;
208
+ }
209
+ const baseUrl = env === "staging" ? "https://cutline-staging.web.app" : "https://thecutline.ai";
210
+ try {
211
+ const response = await fetch(`${baseUrl}/api/firebase-config`, {
212
+ headers: { "Accept": "application/json" },
213
+ signal: (() => {
214
+ const controller = new AbortController();
215
+ setTimeout(() => controller.abort(), 5e3);
216
+ return controller.signal;
217
+ })()
218
+ });
219
+ if (!response.ok) {
220
+ throw new Error(`HTTP ${response.status}`);
221
+ }
222
+ const data = await response.json();
223
+ if (!data.apiKey) {
224
+ throw new Error("No apiKey in response");
225
+ }
226
+ cachedApiKey = { key: data.apiKey, environment: env, fetchedAt: Date.now() };
227
+ return data.apiKey;
228
+ } catch (error) {
229
+ throw new Error(`Firebase API key not found. Set FIREBASE_API_KEY environment variable or ensure ${baseUrl}/api/firebase-config is accessible.`);
230
+ }
231
+ }
232
+ async function exchangeRefreshToken(refreshToken, firebaseApiKey, maxRetries = 3) {
233
+ const FIREBASE_API_KEY = firebaseApiKey || await getFirebaseApiKey();
234
+ let lastError;
235
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
236
+ try {
237
+ const response = await fetch(`https://securetoken.googleapis.com/v1/token?key=${FIREBASE_API_KEY}`, {
238
+ method: "POST",
239
+ headers: { "Content-Type": "application/json" },
240
+ body: JSON.stringify({
241
+ grant_type: "refresh_token",
242
+ refresh_token: refreshToken
243
+ }),
244
+ // Add timeout to prevent hanging requests
245
+ signal: (() => {
246
+ const controller = new AbortController();
247
+ setTimeout(() => controller.abort(), 1e4);
248
+ return controller.signal;
249
+ })()
250
+ });
251
+ if (!response.ok) {
252
+ const error = await response.json();
253
+ const errorMessage = error.error?.message || "Unknown error";
254
+ if (response.status >= 400 && response.status < 500) {
255
+ throw new Error(`Token exchange failed: ${errorMessage}`);
256
+ }
257
+ lastError = new Error(`Token exchange failed (attempt ${attempt + 1}/${maxRetries}): ${errorMessage}`);
258
+ if (attempt < maxRetries - 1) {
259
+ await new Promise((resolve) => setTimeout(resolve, 500 * Math.pow(2, attempt)));
260
+ continue;
261
+ }
262
+ throw lastError;
263
+ }
264
+ const data = await response.json();
265
+ const idToken = data.id_token;
266
+ try {
267
+ const parts = idToken.split(".");
268
+ if (parts.length === 3) {
269
+ const payload = JSON.parse(Buffer.from(parts[1], "base64").toString());
270
+ const tokenProjectId = payload.aud || payload.project_id;
271
+ console.error(`[MCP Auth] Token exchanged successfully for project: ${tokenProjectId}`);
272
+ }
273
+ } catch (e) {
274
+ }
275
+ return idToken;
276
+ } catch (error) {
277
+ if (error.name === "AbortError" || error.name === "TypeError" || error.message?.includes("fetch")) {
278
+ lastError = error;
279
+ if (attempt < maxRetries - 1) {
280
+ await new Promise((resolve) => setTimeout(resolve, 500 * Math.pow(2, attempt)));
281
+ continue;
282
+ }
283
+ } else {
284
+ throw error;
285
+ }
286
+ }
287
+ }
288
+ throw lastError || new Error("Token exchange failed after retries");
289
+ }
290
+ function getStoredApiKey() {
291
+ if (process.env.CUTLINE_API_KEY) {
292
+ return {
293
+ apiKey: process.env.CUTLINE_API_KEY,
294
+ environment: process.env.CUTLINE_ENV === "staging" ? "staging" : "production"
295
+ };
296
+ }
297
+ try {
298
+ const configPath = path.join(os.homedir(), ".cutline-mcp", "config.json");
299
+ if (fs.existsSync(configPath)) {
300
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
301
+ if (config.apiKey) {
302
+ return { apiKey: config.apiKey, environment: config.environment };
303
+ }
304
+ }
305
+ } catch {
306
+ }
307
+ return null;
308
+ }
309
+ async function getStoredToken() {
310
+ if (process.env.CUTLINE_MCP_REFRESH_TOKEN) {
311
+ console.error("[MCP Auth] Using token from CUTLINE_MCP_REFRESH_TOKEN env var");
312
+ return {
313
+ refreshToken: process.env.CUTLINE_MCP_REFRESH_TOKEN,
314
+ environment: process.env.CUTLINE_ENV === "staging" ? "staging" : "production"
315
+ };
316
+ }
317
+ try {
318
+ const configPath = path.join(os.homedir(), ".cutline-mcp", "config.json");
319
+ if (fs.existsSync(configPath)) {
320
+ const content = fs.readFileSync(configPath, "utf-8");
321
+ const config = JSON.parse(content);
322
+ if (config.refreshToken) {
323
+ console.error("[MCP Auth] Using token from ~/.cutline-mcp/config.json", config.environment ? `(environment: ${config.environment})` : "");
324
+ return {
325
+ refreshToken: config.refreshToken,
326
+ environment: config.environment
327
+ };
328
+ }
329
+ }
330
+ } catch (e) {
331
+ console.error("[MCP Auth] Failed to read config file:", e);
332
+ }
333
+ return null;
334
+ }
335
+ async function requirePremiumWithAutoAuth(authToken) {
336
+ let idToken = authToken;
337
+ let environment;
338
+ if (!idToken) {
339
+ const storedTokenInfo = await getStoredToken();
340
+ environment = storedTokenInfo?.environment;
341
+ if (storedTokenInfo) {
342
+ const { refreshToken } = storedTokenInfo;
343
+ const apiKeyToUse = await getFirebaseApiKey(environment || "production");
344
+ const cached = getCachedToken(refreshToken);
345
+ if (cached) {
346
+ idToken = cached;
347
+ } else {
348
+ try {
349
+ idToken = await exchangeRefreshToken(refreshToken, apiKeyToUse);
350
+ setCachedToken(refreshToken, idToken, Date.now() + 50 * 60 * 1e3);
351
+ } catch (e) {
352
+ console.error("Token exchange failed:", e);
353
+ tokenCache.delete(refreshToken);
354
+ }
355
+ }
356
+ }
357
+ }
358
+ return await requirePremium(idToken, environment);
359
+ }
360
+ async function requireAuthOnly(authToken) {
361
+ let idToken = authToken;
362
+ if (!idToken) {
363
+ const storedTokenInfo = await getStoredToken();
364
+ if (storedTokenInfo) {
365
+ const { refreshToken, environment } = storedTokenInfo;
366
+ const apiKeyToUse = await getFirebaseApiKey(environment || "production");
367
+ const cached = getCachedToken(refreshToken);
368
+ if (cached) {
369
+ idToken = cached;
370
+ } else {
371
+ try {
372
+ idToken = await exchangeRefreshToken(refreshToken, apiKeyToUse);
373
+ setCachedToken(refreshToken, idToken, Date.now() + 50 * 60 * 1e3);
374
+ } catch (e) {
375
+ console.error("Token exchange failed:", e);
376
+ tokenCache.delete(refreshToken);
377
+ }
378
+ }
379
+ }
380
+ }
381
+ const decoded = await validateAuth(idToken);
382
+ if (!decoded) {
383
+ throw new McpError(ErrorCode.InvalidRequest, "Authentication required. Run 'cutline-mcp login' to authenticate.");
384
+ }
385
+ return decoded;
386
+ }
387
+ async function resolveAuthContextFree(authToken) {
388
+ const decoded = await requireAuthOnly(authToken);
389
+ const accountType = decoded.accountType;
390
+ const ownerUid = decoded.ownerUid;
391
+ if (accountType === "agent" && ownerUid) {
392
+ return { decoded, isAgent: true, effectiveUid: ownerUid };
393
+ }
394
+ return { decoded, isAgent: false, effectiveUid: decoded.uid };
395
+ }
396
+
397
+ // ../mcp/dist/mcp/src/data-client.js
398
+ function getBaseUrl(environment) {
399
+ return environment === "staging" ? "https://us-central1-cutline-staging.cloudfunctions.net" : "https://us-central1-cutline-prod.cloudfunctions.net";
400
+ }
401
+ var cachedBaseUrl;
402
+ var cachedIdToken;
403
+ var tokenExpiresAt = 0;
404
+ var FIREBASE_API_KEY_CACHE = {};
405
+ async function getFirebaseApiKey2(environment) {
406
+ const envKey = process.env.FIREBASE_API_KEY || process.env.NEXT_PUBLIC_FIREBASE_API_KEY;
407
+ if (envKey)
408
+ return envKey;
409
+ if (FIREBASE_API_KEY_CACHE.key && FIREBASE_API_KEY_CACHE.env === environment && FIREBASE_API_KEY_CACHE.at && Date.now() - FIREBASE_API_KEY_CACHE.at < 36e5) {
410
+ return FIREBASE_API_KEY_CACHE.key;
411
+ }
412
+ const baseUrl = environment === "staging" ? "https://cutline-staging.web.app" : "https://thecutline.ai";
413
+ const res = await fetch(`${baseUrl}/api/firebase-config`, {
414
+ headers: { Accept: "application/json" },
415
+ signal: AbortSignal.timeout(5e3)
416
+ });
417
+ if (!res.ok)
418
+ throw new Error(`Failed to fetch firebase config: HTTP ${res.status}`);
419
+ const data = await res.json();
420
+ if (!data.apiKey)
421
+ throw new Error("No apiKey in firebase-config response");
422
+ FIREBASE_API_KEY_CACHE.key = data.apiKey;
423
+ FIREBASE_API_KEY_CACHE.env = environment;
424
+ FIREBASE_API_KEY_CACHE.at = Date.now();
425
+ return data.apiKey;
426
+ }
427
+ async function exchangeRefreshForId(refreshToken, environment) {
428
+ const apiKey = await getFirebaseApiKey2(environment);
429
+ const res = await fetch(`https://securetoken.googleapis.com/v1/token?key=${apiKey}`, {
430
+ method: "POST",
431
+ headers: { "Content-Type": "application/json" },
432
+ body: JSON.stringify({ grant_type: "refresh_token", refresh_token: refreshToken }),
433
+ signal: AbortSignal.timeout(1e4)
434
+ });
435
+ if (!res.ok) {
436
+ const err = await res.json().catch(() => ({}));
437
+ throw new Error(`Token exchange failed: ${err?.error?.message || res.status}`);
438
+ }
439
+ const data = await res.json();
440
+ return data.id_token;
441
+ }
442
+ async function resolveAuth() {
443
+ const apiKeyInfo = getStoredApiKey();
444
+ if (apiKeyInfo) {
445
+ return {
446
+ baseUrl: getBaseUrl(apiKeyInfo.environment),
447
+ idToken: apiKeyInfo.apiKey
448
+ };
449
+ }
450
+ if (cachedIdToken && Date.now() < tokenExpiresAt && cachedBaseUrl) {
451
+ return { baseUrl: cachedBaseUrl, idToken: cachedIdToken };
452
+ }
453
+ const stored = await getStoredToken();
454
+ if (!stored) {
455
+ throw new Error("Not authenticated. Run 'cutline-mcp login' or set CUTLINE_API_KEY.");
456
+ }
457
+ const env = stored.environment || "production";
458
+ const baseUrl = getBaseUrl(env);
459
+ const idToken = await exchangeRefreshForId(stored.refreshToken, env);
460
+ cachedBaseUrl = baseUrl;
461
+ cachedIdToken = idToken;
462
+ tokenExpiresAt = Date.now() + 50 * 60 * 1e3;
463
+ return { baseUrl, idToken };
464
+ }
465
+ async function proxy(action, params = {}) {
466
+ const { baseUrl, idToken } = await resolveAuth();
467
+ const res = await fetch(`${baseUrl}/mcpDataProxy`, {
468
+ method: "POST",
469
+ headers: {
470
+ "Content-Type": "application/json",
471
+ Authorization: `Bearer ${idToken}`
472
+ },
473
+ body: JSON.stringify({ action, params }),
474
+ signal: AbortSignal.timeout(3e4)
475
+ });
476
+ if (!res.ok) {
477
+ if (res.status === 401) {
478
+ cachedIdToken = void 0;
479
+ tokenExpiresAt = 0;
480
+ throw new Error("Authentication expired \u2014 retrying on next call");
481
+ }
482
+ const body = await res.text().catch(() => "");
483
+ throw new Error(`mcpDataProxy ${action} failed (${res.status}): ${body.slice(0, 200)}`);
484
+ }
485
+ return res.json();
486
+ }
487
+ async function listPremortems(opts) {
488
+ const res = await proxy("premortem.list", opts || {});
489
+ return res.docs;
490
+ }
491
+ async function getPremortem(id) {
492
+ const res = await proxy("premortem.get", { id });
493
+ return res.doc;
494
+ }
495
+ async function createPremortem(data, id) {
496
+ return proxy("premortem.create", { data, id });
497
+ }
498
+ async function updatePremortem(id, data) {
499
+ return proxy("premortem.update", { id, data });
500
+ }
501
+ async function getIdeaReport(id) {
502
+ const res = await proxy("premortem.getIdea", { id });
503
+ return res.doc;
504
+ }
505
+ async function saveChat(id, data) {
506
+ return proxy("chat.save", { id, data });
507
+ }
508
+ async function getChat(id) {
509
+ const res = await proxy("chat.get", { id });
510
+ return res.doc;
511
+ }
512
+ async function updateChat(id, data) {
513
+ return proxy("chat.update", { id, data });
514
+ }
515
+ async function createExplorationSession(id, data, collection) {
516
+ return proxy("exploration.create", { id, data, collection });
517
+ }
518
+ async function getExplorationSession(id, collection) {
519
+ const res = await proxy("exploration.get", { id, collection });
520
+ return res.doc;
521
+ }
522
+ async function updateExplorationSession(id, data, collection) {
523
+ return proxy("exploration.update", { id, data, collection });
524
+ }
525
+ async function listExplorationSessions(collection, limit) {
526
+ const res = await proxy("exploration.list", { collection, limit });
527
+ return res.docs;
528
+ }
529
+ async function getAllEntities(productId) {
530
+ const res = await proxy("graph.getEntities", { productId });
531
+ return res.docs.map((d) => ({ ...d, ingested_at: new Date(d.ingested_at?._seconds ? d.ingested_at._seconds * 1e3 : d.ingested_at) }));
532
+ }
533
+ async function upsertEntities(productId, _sourceType, _sourceId, entities) {
534
+ await proxy("graph.upsertEntities", { productId, entities: entities.map((e) => ({ ...e, ingested_at: e.ingested_at?.toISOString?.() ?? e.ingested_at })) });
535
+ }
536
+ async function addEntity(productId, entity) {
537
+ await proxy("graph.upsertEntities", { productId, entities: [{ ...entity, ingested_at: entity.ingested_at?.toISOString?.() ?? entity.ingested_at }] });
538
+ }
539
+ async function getEntityById(productId, entityId) {
540
+ const all = await getAllEntities(productId);
541
+ return all.find((e) => e.id === entityId) ?? null;
542
+ }
543
+ async function getEntitiesWithEmbeddings(productId) {
544
+ const all = await getAllEntities(productId);
545
+ return all.filter((e) => e.embedding && e.embedding.length > 0);
546
+ }
547
+ async function updateEntityEmbedding(productId, entityId, embedding) {
548
+ await proxy("graph.updateEmbedding", { productId, collection: "entities", docId: entityId, embedding });
549
+ }
550
+ async function getAllEdges(productId) {
551
+ const res = await proxy("graph.getEdges", { productId });
552
+ return res.docs;
553
+ }
554
+ async function upsertEdges(productId, _sourceId, edges) {
555
+ await proxy("graph.upsertEdges", { productId, edges });
556
+ }
557
+ async function addEdges(productId, edges) {
558
+ await proxy("graph.upsertEdges", { productId, edges });
559
+ }
560
+ async function getAllBindings(productId) {
561
+ const res = await proxy("graph.getBindings", { productId });
562
+ return res.docs;
563
+ }
564
+ async function getBindingsForEntity(productId, entityId) {
565
+ const res = await proxy("graph.getBindings", { productId, entityId });
566
+ return res.docs;
567
+ }
568
+ async function upsertBindings(productId, bindings) {
569
+ await proxy("graph.upsertBindings", { productId, bindings });
570
+ }
571
+ async function deleteBinding(productId, bindingId) {
572
+ await proxy("graph.upsertBindings", { productId, deleteIds: [bindingId] });
573
+ }
574
+ var NODE_CACHE_TTL = 10 * 60 * 1e3;
575
+ var nodeCache = /* @__PURE__ */ new Map();
576
+ var lightNodeCache = /* @__PURE__ */ new Map();
577
+ function invalidateNodeCache(productId) {
578
+ nodeCache.delete(productId);
579
+ lightNodeCache.delete(productId);
580
+ }
581
+ async function getAllNodes(productId) {
582
+ const cached = nodeCache.get(productId);
583
+ if (cached && Date.now() - cached.fetchedAt < NODE_CACHE_TTL)
584
+ return cached.nodes;
585
+ const res = await proxy("graph.getNodes", { productId });
586
+ const nodes = res.docs.map((d) => ({
587
+ ...d,
588
+ ingested_at: new Date(d.ingested_at?._seconds ? d.ingested_at._seconds * 1e3 : d.ingested_at)
589
+ }));
590
+ nodeCache.set(productId, { nodes, fetchedAt: Date.now() });
591
+ return nodes;
592
+ }
593
+ async function getAllNodesLight(productId) {
594
+ const cached = lightNodeCache.get(productId);
595
+ if (cached && Date.now() - cached.fetchedAt < NODE_CACHE_TTL)
596
+ return cached.nodes;
597
+ const res = await proxy("graph.getNodes", { productId, light: true });
598
+ const nodes = res.docs.map((d) => ({
599
+ ...d,
600
+ ingested_at: new Date(d.ingested_at?._seconds ? d.ingested_at._seconds * 1e3 : d.ingested_at)
601
+ }));
602
+ lightNodeCache.set(productId, { nodes, fetchedAt: Date.now() });
603
+ return nodes;
604
+ }
605
+ async function getNodesByCategories(productId, categories) {
606
+ const res = await proxy("graph.getNodes", { productId, categories: categories.slice(0, 10) });
607
+ return res.docs.map((d) => ({
608
+ ...d,
609
+ ingested_at: new Date(d.ingested_at?._seconds ? d.ingested_at._seconds * 1e3 : d.ingested_at)
610
+ }));
611
+ }
612
+ async function getNodesBySource(productId, sourceType) {
613
+ const res = await proxy("graph.getNodes", { productId, sourceType });
614
+ return res.docs.map((d) => ({
615
+ ...d,
616
+ ingested_at: new Date(d.ingested_at?._seconds ? d.ingested_at._seconds * 1e3 : d.ingested_at)
617
+ }));
618
+ }
619
+ async function upsertNodes(productId, _sourceType, _sourceId, nodes) {
620
+ await proxy("graph.upsertNodes", { productId, nodes: nodes.map((n) => ({ ...n, ingested_at: n.ingested_at?.toISOString?.() ?? n.ingested_at })) });
621
+ invalidateNodeCache(productId);
622
+ }
623
+ async function addNodes(productId, nodes) {
624
+ await proxy("graph.upsertNodes", { productId, nodes: nodes.map((n) => ({ ...n, ingested_at: n.ingested_at?.toISOString?.() ?? n.ingested_at })) });
625
+ invalidateNodeCache(productId);
626
+ }
627
+ async function deleteAllNodes(productId) {
628
+ await proxy("graph.deleteAll", { productId });
629
+ invalidateNodeCache(productId);
630
+ }
631
+ async function hasConstraints(productId) {
632
+ const res = await proxy("graph.hasConstraints", { productId });
633
+ return res.hasConstraints;
634
+ }
635
+ async function searchNodesByKeywords(productId, keywords) {
636
+ const allNodes = await getAllNodes(productId);
637
+ const lowerKeywords = keywords.map((k) => k.toLowerCase());
638
+ return allNodes.filter((node) => {
639
+ const nodeKeywords = (node.keywords || []).map((k) => k.toLowerCase());
640
+ return lowerKeywords.some((kw) => nodeKeywords.some((nk) => nk.includes(kw) || kw.includes(nk)));
641
+ });
642
+ }
643
+ async function getNodesWithEmbeddings(productId) {
644
+ const all = await getAllNodes(productId);
645
+ return all.filter((n) => n.embedding && n.embedding.length > 0);
646
+ }
647
+ async function getNodesMissingEmbeddings(productId) {
648
+ const all = await getAllNodes(productId);
649
+ return all.filter((n) => !n.embedding || n.embedding.length === 0);
650
+ }
651
+ async function updateNodeEmbeddings(productId, updates) {
652
+ for (const { id, embedding } of updates) {
653
+ await proxy("graph.updateEmbedding", { productId, collection: "nodes", docId: id, embedding });
654
+ }
655
+ invalidateNodeCache(productId);
656
+ }
657
+ async function addTestCases(productId, cases) {
658
+ await proxy("graph.addTestCases", { productId, testCases: cases });
659
+ }
660
+ async function getTestCasesForEntity(productId, entityId) {
661
+ const res = await proxy("graph.getTestCases", { productId, entityId });
662
+ return res.docs;
663
+ }
664
+ async function getTestCasesForProduct(productId) {
665
+ const res = await proxy("graph.getTestCases", { productId });
666
+ return res.docs;
667
+ }
668
+ async function getGraphMetadata(productId) {
669
+ const res = await proxy("graph.getMeta", { productId });
670
+ if (!res.doc)
671
+ return null;
672
+ const data = res.doc;
673
+ return { ...data, last_build: new Date(data.last_build?._seconds ? data.last_build._seconds * 1e3 : data.last_build ?? Date.now()) };
674
+ }
675
+ async function updateGraphMetadata(productId, meta) {
676
+ const payload = { ...meta };
677
+ if (meta.last_build instanceof Date) {
678
+ payload.last_build = meta.last_build.toISOString();
679
+ }
680
+ await proxy("graph.updateMeta", { productId, data: payload });
681
+ }
682
+ async function recordScoreSnapshot(productId, metrics, event, _eventDetail) {
683
+ try {
684
+ const snapshot = {
685
+ engineering_readiness_pct: Math.round(metrics.engineering_readiness_pct ?? 0),
686
+ security_readiness_pct: Math.round(metrics.security_readiness_pct ?? 0),
687
+ reliability_readiness_pct: Math.round(metrics.reliability_readiness_pct ?? 0),
688
+ scalability_readiness_pct: Math.round(metrics.scalability_readiness_pct ?? 0),
689
+ nfr_coverage_pct: Math.round((metrics.nfr_coverage?.overall ?? 0) * 100),
690
+ speedup_factor: metrics.time_estimate?.speedup_factor ?? 1,
691
+ event,
692
+ ..._eventDetail ? { event_detail: _eventDetail } : {},
693
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
694
+ };
695
+ const snapshotId = `score_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
696
+ const prefix = "result.decision.productionalization_feasibility";
697
+ const jobUpdate = {
698
+ [`${prefix}.engineering_readiness_pct`]: snapshot.engineering_readiness_pct,
699
+ [`${prefix}.security_readiness_pct`]: snapshot.security_readiness_pct,
700
+ [`${prefix}.reliability_readiness_pct`]: snapshot.reliability_readiness_pct,
701
+ [`${prefix}.scalability_readiness_pct`]: snapshot.scalability_readiness_pct,
702
+ [`${prefix}.nfr_coverage_pct`]: snapshot.nfr_coverage_pct
703
+ };
704
+ await proxy("graph.recordScore", { productId, snapshotId, snapshot, jobUpdate });
705
+ } catch (err) {
706
+ console.error("[score-history] Failed to record snapshot:", err.message);
707
+ }
708
+ }
709
+ async function deleteGraphData(productId) {
710
+ await proxy("graph.deleteAll", { productId });
711
+ invalidateNodeCache(productId);
712
+ }
713
+ async function getReadinessReport(id) {
714
+ const res = await proxy("readiness.get", { id });
715
+ return res.doc;
716
+ }
717
+ async function saveReadinessReport(id, data) {
718
+ return proxy("readiness.save", { id, data });
719
+ }
720
+ async function generateReadinessReportViaProxy(input) {
721
+ const { productId, ownerUid, computeMetricsFromGraph: computeMetrics, computeGrade } = input;
722
+ const [entities, edges, constraintNodes, bindings] = await Promise.all([
723
+ getAllEntities(productId),
724
+ getAllEdges(productId),
725
+ getAllNodes(productId),
726
+ getAllBindings(productId)
727
+ ]);
728
+ if (entities.length === 0 && constraintNodes.length === 0) {
729
+ throw new Error(`No graph data for product "${productId}". Run graph_ingest_requirements or constraints_ingest first.`);
730
+ }
731
+ const metrics = computeMetrics(entities, edges, constraintNodes, bindings);
732
+ const readinessPct = metrics.engineering_readiness_pct ?? 0;
733
+ const jobData = await getPremortem(productId);
734
+ const result = jobData?.result;
735
+ const productName = result?.project?.name || jobData?.payload?.project?.name || "Untitled Product";
736
+ const verdict = result?.decision?.recommendation;
737
+ const techStack = entities.filter((e) => e.type === "component").map((e) => e.name).slice(0, 15);
738
+ const reportId = productId;
739
+ const now = /* @__PURE__ */ new Date();
740
+ const existing = await getReadinessReport(reportId);
741
+ const report = {
742
+ productId,
743
+ productName,
744
+ ownerUid,
745
+ grade: computeGrade(readinessPct),
746
+ readinessPct: Math.round(readinessPct),
747
+ securityReadinessPct: Math.round(metrics.security_readiness_pct ?? 0),
748
+ reliabilityReadinessPct: Math.round(metrics.reliability_readiness_pct ?? 0),
749
+ scalabilityReadinessPct: Math.round(metrics.scalability_readiness_pct ?? 0),
750
+ nfrCoverage: metrics.nfr_coverage,
751
+ timeEstimate: {
752
+ unassisted_hours: metrics.time_estimate.unassisted_hours,
753
+ assisted_hours: metrics.time_estimate.assisted_hours,
754
+ speedup_factor: metrics.time_estimate.speedup_factor
755
+ },
756
+ complexityFactors: {
757
+ entity_count: metrics.complexity_factors.entity_count,
758
+ nfr_count: metrics.complexity_factors.nfr_count,
759
+ critical_nfr_count: metrics.complexity_factors.critical_nfr_count,
760
+ pii_data_types: metrics.complexity_factors.pii_data_types,
761
+ conflict_count: metrics.complexity_factors.conflict_count
762
+ },
763
+ rgrCompletedPhases: metrics.rgr_completed_phases ?? [],
764
+ complianceFrameworks: metrics.compliance_frameworks ?? [],
765
+ verdict,
766
+ techStack,
767
+ public: true,
768
+ createdAt: existing?.createdAt ? new Date(existing.createdAt) : now,
769
+ updatedAt: now
770
+ };
771
+ await saveReadinessReport(reportId, report);
772
+ return { report, reportId };
773
+ }
774
+ async function listTemplates(opts) {
775
+ const res = await proxy("template.list", opts || {});
776
+ return res.docs;
777
+ }
778
+ async function getTemplate(id) {
779
+ const res = await proxy("template.get", { id });
780
+ return res.doc;
781
+ }
782
+ async function createTemplate(data, id) {
783
+ return proxy("template.create", { data, id });
784
+ }
785
+ async function updateTemplate(id, data) {
786
+ return proxy("template.update", { id, data });
787
+ }
788
+ async function saveScanReport(data, collection, id) {
789
+ return proxy("scan.save", { data, collection, id });
790
+ }
791
+ async function getScanRateLimit() {
792
+ return proxy("scan.rateCheck", {});
793
+ }
794
+ async function updateScanRateLimit(data) {
795
+ return proxy("scan.rateUpdate", { data });
796
+ }
797
+ async function listPersonas(productId) {
798
+ const res = await proxy("persona.list", { productId });
799
+ return res.docs;
800
+ }
801
+ async function getPersona(personaId) {
802
+ const res = await proxy("persona.get", { id: personaId });
803
+ return res.doc;
804
+ }
805
+ async function callCF(functionName, body, timeoutMs = 6e4) {
806
+ const { baseUrl, idToken } = await resolveAuth();
807
+ const res = await fetch(`${baseUrl}/${functionName}`, {
808
+ method: "POST",
809
+ headers: {
810
+ "Content-Type": "application/json",
811
+ Authorization: `Bearer ${idToken}`
812
+ },
813
+ body: JSON.stringify(body),
814
+ signal: AbortSignal.timeout(timeoutMs)
815
+ });
816
+ if (!res.ok) {
817
+ if (res.status === 401) {
818
+ cachedIdToken = void 0;
819
+ tokenExpiresAt = 0;
820
+ }
821
+ const text = await res.text().catch(() => "");
822
+ throw new Error(`CF ${functionName} failed (${res.status}): ${text.slice(0, 300)}`);
823
+ }
824
+ return res.json();
825
+ }
826
+ async function callCFGet(functionName, query, timeoutMs = 3e4) {
827
+ const { baseUrl, idToken } = await resolveAuth();
828
+ const qs = new URLSearchParams(query).toString();
829
+ const res = await fetch(`${baseUrl}/${functionName}?${qs}`, {
830
+ method: "GET",
831
+ headers: { Authorization: `Bearer ${idToken}` },
832
+ signal: AbortSignal.timeout(timeoutMs)
833
+ });
834
+ if (!res.ok) {
835
+ if (res.status === 401) {
836
+ cachedIdToken = void 0;
837
+ tokenExpiresAt = 0;
838
+ }
839
+ const text = await res.text().catch(() => "");
840
+ throw new Error(`CF ${functionName} failed (${res.status}): ${text.slice(0, 300)}`);
841
+ }
842
+ return res.json();
843
+ }
844
+ async function cfGenerateTrialRun(prompt) {
845
+ const res = await callCF("trialRun", { prompt });
846
+ return res.text;
847
+ }
848
+ async function cfGenerateChatSuggestion(prompt, wikiMarkdown) {
849
+ const res = await callCF("agentChat", { prompt, wikiMarkdown });
850
+ return res.text;
851
+ }
852
+ async function cfGenerateAnswer(question, doc) {
853
+ return callCF("qaAnswer", { question, doc });
854
+ }
855
+ async function cfChatWithPersona(persona, userMessage, product, conversationHistory) {
856
+ return callCF("personaAgent", { persona, userMessage, product, conversationHistory });
857
+ }
858
+ async function cfGenerateTemplateResponse(message, context) {
859
+ return callCF("templateDiscoveryAgent", { message, context }, 12e4);
860
+ }
861
+ async function cfGenerateExplorationResponse(message, context) {
862
+ return callCF("explorationAgent", { message, context }, 12e4);
863
+ }
864
+ async function cfBuildAndUploadPdf(doc) {
865
+ return callCF("premortemPdf", doc, 6e4);
866
+ }
867
+ async function cfGetWikiMarkdown(projectId) {
868
+ const res = await callCFGet("wikiLoad", { projectId });
869
+ return res.markdown;
870
+ }
871
+ async function cfSaveWikiMarkdown(projectId, markdown) {
872
+ await callCF("wikiSave", { projectId, markdown });
873
+ }
874
+ async function cfApplyEdits(edits) {
875
+ return callCF("applyWikiEdits", { edits });
876
+ }
877
+ async function cfPremortemRun(input) {
878
+ return callCF("premortemRun", input, 6e5);
879
+ }
880
+ async function cfPremortemStart(input) {
881
+ return callCF("premortemStart", input, 3e4);
882
+ }
883
+ async function cfPremortemKick(jobId) {
884
+ return callCF("premortemKick", { id: jobId }, 6e5);
885
+ }
886
+ async function cfPremortemChatAgent(message, context) {
887
+ return callCF("premortemChatAgent", { message, context }, 12e4);
888
+ }
889
+ async function cfRegenAssumptions(input, doc) {
890
+ return callCF("regenAssumptions", { input, doc }, 9e4);
891
+ }
892
+ async function cfRegenExperiments(input, doc) {
893
+ return callCF("regenExperiments", { input, doc }, 9e4);
894
+ }
895
+ async function cfGenerateStructuredContent(options) {
896
+ const res = await proxy("llm.generate", options);
897
+ return res.text;
898
+ }
899
+ async function cfGenerateEmbeddings(texts, taskType = "RETRIEVAL_DOCUMENT") {
900
+ return proxy("embeddings.generate", { texts, taskType });
901
+ }
902
+ async function cfExplorationAgent(message, context) {
903
+ return callCF("explorationAgent", { message, context }, 12e4);
904
+ }
905
+ async function cfConsultingDiscoveryAgent(message, context) {
906
+ return callCF("consultingDiscoveryAgent", { message, context }, 12e4);
907
+ }
908
+ async function cfCreateLinearIssues(schema_json, limit) {
909
+ return callCF("integrationsIssues", { schema_json, limit });
910
+ }
911
+ function generatePodcastTagline(persona) {
912
+ const riskTolerance = persona.personality?.risk_tolerance;
913
+ const decisionStyle = persona.grounding?.behaviorGuardrails?.decisionStyle;
914
+ const skepticism = persona.grounding?.behaviorGuardrails?.skepticismLevel;
915
+ const descriptors = [];
916
+ if (skepticism !== void 0) {
917
+ if (skepticism >= 7)
918
+ descriptors.push("skeptical");
919
+ else if (skepticism <= 3)
920
+ descriptors.push("enthusiastic");
921
+ }
922
+ if (decisionStyle) {
923
+ const map = { impulsive: "decisive", methodical: "thorough", "consensus-seeking": "collaborative", "data-driven": "analytical" };
924
+ descriptors.push(map[decisionStyle] || decisionStyle);
925
+ }
926
+ if (riskTolerance) {
927
+ const map = { low: "cautious", medium: "pragmatic", high: "bold" };
928
+ descriptors.push(map[riskTolerance] || riskTolerance);
929
+ }
930
+ const role = persona.role?.split(" ").pop() || "professional";
931
+ return `The ${descriptors[0] || "insightful"} ${role}`;
932
+ }
933
+ function generatePodcastIntro(persona) {
934
+ const name = persona.name;
935
+ const role = persona.role || "our guest";
936
+ const traits = persona.personality?.traits?.slice(0, 2);
937
+ const traitsStr = traits?.length ? `, known for being ${traits.join(" and ")}` : "";
938
+ const firstGoal = persona.behaviors?.goals?.[0];
939
+ const goalStr = firstGoal ? ` Their focus: ${firstGoal}.` : "";
940
+ return `Joining us is ${name}, ${role}${traitsStr}.${goalStr}`;
941
+ }
942
+ function formatPodcastIntro(persona) {
943
+ return {
944
+ id: persona.id,
945
+ name: persona.name,
946
+ tagline: persona.podcast?.tagline || generatePodcastTagline(persona),
947
+ intro: persona.podcast?.intro || generatePodcastIntro(persona),
948
+ perspective: persona.podcast?.perspective || null,
949
+ signatureTraits: persona.podcast?.signatureTraits || [],
950
+ voiceId: persona.podcast?.voiceId || null,
951
+ voiceStyle: persona.podcast?.voiceStyle || null
952
+ };
953
+ }
954
+ async function getPodcastIntroductions(productId, format = "json") {
955
+ const personas = await listPersonas(productId);
956
+ const introductions = personas.map(formatPodcastIntro);
957
+ if (format === "script") {
958
+ const script = introductions.map((p, i) => {
959
+ const prefix = i === 0 ? "Our first guest today" : i === introductions.length - 1 ? "And finally" : "Also joining us";
960
+ const traits = p.signatureTraits.length > 0 ? ` Watch out for their signature move: ${p.signatureTraits[0]}.` : "";
961
+ return `${prefix} \u2014 ${p.tagline}. ${p.intro}${traits}`;
962
+ }).join("\n\n");
963
+ return { format: "script", script, participants: introductions };
964
+ }
965
+ if (format === "bullets") {
966
+ const bullets = introductions.map((p) => {
967
+ const traits = p.signatureTraits.length > 0 ? `
968
+ \u2022 Traits: ${p.signatureTraits.join(", ")}` : "";
969
+ const voice = p.voiceId ? `
970
+ \u2022 Voice: ${p.voiceId} (${p.voiceStyle || "default"})` : "";
971
+ return `\u2022 ${p.name}: "${p.tagline}"${traits}${voice}`;
972
+ }).join("\n\n");
973
+ return { format: "bullets", bullets, participants: introductions };
974
+ }
975
+ return { format: "json", participants: introductions };
976
+ }
977
+
978
+ export {
979
+ validateRequestSize,
980
+ mapErrorToMcp,
981
+ validateAuth,
982
+ resolveAuthContext,
983
+ requirePremiumWithAutoAuth,
984
+ resolveAuthContextFree,
985
+ listPremortems,
986
+ getPremortem,
987
+ createPremortem,
988
+ updatePremortem,
989
+ getIdeaReport,
990
+ saveChat,
991
+ getChat,
992
+ updateChat,
993
+ createExplorationSession,
994
+ getExplorationSession,
995
+ updateExplorationSession,
996
+ listExplorationSessions,
997
+ getAllEntities,
998
+ upsertEntities,
999
+ addEntity,
1000
+ getEntityById,
1001
+ getEntitiesWithEmbeddings,
1002
+ updateEntityEmbedding,
1003
+ getAllEdges,
1004
+ upsertEdges,
1005
+ addEdges,
1006
+ getAllBindings,
1007
+ getBindingsForEntity,
1008
+ upsertBindings,
1009
+ deleteBinding,
1010
+ getAllNodes,
1011
+ getAllNodesLight,
1012
+ getNodesByCategories,
1013
+ getNodesBySource,
1014
+ upsertNodes,
1015
+ addNodes,
1016
+ deleteAllNodes,
1017
+ hasConstraints,
1018
+ searchNodesByKeywords,
1019
+ getNodesWithEmbeddings,
1020
+ getNodesMissingEmbeddings,
1021
+ updateNodeEmbeddings,
1022
+ addTestCases,
1023
+ getTestCasesForEntity,
1024
+ getTestCasesForProduct,
1025
+ getGraphMetadata,
1026
+ updateGraphMetadata,
1027
+ recordScoreSnapshot,
1028
+ deleteGraphData,
1029
+ getReadinessReport,
1030
+ saveReadinessReport,
1031
+ generateReadinessReportViaProxy,
1032
+ listTemplates,
1033
+ getTemplate,
1034
+ createTemplate,
1035
+ updateTemplate,
1036
+ saveScanReport,
1037
+ getScanRateLimit,
1038
+ updateScanRateLimit,
1039
+ listPersonas,
1040
+ getPersona,
1041
+ cfGenerateTrialRun,
1042
+ cfGenerateChatSuggestion,
1043
+ cfGenerateAnswer,
1044
+ cfChatWithPersona,
1045
+ cfGenerateTemplateResponse,
1046
+ cfGenerateExplorationResponse,
1047
+ cfBuildAndUploadPdf,
1048
+ cfGetWikiMarkdown,
1049
+ cfSaveWikiMarkdown,
1050
+ cfApplyEdits,
1051
+ cfPremortemRun,
1052
+ cfPremortemStart,
1053
+ cfPremortemKick,
1054
+ cfPremortemChatAgent,
1055
+ cfRegenAssumptions,
1056
+ cfRegenExperiments,
1057
+ cfGenerateStructuredContent,
1058
+ cfGenerateEmbeddings,
1059
+ cfExplorationAgent,
1060
+ cfConsultingDiscoveryAgent,
1061
+ cfCreateLinearIssues,
1062
+ getPodcastIntroductions
1063
+ };