@recapt/mcp 0.0.19-beta → 0.0.20-beta

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 (93) hide show
  1. package/dist/cli/index.d.ts +0 -11
  2. package/dist/cli/index.js +1059 -21
  3. package/dist/index.d.ts +0 -19
  4. package/dist/index.js +3210 -89
  5. package/package.json +5 -3
  6. package/dist/api/client.d.ts +0 -6
  7. package/dist/api/client.js +0 -6
  8. package/dist/cli/commands/setup-self-improvement-gh.d.ts +0 -7
  9. package/dist/cli/commands/setup-self-improvement-gh.js +0 -310
  10. package/dist/cli/commands/setup.d.ts +0 -7
  11. package/dist/cli/commands/setup.js +0 -178
  12. package/dist/cli/commands/skill.d.ts +0 -24
  13. package/dist/cli/commands/skill.js +0 -264
  14. package/dist/cli/utils/ide-config.d.ts +0 -31
  15. package/dist/cli/utils/ide-config.js +0 -294
  16. package/dist/cli/utils/prompts.d.ts +0 -22
  17. package/dist/cli/utils/prompts.js +0 -71
  18. package/dist/tools/analyzeFlow.d.ts +0 -7
  19. package/dist/tools/analyzeFlow.js +0 -68
  20. package/dist/tools/analyzeFunnel.d.ts +0 -7
  21. package/dist/tools/analyzeFunnel.js +0 -63
  22. package/dist/tools/catalog/callTool.d.ts +0 -22
  23. package/dist/tools/catalog/callTool.js +0 -92
  24. package/dist/tools/catalog/index.d.ts +0 -11
  25. package/dist/tools/catalog/index.js +0 -11
  26. package/dist/tools/catalog/searchTools.d.ts +0 -22
  27. package/dist/tools/catalog/searchTools.js +0 -194
  28. package/dist/tools/compareCohorts.d.ts +0 -6
  29. package/dist/tools/compareCohorts.js +0 -84
  30. package/dist/tools/comparePeriods.d.ts +0 -6
  31. package/dist/tools/comparePeriods.js +0 -54
  32. package/dist/tools/detectDrift.d.ts +0 -6
  33. package/dist/tools/detectDrift.js +0 -55
  34. package/dist/tools/detectRegressions.d.ts +0 -6
  35. package/dist/tools/detectRegressions.js +0 -63
  36. package/dist/tools/diagnostic.d.ts +0 -6
  37. package/dist/tools/diagnostic.js +0 -109
  38. package/dist/tools/discoverPersonas.d.ts +0 -6
  39. package/dist/tools/discoverPersonas.js +0 -50
  40. package/dist/tools/getActionableIssues.d.ts +0 -7
  41. package/dist/tools/getActionableIssues.js +0 -55
  42. package/dist/tools/getAnomalies.d.ts +0 -6
  43. package/dist/tools/getAnomalies.js +0 -53
  44. package/dist/tools/getConsoleErrors.d.ts +0 -6
  45. package/dist/tools/getConsoleErrors.js +0 -61
  46. package/dist/tools/getDeadClicks.d.ts +0 -6
  47. package/dist/tools/getDeadClicks.js +0 -42
  48. package/dist/tools/getDomains.d.ts +0 -6
  49. package/dist/tools/getDomains.js +0 -34
  50. package/dist/tools/getElementFriction.d.ts +0 -6
  51. package/dist/tools/getElementFriction.js +0 -45
  52. package/dist/tools/getFlowFriction.d.ts +0 -7
  53. package/dist/tools/getFlowFriction.js +0 -57
  54. package/dist/tools/getFormFriction.d.ts +0 -6
  55. package/dist/tools/getFormFriction.js +0 -42
  56. package/dist/tools/getIssues.d.ts +0 -6
  57. package/dist/tools/getIssues.js +0 -82
  58. package/dist/tools/getJourneyPatterns.d.ts +0 -7
  59. package/dist/tools/getJourneyPatterns.js +0 -50
  60. package/dist/tools/getPageMetrics.d.ts +0 -6
  61. package/dist/tools/getPageMetrics.js +0 -47
  62. package/dist/tools/getPageTrends.d.ts +0 -6
  63. package/dist/tools/getPageTrends.js +0 -46
  64. package/dist/tools/getSessionDetails.d.ts +0 -6
  65. package/dist/tools/getSessionDetails.js +0 -70
  66. package/dist/tools/getSessionPages.d.ts +0 -7
  67. package/dist/tools/getSessionPages.js +0 -74
  68. package/dist/tools/getUxHealthReport.d.ts +0 -7
  69. package/dist/tools/getUxHealthReport.js +0 -50
  70. package/dist/tools/improvementRun.d.ts +0 -6
  71. package/dist/tools/improvementRun.js +0 -386
  72. package/dist/tools/knowledge.d.ts +0 -6
  73. package/dist/tools/knowledge.js +0 -186
  74. package/dist/tools/listPages.d.ts +0 -6
  75. package/dist/tools/listPages.js +0 -50
  76. package/dist/tools/listSessions.d.ts +0 -7
  77. package/dist/tools/listSessions.js +0 -67
  78. package/dist/tools/memory.d.ts +0 -7
  79. package/dist/tools/memory.js +0 -119
  80. package/dist/tools/predictOutcomes.d.ts +0 -6
  81. package/dist/tools/predictOutcomes.js +0 -66
  82. package/dist/tools/remediation.d.ts +0 -6
  83. package/dist/tools/remediation.js +0 -335
  84. package/dist/tools/scanSite.d.ts +0 -6
  85. package/dist/tools/scanSite.js +0 -51
  86. package/dist/tools/searchSessions.d.ts +0 -6
  87. package/dist/tools/searchSessions.js +0 -51
  88. package/dist/tools/triage.d.ts +0 -6
  89. package/dist/tools/triage.js +0 -114
  90. package/dist/tools/triageSessions.d.ts +0 -8
  91. package/dist/tools/triageSessions.js +0 -197
  92. package/dist/tools/upgradeOptions.d.ts +0 -7
  93. package/dist/tools/upgradeOptions.js +0 -67
package/dist/index.js CHANGED
@@ -1,54 +1,3175 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * MCP Server entry point.
4
- *
5
- * Exposes recapt behavioral intelligence as tools for AI coding agents.
6
- * Uses a search_tools + call_tool pattern to minimize context while
7
- * providing access to 40+ analysis tools.
8
- *
9
- * Exposed tools (11):
10
- * - search_tools: Discover tools by intent
11
- * - call_tool: Execute discovered tools
12
- * - get_domains: List tracked domains
13
- * - run_full_diagnostic: Self-improvement entry point
14
- * - triage_sessions: Find sessions needing attention
15
- * - get_upgrade_options: Check available plan upgrades
16
- * - memory_save, memory_recall, memory_list, memory_delete, memory_clear
17
- *
18
- * Hidden tools (50+): Accessible via call_tool after discovery with search_tools
19
- */
2
+
3
+ // src/index.ts
20
4
  import "dotenv/config";
21
- // @ts-ignore - MCP SDK types are complex
22
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23
- // @ts-ignore - MCP SDK types are complex
24
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
25
- import { isApiConfigured, getApiUrl, allTools, hiddenTools, getDomainsTool, runFullDiagnosticTool, triageSessionsTool, getUpgradeOptionsTool, } from "@glimt/mcp-tools";
26
- import { registerSearchTools, registerCallTool, registerToolHandler, } from "./tools/catalog/index.js";
27
- import { registerMemoryTools } from "./tools/memory.js";
28
- // ─────────────────────────────────────────────────────────────────────────────
29
- // Tool Registration
30
- // ─────────────────────────────────────────────────────────────────────────────
31
- /**
32
- * Register a tool definition with the MCP server.
33
- * The MCP SDK accepts Zod schemas directly via .shape
34
- */
7
+
8
+ // ../../libraries/mcp-tools/dist/client.js
9
+ var DEFAULT_API_URL = "https://api.recapt.app";
10
+ var REQUEST_TIMEOUT_MS = 12e4;
11
+ var _config = {};
12
+ function getApiUrl() {
13
+ return _config.apiUrl || process.env.RECAPT_API_URL || process.env.MCP_API_URL || process.env.EXTERNAL_API_URL || DEFAULT_API_URL;
14
+ }
15
+ function getSecretKey() {
16
+ return _config.secretKey || process.env.RECAPT_SECRET_KEY || process.env.MCP_SECRET_KEY;
17
+ }
18
+ function getOrganizationId() {
19
+ return _config.organizationId || process.env.MCP_ORGANIZATION_ID;
20
+ }
21
+ function isApiConfigured() {
22
+ return !!getSecretKey();
23
+ }
24
+ async function request(options) {
25
+ const { method, path, params, body } = options;
26
+ const secretKey = getSecretKey();
27
+ if (!secretKey) {
28
+ return { error: "RECAPT_SECRET_KEY not configured" };
29
+ }
30
+ const apiUrl = getApiUrl();
31
+ const url = new URL(`${apiUrl}/v1beta/query${path}`);
32
+ if (params) {
33
+ for (const [key, value] of Object.entries(params)) {
34
+ if (value !== void 0) {
35
+ url.searchParams.set(key, String(value));
36
+ }
37
+ }
38
+ }
39
+ const controller = new AbortController();
40
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
41
+ const headers = {
42
+ "x-private-key": secretKey,
43
+ "Content-Type": "application/json"
44
+ };
45
+ const orgId = getOrganizationId();
46
+ if (orgId) {
47
+ headers["x-organization-id"] = orgId;
48
+ }
49
+ try {
50
+ const res = await fetch(url.toString(), {
51
+ method,
52
+ headers,
53
+ body: body ? JSON.stringify(body) : void 0,
54
+ signal: controller.signal
55
+ });
56
+ clearTimeout(timeout);
57
+ if (!res.ok) {
58
+ const errorBody = await res.json().catch(() => ({}));
59
+ return { error: errorBody.error || `HTTP ${res.status}` };
60
+ }
61
+ const data = await res.json();
62
+ return { data };
63
+ } catch (err) {
64
+ clearTimeout(timeout);
65
+ if (err instanceof Error && err.name === "AbortError") {
66
+ return { error: `Request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s` };
67
+ }
68
+ return { error: err instanceof Error ? err.message : String(err) };
69
+ }
70
+ }
71
+ function apiGet(path, params) {
72
+ return request({ method: "GET", path, params });
73
+ }
74
+ function apiPost(path, body = {}) {
75
+ return request({ method: "POST", path, body });
76
+ }
77
+ function apiPatch(path, body = {}) {
78
+ return request({ method: "PATCH", path, body });
79
+ }
80
+ function apiDelete(path) {
81
+ return request({ method: "DELETE", path });
82
+ }
83
+
84
+ // ../../libraries/mcp-tools/dist/handlers.js
85
+ function createApiHandler(method, endpoint, buildParams) {
86
+ return async (args) => {
87
+ if (!isApiConfigured()) {
88
+ return {
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: JSON.stringify({ error: "API not configured" })
93
+ }
94
+ ],
95
+ isError: true
96
+ };
97
+ }
98
+ const url = typeof endpoint === "function" ? endpoint(args) : endpoint;
99
+ const params = buildParams ? buildParams(args) : args;
100
+ const apiCall = {
101
+ GET: () => apiGet(url, params),
102
+ POST: () => apiPost(url, params),
103
+ PATCH: () => apiPatch(url, params),
104
+ DELETE: () => apiDelete(url)
105
+ }[method];
106
+ const { data, error } = await apiCall();
107
+ if (error) {
108
+ return {
109
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
110
+ isError: true
111
+ };
112
+ }
113
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
114
+ };
115
+ }
116
+ var toolHandlers = /* @__PURE__ */ new Map();
117
+ function registerAllToolHandlers() {
118
+ toolHandlers.set("get_page_metrics", createApiHandler("GET", "/pages"));
119
+ toolHandlers.set("get_element_friction", createApiHandler("GET", "/elements"));
120
+ toolHandlers.set("get_page_trends", createApiHandler("GET", "/trends"));
121
+ toolHandlers.set("get_dead_clicks", createApiHandler("GET", "/dead-clicks"));
122
+ toolHandlers.set("get_console_errors", createApiHandler("GET", "/console-errors"));
123
+ toolHandlers.set("get_form_friction", createApiHandler("GET", "/forms/friction"));
124
+ toolHandlers.set("list_pages", createApiHandler("GET", "/pages/list"));
125
+ toolHandlers.set("scan_site", createApiHandler("GET", "/site/overview", (args) => ({
126
+ top_n: args.top_n ?? 10,
127
+ offset: args.offset ?? 0
128
+ })));
129
+ toolHandlers.set("get_ux_health_report", createApiHandler("GET", "/health-report"));
130
+ toolHandlers.set("analyze_flow", createApiHandler("POST", "/flows/analyze"));
131
+ toolHandlers.set("analyze_funnel", createApiHandler("POST", "/flows/funnel"));
132
+ toolHandlers.set("get_flow_friction", createApiHandler("GET", "/flow-friction"));
133
+ toolHandlers.set("get_journey_patterns", createApiHandler("GET", "/flows/patterns"));
134
+ toolHandlers.set("get_issues", createApiHandler("GET", "/issues"));
135
+ toolHandlers.set("get_actionable_issues", createApiHandler("GET", "/actionable-issues"));
136
+ toolHandlers.set("get_anomalies", createApiHandler("GET", "/anomalies"));
137
+ toolHandlers.set("detect_regressions", createApiHandler("GET", "/regressions"));
138
+ toolHandlers.set("detect_drift", createApiHandler("GET", "/drift"));
139
+ toolHandlers.set("compare_cohorts", createApiHandler("POST", "/cohorts/compare"));
140
+ toolHandlers.set("compare_periods", createApiHandler("POST", "/pages/compare-periods"));
141
+ toolHandlers.set("discover_personas", createApiHandler("GET", "/personas"));
142
+ toolHandlers.set("search_sessions", createApiHandler("POST", "/sessions/search"));
143
+ toolHandlers.set("list_sessions", createApiHandler("GET", "/sessions"));
144
+ toolHandlers.set("get_session_pages", async (args) => {
145
+ if (!isApiConfigured()) {
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: JSON.stringify({ error: "API not configured" })
151
+ }
152
+ ],
153
+ isError: true
154
+ };
155
+ }
156
+ const { session_id, session_ids } = args;
157
+ if (!session_id && (!session_ids || session_ids.length === 0)) {
158
+ return {
159
+ content: [
160
+ {
161
+ type: "text",
162
+ text: JSON.stringify({
163
+ error: "Either session_id or session_ids must be provided"
164
+ })
165
+ }
166
+ ],
167
+ isError: true
168
+ };
169
+ }
170
+ const isSingleSession = session_id && !session_ids;
171
+ const { data, error } = isSingleSession ? await apiGet(`/sessions/${session_id}/pages`) : await apiPost("/sessions/pages", {
172
+ session_ids: session_ids ?? [session_id]
173
+ });
174
+ if (error) {
175
+ return {
176
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
177
+ isError: true
178
+ };
179
+ }
180
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
181
+ });
182
+ toolHandlers.set("get_session_details", async (args) => {
183
+ if (!isApiConfigured()) {
184
+ return {
185
+ content: [
186
+ {
187
+ type: "text",
188
+ text: JSON.stringify({ error: "API not configured" })
189
+ }
190
+ ],
191
+ isError: true
192
+ };
193
+ }
194
+ const { session_id, session_ids } = args;
195
+ if (!session_id && (!session_ids || session_ids.length === 0)) {
196
+ return {
197
+ content: [
198
+ {
199
+ type: "text",
200
+ text: JSON.stringify({
201
+ error: "Either session_id or session_ids must be provided"
202
+ })
203
+ }
204
+ ],
205
+ isError: true
206
+ };
207
+ }
208
+ const isSingleSession = session_id && !session_ids;
209
+ const { data, error } = isSingleSession ? await apiGet(`/sessions/${session_id}`) : await apiPost("/sessions/details", {
210
+ session_ids: session_ids ?? [session_id]
211
+ });
212
+ if (error) {
213
+ return {
214
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
215
+ isError: true
216
+ };
217
+ }
218
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
219
+ });
220
+ toolHandlers.set("predict_outcomes", createApiHandler("POST", "/predict"));
221
+ toolHandlers.set("run_full_diagnostic", createApiHandler("GET", "/diagnostic/full", (args) => ({
222
+ days: args.days ?? 7
223
+ })));
224
+ toolHandlers.set("investigate_issue", createApiHandler("GET", (args) => `/issues/${args.issue_id}/investigate`, (args) => ({ days: args.days ?? 7 })));
225
+ toolHandlers.set("validate_issue", createApiHandler("POST", (args) => `/issues/${args.issue_id}/validate`, (args) => ({ lookback_days: args.lookback_days ?? 3 })));
226
+ toolHandlers.set("dismiss_issue", createApiHandler("POST", (args) => `/issues/${args.issue_id}/dismiss`, (args) => ({
227
+ reason: args.reason,
228
+ explanation: args.explanation,
229
+ create_knowledge: args.create_knowledge ?? true
230
+ })));
231
+ toolHandlers.set("mark_intended_behavior", createApiHandler("POST", (args) => `/issues/${args.issue_id}/mark-intended`, (args) => ({ explanation: args.explanation })));
232
+ toolHandlers.set("get_issue_history", createApiHandler("GET", (args) => `/issues/${args.issue_id}/history`));
233
+ toolHandlers.set("propose_fix", createApiHandler("POST", "/remediations"));
234
+ toolHandlers.set("list_pending_fixes", createApiHandler("GET", "/remediations/pending"));
235
+ toolHandlers.set("list_remediations", createApiHandler("GET", "/remediations"));
236
+ toolHandlers.set("confirm_deployment", createApiHandler("PATCH", (args) => `/remediations/${args.remediation_id}/deploy`, () => ({})));
237
+ toolHandlers.set("evaluate_fix", createApiHandler("POST", (args) => `/remediations/${args.remediation_id}/evaluate`, (args) => ({ min_hours: args.min_hours ?? 24 })));
238
+ toolHandlers.set("get_fix_history", createApiHandler("GET", (args) => `/remediations/history/${args.issue_id}`));
239
+ toolHandlers.set("list_remediations_by_status", createApiHandler("GET", "/remediations/by-status"));
240
+ toolHandlers.set("get_remediation_by_pr", createApiHandler("GET", (args) => `/remediations/by-pr/${args.pr_number}`));
241
+ toolHandlers.set("update_remediation_status", createApiHandler("PATCH", (args) => `/remediations/${args.remediation_id}/status`));
242
+ toolHandlers.set("get_site_knowledge", createApiHandler("GET", "/knowledge"));
243
+ toolHandlers.set("add_site_knowledge", createApiHandler("POST", "/knowledge", (args) => ({
244
+ ...args,
245
+ source: "agent"
246
+ })));
247
+ toolHandlers.set("delete_site_knowledge", createApiHandler("DELETE", (args) => `/knowledge/${args.knowledge_id}`));
248
+ toolHandlers.set("get_similar_fixes", createApiHandler("GET", "/knowledge/similar-fixes"));
249
+ toolHandlers.set("start_improvement_run", createApiHandler("POST", "/improvement-runs"));
250
+ toolHandlers.set("update_improvement_run", createApiHandler("PATCH", (args) => `/improvement-runs/${args.run_id}`));
251
+ toolHandlers.set("record_improvement_action", createApiHandler("POST", (args) => `/improvement-runs/${args.run_id}/actions`));
252
+ toolHandlers.set("update_improvement_action", createApiHandler("PATCH", (args) => `/improvement-runs/${args.run_id}/actions/${args.action_id}`));
253
+ toolHandlers.set("get_improvement_run", createApiHandler("GET", (args) => `/improvement-runs/${args.run_id}`));
254
+ toolHandlers.set("list_improvement_runs", createApiHandler("GET", "/improvement-runs"));
255
+ toolHandlers.set("get_domains", createApiHandler("GET", "/domains"));
256
+ toolHandlers.set("triage_sessions", createApiHandler("GET", "/sessions/triage", (args) => {
257
+ const { session_ids, days, page_path, min_severity, limit } = args;
258
+ return {
259
+ session_ids: session_ids?.join(","),
260
+ days: session_ids?.length ? void 0 : days ?? 7,
261
+ page_path: session_ids?.length ? void 0 : page_path,
262
+ min_severity,
263
+ limit: session_ids?.length ? session_ids.length : limit ?? 20
264
+ };
265
+ }));
266
+ toolHandlers.set("get_upgrade_options", createApiHandler("GET", "/upgrade-options"));
267
+ }
268
+ registerAllToolHandlers();
269
+
270
+ // ../../node_modules/zod-to-json-schema/dist/esm/selectParser.js
271
+ import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind3 } from "zod/v3";
272
+
273
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/array.js
274
+ import { ZodFirstPartyTypeKind } from "zod/v3";
275
+
276
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/record.js
277
+ import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind2 } from "zod/v3";
278
+
279
+ // ../../node_modules/zod-to-json-schema/dist/esm/parsers/string.js
280
+ var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
281
+
282
+ // ../../libraries/mcp-tools/dist/tools/analyzeFlow.js
283
+ import { z } from "zod";
284
+
285
+ // ../../libraries/mcp-tools/dist/tools/_utils.js
286
+ function errorResult(message) {
287
+ return {
288
+ content: [{ type: "text", text: JSON.stringify({ error: message }) }],
289
+ isError: true
290
+ };
291
+ }
292
+ function successResult(data) {
293
+ return {
294
+ content: [{ type: "text", text: JSON.stringify(data) }]
295
+ };
296
+ }
297
+ function apiNotConfiguredResult() {
298
+ return errorResult("API not configured");
299
+ }
300
+
301
+ // ../../libraries/mcp-tools/dist/tools/analyzeFlow.js
302
+ var analyzeFlowTool = {
303
+ name: "analyze_flow",
304
+ description: "Analyze user navigation flows between pages. Returns paths with steps, behavioral metrics (frustration, confusion), success rates, and bottlenecks. Use this to understand how users navigate between specific pages.",
305
+ inputSchema: z.object({
306
+ start_page: z.string().optional().describe("Starting page path (e.g., /pricing). Partial match supported."),
307
+ end_page: z.string().optional().describe("Ending page path (e.g., /checkout). Partial match supported."),
308
+ days: z.number().optional().default(7).describe("Number of days to analyze (default: 7)")
309
+ }),
310
+ handler: async (args) => {
311
+ const { start_page, end_page, days } = args;
312
+ if (!isApiConfigured()) {
313
+ return apiNotConfiguredResult();
314
+ }
315
+ if (!start_page && !end_page) {
316
+ return errorResult("At least one of start_page or end_page is required");
317
+ }
318
+ const { data, error } = await apiPost("/flows/analyze", {
319
+ start_page,
320
+ end_page,
321
+ days: days ?? 7
322
+ });
323
+ if (error) {
324
+ return errorResult(error);
325
+ }
326
+ return successResult(data);
327
+ }
328
+ };
329
+
330
+ // ../../libraries/mcp-tools/dist/tools/analyzeFunnel.js
331
+ import { z as z2 } from "zod";
332
+ var analyzeFunnelTool = {
333
+ name: "analyze_funnel",
334
+ description: 'Analyze a conversion funnel through a sequence of pages. Returns per-step metrics (entered, converted, dropped, dwell time, frustration, confusion) and dropoff analysis showing where users exit. Supports wildcards: "/checkout*" matches all checkout pages.',
335
+ inputSchema: z2.object({
336
+ steps: z2.array(z2.string()).min(2).describe('Ordered page paths forming the funnel (e.g., ["/cart", "/checkout", "/payment"])'),
337
+ days: z2.number().optional().default(7).describe("Number of days to analyze (default: 7)")
338
+ }),
339
+ handler: async (args) => {
340
+ const { steps, days } = args;
341
+ if (!isApiConfigured()) {
342
+ return apiNotConfiguredResult();
343
+ }
344
+ if (!steps || steps.length < 2) {
345
+ return errorResult("At least 2 steps are required for funnel analysis");
346
+ }
347
+ const { data, error } = await apiPost("/flows/funnel", {
348
+ steps,
349
+ days: days ?? 7
350
+ });
351
+ if (error) {
352
+ return errorResult(error);
353
+ }
354
+ return successResult(data);
355
+ }
356
+ };
357
+
358
+ // ../../libraries/mcp-tools/dist/tools/compareCohorts.js
359
+ import { z as z3 } from "zod";
360
+ var cohortFilterSchema = z3.object({
361
+ device: z3.enum(["desktop", "tablet", "mobile"]).optional().describe("Filter by device type"),
362
+ outcome: z3.enum(["COMPLETED", "STRUGGLED", "BLOCKED", "DISENGAGED"]).optional().describe("Filter by session outcome"),
363
+ pattern: z3.string().optional().describe("Filter by behavioral pattern"),
364
+ page_path: z3.string().optional().describe("Filter by page path"),
365
+ has_friction: z3.boolean().optional().describe("Filter for sessions with friction"),
366
+ has_rage_clicks: z3.boolean().optional().describe("Filter for sessions with rage clicks")
367
+ });
368
+ var compareCohortsTool = {
369
+ name: "compare_cohorts",
370
+ description: 'Compare two user cohorts side by side. Each cohort is defined by filter criteria: device (desktop/tablet/mobile), outcome (COMPLETED/STRUGGLED/BLOCKED/DISENGAGED), pattern, has_friction, has_rage_clicks, or page_path. Returns behavioral metrics for each cohort and statistically significant differences. Use this to answer "how do mobile users differ from desktop?", "what distinguishes users who complete vs abandon?", or "do form users struggle more than non-form users?"',
371
+ inputSchema: z3.object({
372
+ cohort_a: cohortFilterSchema.describe('Filters for cohort A, e.g. {"device": "mobile"} or {"outcome": "COMPLETED"}'),
373
+ cohort_b: cohortFilterSchema.describe('Filters for cohort B, e.g. {"device": "desktop"} or {"outcome": "BLOCKED"}'),
374
+ cohort_a_label: z3.string().optional().describe("Human label for cohort A"),
375
+ cohort_b_label: z3.string().optional().describe("Human label for cohort B"),
376
+ page_path: z3.string().optional().describe("Scope comparison to a specific page"),
377
+ date_from: z3.string().optional().describe("Start date (ISO format)"),
378
+ date_to: z3.string().optional().describe("End date (ISO format)")
379
+ }),
380
+ handler: async (args) => {
381
+ const { cohort_a, cohort_b, cohort_a_label, cohort_b_label, page_path, date_from, date_to } = args;
382
+ if (!isApiConfigured()) {
383
+ return apiNotConfiguredResult();
384
+ }
385
+ const { data, error } = await apiPost("/cohorts/compare", {
386
+ cohort_a,
387
+ cohort_b,
388
+ cohort_a_label,
389
+ cohort_b_label,
390
+ page_path,
391
+ date_from,
392
+ date_to
393
+ });
394
+ if (error) {
395
+ return errorResult(error);
396
+ }
397
+ return successResult(data);
398
+ }
399
+ };
400
+
401
+ // ../../libraries/mcp-tools/dist/tools/comparePeriods.js
402
+ import { z as z4 } from "zod";
403
+ var comparePeriodsTool = {
404
+ name: "compare_periods",
405
+ description: "Compare behavioral metrics between two time periods for a specific page. Returns metrics for each period and the deltas between them. Use this to measure the impact of changes or compare week-over-week performance.",
406
+ inputSchema: z4.object({
407
+ page_path: z4.string().describe("Page path to compare"),
408
+ period_a: z4.object({
409
+ from: z4.string().describe("Start date for period A (ISO format)"),
410
+ to: z4.string().describe("End date for period A (ISO format)")
411
+ }).describe("First time period"),
412
+ period_b: z4.object({
413
+ from: z4.string().describe("Start date for period B (ISO format)"),
414
+ to: z4.string().describe("End date for period B (ISO format)")
415
+ }).describe("Second time period")
416
+ }),
417
+ handler: async (args) => {
418
+ const { page_path, period_a, period_b } = args;
419
+ if (!isApiConfigured()) {
420
+ return apiNotConfiguredResult();
421
+ }
422
+ const { data, error } = await apiPost("/pages/compare-periods", {
423
+ page_path,
424
+ period_a,
425
+ period_b
426
+ });
427
+ if (error) {
428
+ return errorResult(error);
429
+ }
430
+ return successResult(data);
431
+ }
432
+ };
433
+
434
+ // ../../libraries/mcp-tools/dist/tools/detectDrift.js
435
+ import { z as z5 } from "zod";
436
+ var detectDriftTool = {
437
+ name: "detect_drift",
438
+ description: "Detect behavioral drift over time by analyzing how user behavior patterns change. Splits data into time windows and measures centroid shift between consecutive periods. Use this for long-term monitoring to detect gradual UX degradation.",
439
+ inputSchema: z5.object({
440
+ page_path: z5.string().optional().describe("Scope drift detection to a specific page"),
441
+ days: z5.number().optional().default(30).describe("Number of days to analyze (default 30)"),
442
+ window_size: z5.number().optional().default(7).describe("Size of each time window in days (default 7)")
443
+ }),
444
+ handler: async (args) => {
445
+ const { page_path, days, window_size } = args;
446
+ if (!isApiConfigured()) {
447
+ return apiNotConfiguredResult();
448
+ }
449
+ const { data, error } = await apiGet("/drift", {
450
+ page_path,
451
+ days: days ?? 30,
452
+ window_size: window_size ?? 7
453
+ });
454
+ if (error) {
455
+ return errorResult(error);
456
+ }
457
+ return successResult(data);
458
+ }
459
+ };
460
+
461
+ // ../../libraries/mcp-tools/dist/tools/detectRegressions.js
462
+ import { z as z6 } from "zod";
463
+ var detectRegressionsTool = {
464
+ name: "detect_regressions",
465
+ description: "Detect behavioral regressions by comparing recent data against rolling 7-day baselines. By default analyzes top 50 pages by traffic. Use sort_by='low_traffic' to check low-traffic pages that might otherwise be missed. Use offset/limit to paginate through all pages. Returns pages where frustration, confusion, or rage click rate have deviated significantly.",
466
+ inputSchema: z6.object({
467
+ window_hours: z6.number().optional().default(24).describe("How many recent hours to compare against baseline (default 24)"),
468
+ limit: z6.number().optional().default(50).describe("Number of pages to check per request (default 50, max 100)"),
469
+ offset: z6.number().optional().default(0).describe("Skip this many pages for pagination (default 0)"),
470
+ sort_by: z6.enum(["traffic", "low_traffic"]).optional().default("traffic").describe("Sort order: 'traffic' (default) or 'low_traffic' to check overlooked pages")
471
+ }),
472
+ handler: async (args) => {
473
+ const { window_hours, limit, offset, sort_by } = args;
474
+ if (!isApiConfigured()) {
475
+ return apiNotConfiguredResult();
476
+ }
477
+ const { data, error } = await apiGet("/regressions", {
478
+ window_hours: window_hours ?? 24,
479
+ limit: limit ?? 50,
480
+ offset: offset ?? 0,
481
+ sort_by: sort_by ?? "traffic"
482
+ });
483
+ if (error) {
484
+ return errorResult(error);
485
+ }
486
+ return successResult(data);
487
+ }
488
+ };
489
+
490
+ // ../../libraries/mcp-tools/dist/tools/diagnostic.js
491
+ import { z as z7 } from "zod";
492
+ var runFullDiagnosticTool = {
493
+ name: "run_full_diagnostic",
494
+ description: "Get a quick site health overview with prioritized issues and problem pages. This is a lightweight entry point \u2014 it does NOT include regression detection. For a complete analysis, call detect_regressions separately after this. Returns: health score, top issues, pages needing attention, and investigation order.",
495
+ inputSchema: z7.object({
496
+ days: z7.number().optional().default(7).describe("Number of days to analyze (default: 7)")
497
+ }),
498
+ handler: async (args) => {
499
+ const { days } = args;
500
+ if (!isApiConfigured()) {
501
+ return apiNotConfiguredResult();
502
+ }
503
+ const { data, error } = await apiGet("/diagnostic/full", {
504
+ days: days ?? 7
505
+ });
506
+ if (error) {
507
+ return errorResult(error);
508
+ }
509
+ return successResult(data);
510
+ }
511
+ };
512
+ var investigateIssueTool = {
513
+ name: "investigate_issue",
514
+ description: "Deep-dive into a specific issue. Returns affected sessions, element friction data, related console errors, similar issues, and page context. Use after identifying an issue to understand root cause. NOTE: Free tier users get issue summary and page context only. Detailed investigation data (affected sessions, element friction, related errors) requires a paid plan (Starter+).",
515
+ inputSchema: z7.object({
516
+ issue_id: z7.string().describe("The ID of the issue to investigate"),
517
+ days: z7.number().optional().default(7).describe("Number of days to look back (default: 7)")
518
+ }),
519
+ handler: async (args) => {
520
+ const { issue_id, days } = args;
521
+ if (!isApiConfigured()) {
522
+ return apiNotConfiguredResult();
523
+ }
524
+ const { data, error } = await apiGet(`/issues/${issue_id}/investigate`, {
525
+ days: days ?? 7
526
+ });
527
+ if (error) {
528
+ return errorResult(error);
529
+ }
530
+ return successResult(data);
531
+ }
532
+ };
533
+ var validateIssueTool = {
534
+ name: "validate_issue",
535
+ description: "Check if an issue is still actively occurring. Returns staleness info and recommendation (investigate, dismiss_stale, or monitor). Use to filter out stale issues before proposing fixes.",
536
+ inputSchema: z7.object({
537
+ issue_id: z7.string().describe("The ID of the issue to validate"),
538
+ lookback_days: z7.number().optional().default(3).describe("Number of days to look back for recent occurrences (default: 3)")
539
+ }),
540
+ handler: async (args) => {
541
+ const { issue_id, lookback_days } = args;
542
+ if (!isApiConfigured()) {
543
+ return apiNotConfiguredResult();
544
+ }
545
+ const { data, error } = await apiPost(`/issues/${issue_id}/validate`, {
546
+ lookback_days: lookback_days ?? 3
547
+ });
548
+ if (error) {
549
+ return errorResult(error);
550
+ }
551
+ return successResult(data);
552
+ }
553
+ };
554
+
555
+ // ../../libraries/mcp-tools/dist/tools/discoverPersonas.js
556
+ import { z as z8 } from "zod";
557
+ var discoverPersonasTool = {
558
+ name: "discover_personas",
559
+ description: "Discover behavioral personas - groups of users who behave similarly (e.g. confident completers, frustrated battlers, lost explorers). Returns rich profiles with descriptions, risk factors, and recommended interventions. Uses K-means clustering on behavioral embeddings. Optionally scope to a specific page.",
560
+ inputSchema: z8.object({
561
+ page_path: z8.string().optional().describe("Scope persona discovery to a specific page"),
562
+ num_personas: z8.number().optional().default(4).describe("Number of personas to discover (default 4, max 8)")
563
+ }),
564
+ handler: async (args) => {
565
+ const { page_path, num_personas } = args;
566
+ if (!isApiConfigured()) {
567
+ return apiNotConfiguredResult();
568
+ }
569
+ const { data, error } = await apiGet("/personas", {
570
+ page_path,
571
+ num_personas: num_personas ?? 4
572
+ });
573
+ if (error) {
574
+ return errorResult(error);
575
+ }
576
+ return successResult(data);
577
+ }
578
+ };
579
+
580
+ // ../../libraries/mcp-tools/dist/tools/getActionableIssues.js
581
+ import { z as z9 } from "zod";
582
+ var getActionableIssuesTool = {
583
+ name: "get_actionable_issues",
584
+ description: "Get UX issues with element context. Returns detected issues (rage clicks, dead clicks, errors, etc.) along with the most affected elements on each page. Use this when you want to understand and fix specific UX problems.",
585
+ inputSchema: z9.object({
586
+ page_path: z9.string().optional().describe("Filter to a specific page path (e.g., /checkout)"),
587
+ severity: z9.enum(["critical", "high", "medium", "low", "info"]).optional().describe("Minimum severity level to include"),
588
+ limit: z9.number().optional().default(10).describe("Maximum number of issues to return (default: 10)")
589
+ }),
590
+ handler: async (args) => {
591
+ const { page_path, severity, limit } = args;
592
+ if (!isApiConfigured()) {
593
+ return apiNotConfiguredResult();
594
+ }
595
+ const { data, error } = await apiGet("/actionable-issues", {
596
+ page_path,
597
+ severity,
598
+ limit: limit ?? 10
599
+ });
600
+ if (error) {
601
+ return errorResult(error);
602
+ }
603
+ return successResult(data);
604
+ }
605
+ };
606
+
607
+ // ../../libraries/mcp-tools/dist/tools/getAnomalies.js
608
+ import { z as z10 } from "zod";
609
+ var getAnomaliesTool = {
610
+ name: "get_anomalies",
611
+ description: "Detect anomalous sessions and frustration spikes. Finds sessions that deviate from normal behavior patterns.",
612
+ inputSchema: z10.object({
613
+ threshold: z10.number().optional().default(0.35).describe("Anomaly threshold (0-1, default: 0.35)"),
614
+ days: z10.number().optional().default(7).describe("Days to analyze for frustration spikes (default: 7)"),
615
+ page_path: z10.string().optional().describe("Optional: filter to a specific page")
616
+ }),
617
+ handler: async (args) => {
618
+ const { threshold, days, page_path } = args;
619
+ if (!isApiConfigured()) {
620
+ return apiNotConfiguredResult();
621
+ }
622
+ const { data, error } = await apiGet("/anomalies", {
623
+ threshold: threshold ?? 0.35,
624
+ days: days ?? 7,
625
+ page_path
626
+ });
627
+ if (error) {
628
+ return errorResult(error);
629
+ }
630
+ return successResult(data);
631
+ }
632
+ };
633
+
634
+ // ../../libraries/mcp-tools/dist/tools/getConsoleErrors.js
635
+ import { z as z11 } from "zod";
636
+ var getConsoleErrorsTool = {
637
+ name: "get_console_errors",
638
+ description: "Query JavaScript console errors and warnings from user sessions. Use this when you suspect page unresponsiveness, broken buttons, or rendering issues. Returns the most common errors grouped by message, with session impact counts. High rage clicks on root/body elements often correlate with JS errors.",
639
+ inputSchema: z11.object({
640
+ page_path: z11.string().optional().describe("Filter errors to a specific page path"),
641
+ session_id: z11.string().optional().describe("Filter errors to a specific session"),
642
+ severity: z11.enum(["error", "warn", "all"]).optional().default("error").describe('Filter by severity: "error" (default), "warn", or "all"'),
643
+ limit: z11.number().optional().default(200).describe("Max log documents to scan (default 200)")
644
+ }),
645
+ handler: async (args) => {
646
+ const { page_path, session_id, severity, limit } = args;
647
+ if (!isApiConfigured()) {
648
+ return apiNotConfiguredResult();
649
+ }
650
+ const { data, error } = await apiGet("/console-errors", {
651
+ page_path,
652
+ session_id,
653
+ severity: severity ?? "error",
654
+ limit: limit ?? 200
655
+ });
656
+ if (error) {
657
+ return errorResult(error);
658
+ }
659
+ return successResult(data);
660
+ }
661
+ };
662
+
663
+ // ../../libraries/mcp-tools/dist/tools/getDeadClicks.js
664
+ import { z as z12 } from "zod";
665
+ var getDeadClicksTool = {
666
+ name: "get_dead_clicks",
667
+ description: "Find buttons and links that users click but nothing happens (dead clicks). These are interactive elements where the click produced no visible DOM change. Use this to identify BROKEN or UNRESPONSIVE UI elements that frustrate users.",
668
+ inputSchema: z12.object({
669
+ page_path: z12.string().describe("Page path to check for dead clicks (required)")
670
+ }),
671
+ handler: async (args) => {
672
+ const { page_path } = args;
673
+ if (!isApiConfigured()) {
674
+ return apiNotConfiguredResult();
675
+ }
676
+ const { data, error } = await apiGet("/dead-clicks", {
677
+ page_path
678
+ });
679
+ if (error) {
680
+ return errorResult(error);
681
+ }
682
+ return successResult(data);
683
+ }
684
+ };
685
+
686
+ // ../../libraries/mcp-tools/dist/tools/getDomains.js
687
+ import { z as z13 } from "zod";
688
+ var getDomainsTool = {
689
+ name: "get_domains",
690
+ description: "Get the list of domains configured for the organization. Domains represent different websites or applications being tracked.",
691
+ inputSchema: z13.object({}),
692
+ handler: async () => {
693
+ if (!isApiConfigured()) {
694
+ return apiNotConfiguredResult();
695
+ }
696
+ const { data, error } = await apiGet("/domains");
697
+ if (error) {
698
+ return errorResult(error);
699
+ }
700
+ return successResult(data);
701
+ }
702
+ };
703
+
704
+ // ../../libraries/mcp-tools/dist/tools/getElementFriction.js
705
+ import { z as z14 } from "zod";
706
+ var getElementFrictionTool = {
707
+ name: "get_element_friction",
708
+ description: "Get per-element friction data for a page. Shows which elements cause user frustration, with click counts and dominant frustration signals.",
709
+ inputSchema: z14.object({
710
+ page_path: z14.string().describe("Page path to analyze (e.g., /checkout)"),
711
+ selector_filter: z14.string().optional().describe("Optional: filter elements by selector substring")
712
+ }),
713
+ handler: async (args) => {
714
+ const { page_path, selector_filter } = args;
715
+ if (!isApiConfigured()) {
716
+ return apiNotConfiguredResult();
717
+ }
718
+ const { data, error } = await apiGet("/elements", {
719
+ page_path,
720
+ selector_filter
721
+ });
722
+ if (error) {
723
+ return errorResult(error);
724
+ }
725
+ return successResult(data);
726
+ }
727
+ };
728
+
729
+ // ../../libraries/mcp-tools/dist/tools/getFlowFriction.js
730
+ import { z as z15 } from "zod";
731
+ var getFlowFrictionTool = {
732
+ name: "get_flow_friction",
733
+ description: "Discover user flows with behavioral metrics. Returns navigation transitions with frustration, confusion, and health scores for each page in the flow. Also flags backtrack hotspots (pages users return to) and dropoff pages. Use this to find which flows need optimization.",
734
+ inputSchema: z15.object({
735
+ page_path: z15.string().optional().describe("Focus on flows involving this page (e.g., /checkout)"),
736
+ days: z15.number().optional().default(7).describe("Number of days to analyze (default: 7)"),
737
+ limit: z15.number().optional().default(10).describe("Maximum number of flows to return (default: 10)")
738
+ }),
739
+ handler: async (args) => {
740
+ const { page_path, days, limit } = args;
741
+ if (!isApiConfigured()) {
742
+ return apiNotConfiguredResult();
743
+ }
744
+ const { data, error } = await apiGet("/flow-friction", {
745
+ page_path,
746
+ days: days ?? 7,
747
+ limit: limit ?? 10
748
+ });
749
+ if (error) {
750
+ return errorResult(error);
751
+ }
752
+ return successResult(data);
753
+ }
754
+ };
755
+
756
+ // ../../libraries/mcp-tools/dist/tools/getFormFriction.js
757
+ import { z as z16 } from "zod";
758
+ var getFormFrictionTool = {
759
+ name: "get_form_friction",
760
+ description: "Analyze which form fields cause friction on a page. Returns per-field metrics including dwell time, correction rate, and abandonment rate. Use this to identify problematic form fields in checkout, signup, or other forms.",
761
+ inputSchema: z16.object({
762
+ page_path: z16.string().describe("Page path containing the form to analyze (required)")
763
+ }),
764
+ handler: async (args) => {
765
+ const { page_path } = args;
766
+ if (!isApiConfigured()) {
767
+ return apiNotConfiguredResult();
768
+ }
769
+ const { data, error } = await apiGet("/forms/friction", {
770
+ page_path
771
+ });
772
+ if (error) {
773
+ return errorResult(error);
774
+ }
775
+ return successResult(data);
776
+ }
777
+ };
778
+
779
+ // ../../libraries/mcp-tools/dist/tools/getIssues.js
780
+ import { z as z17 } from "zod";
781
+ var VALID_CATEGORIES = [
782
+ "code_error",
783
+ "dead_click",
784
+ "rage_click",
785
+ "ux_friction",
786
+ "performance",
787
+ "form_issue",
788
+ "behavioral_anomaly",
789
+ "structural_issue"
790
+ ];
791
+ var VALID_SEVERITIES = ["critical", "high", "medium", "low", "info"];
792
+ var VALID_STATUSES = [
793
+ "pending",
794
+ "active",
795
+ "escalated",
796
+ "resolved",
797
+ "dismissed",
798
+ "expired"
799
+ ];
800
+ var getIssuesTool = {
801
+ name: "get_issues",
802
+ description: "Get detected UX issues. Issues are automatically detected from session recordings and include rage clicks, dead clicks, form issues, and behavioral anomalies.",
803
+ inputSchema: z17.object({
804
+ page_path: z17.string().optional().describe("Filter issues to a specific page path"),
805
+ category: z17.enum(VALID_CATEGORIES).optional().describe("Filter by issue category"),
806
+ severity: z17.enum(VALID_SEVERITIES).optional().describe("Filter by severity level"),
807
+ status: z17.enum(VALID_STATUSES).optional().default("active").describe("Filter by status (default: active)"),
808
+ limit: z17.number().optional().default(20).describe("Maximum number of issues to return (default: 20)")
809
+ }),
810
+ handler: async (args) => {
811
+ const { page_path, category, severity, status, limit } = args;
812
+ if (!isApiConfigured()) {
813
+ return apiNotConfiguredResult();
814
+ }
815
+ const { data, error } = await apiGet("/issues", {
816
+ page_path,
817
+ category,
818
+ severity,
819
+ status: status ?? "active",
820
+ limit: limit ?? 20
821
+ });
822
+ if (error) {
823
+ return errorResult(error);
824
+ }
825
+ return successResult(data);
826
+ }
827
+ };
828
+
829
+ // ../../libraries/mcp-tools/dist/tools/getJourneyPatterns.js
830
+ import { z as z18 } from "zod";
831
+ var getJourneyPatternsTool = {
832
+ name: "get_journey_patterns",
833
+ description: "Discover navigation patterns across sessions. Returns top page transitions, backtrack hotspots (pages users return to), and dropoff pages (where sessions end). Use this to understand organic user navigation.",
834
+ inputSchema: z18.object({
835
+ page_path: z18.string().optional().describe("Filter to transitions involving this page (e.g., /pricing)"),
836
+ days: z18.number().optional().default(7).describe("Number of days to analyze (default: 7)")
837
+ }),
838
+ handler: async (args) => {
839
+ const { page_path, days } = args;
840
+ if (!isApiConfigured()) {
841
+ return apiNotConfiguredResult();
842
+ }
843
+ const { data, error } = await apiGet("/flows/patterns", {
844
+ page_path,
845
+ days: days ?? 7
846
+ });
847
+ if (error) {
848
+ return errorResult(error);
849
+ }
850
+ return successResult(data);
851
+ }
852
+ };
853
+
854
+ // ../../libraries/mcp-tools/dist/tools/getPageMetrics.js
855
+ import { z as z19 } from "zod";
856
+ var getPageMetricsTool = {
857
+ name: "get_page_metrics",
858
+ description: "Get aggregated behavioral metrics for a page. Returns frustration, confusion, confidence, energy scores, and health score. If page_path is omitted, returns metrics for all pages.",
859
+ inputSchema: z19.object({
860
+ page_path: z19.string().optional().describe("Page path to get metrics for (e.g., /checkout). Omit to get all pages."),
861
+ days: z19.number().optional().default(7).describe("Number of days to look back (default: 7)")
862
+ }),
863
+ handler: async (args) => {
864
+ const { page_path, days } = args;
865
+ if (!isApiConfigured()) {
866
+ return apiNotConfiguredResult();
867
+ }
868
+ const { data, error } = await apiGet("/pages", {
869
+ page_path,
870
+ days
871
+ });
872
+ if (error) {
873
+ return errorResult(error);
874
+ }
875
+ return successResult(data);
876
+ }
877
+ };
878
+
879
+ // ../../libraries/mcp-tools/dist/tools/getPageTrends.js
880
+ import { z as z20 } from "zod";
881
+ var getPageTrendsTool = {
882
+ name: "get_page_trends",
883
+ description: "Get daily behavioral trends for a page. Shows how frustration, confusion, and confidence change over time.",
884
+ inputSchema: z20.object({
885
+ page_path: z20.string().describe("Page path to analyze (e.g., /checkout)"),
886
+ days: z20.number().optional().default(14).describe("Number of days to look back (default: 14)")
887
+ }),
888
+ handler: async (args) => {
889
+ const { page_path, days } = args;
890
+ if (!isApiConfigured()) {
891
+ return apiNotConfiguredResult();
892
+ }
893
+ const { data, error } = await apiGet("/trends", {
894
+ page_path,
895
+ days: days ?? 14
896
+ });
897
+ if (error) {
898
+ return errorResult(error);
899
+ }
900
+ return successResult(data);
901
+ }
902
+ };
903
+
904
+ // ../../libraries/mcp-tools/dist/tools/getSessionDetails.js
905
+ import { z as z21 } from "zod";
906
+ var getSessionDetailsTool = {
907
+ name: "get_session_details",
908
+ description: "Get behavioral timeline for one or more sessions. Shows how frustration, confusion, and confidence evolved over time. Accepts a single session_id or an array of session_ids (max 20).",
909
+ inputSchema: z21.object({
910
+ session_id: z21.string().optional().describe("Single session ID to get details for"),
911
+ session_ids: z21.array(z21.string()).max(20).optional().describe("Array of session IDs to get details for (max 20)")
912
+ }),
913
+ handler: async (args) => {
914
+ const { session_id, session_ids } = args;
915
+ if (!isApiConfigured()) {
916
+ return apiNotConfiguredResult();
917
+ }
918
+ if (!session_id && (!session_ids || session_ids.length === 0)) {
919
+ return errorResult("Either session_id or session_ids must be provided");
920
+ }
921
+ if (session_id && !session_ids) {
922
+ const { data: data2, error: error2 } = await apiGet(`/sessions/${session_id}`);
923
+ if (error2) {
924
+ return errorResult(error2);
925
+ }
926
+ return successResult(data2);
927
+ }
928
+ const ids = session_ids ?? [session_id];
929
+ const { data, error } = await apiPost("/sessions/details", {
930
+ session_ids: ids
931
+ });
932
+ if (error) {
933
+ return errorResult(error);
934
+ }
935
+ return successResult(data);
936
+ }
937
+ };
938
+
939
+ // ../../libraries/mcp-tools/dist/tools/getSessionPages.js
940
+ import { z as z22 } from "zod";
941
+ var getSessionPagesTool = {
942
+ name: "get_session_pages",
943
+ description: "Get the pages visited within one or more sessions. Returns navigation history with timestamps, source pages, and dwell times. Use this to understand the user's journey through the site during a session. Accepts a single session_id or an array of session_ids (max 20).",
944
+ inputSchema: z22.object({
945
+ session_id: z22.string().optional().describe("Single session ID to get pages for"),
946
+ session_ids: z22.array(z22.string()).max(20).optional().describe("Array of session IDs to get pages for (max 20)")
947
+ }),
948
+ handler: async (args) => {
949
+ const { session_id, session_ids } = args;
950
+ if (!isApiConfigured()) {
951
+ return apiNotConfiguredResult();
952
+ }
953
+ if (!session_id && (!session_ids || session_ids.length === 0)) {
954
+ return errorResult("Either session_id or session_ids must be provided");
955
+ }
956
+ if (session_id && !session_ids) {
957
+ const { data: data2, error: error2 } = await apiGet(`/sessions/${session_id}/pages`);
958
+ if (error2) {
959
+ return errorResult(error2);
960
+ }
961
+ return successResult(data2);
962
+ }
963
+ const ids = session_ids ?? [session_id];
964
+ const { data, error } = await apiPost("/sessions/pages", {
965
+ session_ids: ids
966
+ });
967
+ if (error) {
968
+ return errorResult(error);
969
+ }
970
+ return successResult(data);
971
+ }
972
+ };
973
+
974
+ // ../../libraries/mcp-tools/dist/tools/getUxHealthReport.js
975
+ import { z as z23 } from "zod";
976
+ var getUxHealthReportTool = {
977
+ name: "get_ux_health_report",
978
+ description: "Get a comprehensive UX health report combining page metrics, issues, and anomalies. Returns overall health score, per-page breakdown, active issues, and frustration spikes. Use this as a starting point to understand overall UX state before diving deeper.",
979
+ inputSchema: z23.object({
980
+ page_path: z23.string().optional().describe("Focus on a specific page (omit for site-wide report)"),
981
+ days: z23.number().optional().default(7).describe("Number of days to analyze (default: 7)")
982
+ }),
983
+ handler: async (args) => {
984
+ const { page_path, days } = args;
985
+ if (!isApiConfigured()) {
986
+ return apiNotConfiguredResult();
987
+ }
988
+ const { data, error } = await apiGet("/health-report", {
989
+ page_path,
990
+ days: days ?? 7
991
+ });
992
+ if (error) {
993
+ return errorResult(error);
994
+ }
995
+ return successResult(data);
996
+ }
997
+ };
998
+
999
+ // ../../libraries/mcp-tools/dist/tools/upgradeOptions.js
1000
+ import { z as z24 } from "zod";
1001
+ function formatUpgradeOptions(data) {
1002
+ const lines = [];
1003
+ lines.push("# UPGRADE OPTIONS\n");
1004
+ lines.push(`**Current Plan:** ${data.currentPlan.name} (${data.currentPlan.tag})
1005
+ `);
1006
+ if (data.availableUpgrades.length === 0) {
1007
+ lines.push("You're on the highest tier! No upgrades available.");
1008
+ return lines.join("\n");
1009
+ }
1010
+ lines.push("## Available Upgrades\n");
1011
+ for (const upgrade of data.availableUpgrades) {
1012
+ lines.push(`### ${upgrade.name}
1013
+ `);
1014
+ lines.push("**Features:**");
1015
+ for (const feature of upgrade.features) {
1016
+ lines.push(`- ${feature}`);
1017
+ }
1018
+ lines.push("");
1019
+ lines.push("**Benefits:**");
1020
+ for (const benefit of upgrade.benefits) {
1021
+ lines.push(`- ${benefit}`);
1022
+ }
1023
+ lines.push("");
1024
+ }
1025
+ lines.push("---");
1026
+ lines.push(`**Upgrade now:** ${data.upgradeUrl}`);
1027
+ return lines.join("\n");
1028
+ }
1029
+ var getUpgradeOptionsTool = {
1030
+ name: "get_upgrade_options",
1031
+ description: "Get available upgrade options for the current plan. Use this when a user hits a feature limit or when you need to explain what's available on higher tiers. Returns the current plan, available upgrades with their features and benefits, and a direct link to the billing page. Use this to help users understand the value of upgrading when they encounter gated features.",
1032
+ inputSchema: z24.object({}),
1033
+ handler: async () => {
1034
+ if (!isApiConfigured()) {
1035
+ return apiNotConfiguredResult();
1036
+ }
1037
+ const { data, error } = await apiGet("/upgrade-options");
1038
+ if (error) {
1039
+ return errorResult(error);
1040
+ }
1041
+ const output = formatUpgradeOptions(data);
1042
+ return { content: [{ type: "text", text: output }] };
1043
+ }
1044
+ };
1045
+
1046
+ // ../../libraries/mcp-tools/dist/tools/improvementRun.js
1047
+ import { z as z25 } from "zod";
1048
+ var startImprovementRunTool = {
1049
+ name: "start_improvement_run",
1050
+ description: "Register the start of a self-improvement workflow run. Call this at the beginning of an improvement session to track the run and its outcomes. Returns a run ID to use for subsequent updates.",
1051
+ inputSchema: z25.object({
1052
+ trigger_type: z25.enum(["github_actions", "cron", "manual", "api"]).describe("What triggered this improvement run"),
1053
+ trigger_metadata: z25.record(z25.unknown()).optional().describe("Additional metadata about the trigger (e.g., GitHub run ID, branch, actor)"),
1054
+ phases: z25.array(z25.object({
1055
+ name: z25.string(),
1056
+ status: z25.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending")
1057
+ })).optional().describe("Initial phases to track (e.g., diagnose, investigate, fix)")
1058
+ }),
1059
+ handler: async (args) => {
1060
+ const { trigger_type, trigger_metadata, phases } = args;
1061
+ if (!isApiConfigured()) {
1062
+ return apiNotConfiguredResult();
1063
+ }
1064
+ const { data, error } = await apiPost("/improvement-runs", {
1065
+ trigger_type,
1066
+ trigger_metadata,
1067
+ phases
1068
+ });
1069
+ if (error) {
1070
+ return errorResult(error);
1071
+ }
1072
+ return successResult(data);
1073
+ }
1074
+ };
1075
+ var updateImprovementRunTool = {
1076
+ name: "update_improvement_run",
1077
+ description: "Update an improvement run's status, phases, summary, or diagnostic data. Use to track progress through the workflow phases and record final outcomes.",
1078
+ inputSchema: z25.object({
1079
+ run_id: z25.string().describe("The ID of the improvement run to update"),
1080
+ status: z25.enum(["running", "completed", "failed", "cancelled"]).optional().describe("New status for the run"),
1081
+ title: z25.string().optional().describe("A concise title summarizing what was fixed in this run (e.g., 'Fixed checkout button, improved mobile nav'). Set when completing the run."),
1082
+ phases: z25.array(z25.object({
1083
+ name: z25.string(),
1084
+ status: z25.enum([
1085
+ "pending",
1086
+ "running",
1087
+ "completed",
1088
+ "skipped",
1089
+ "failed"
1090
+ ]),
1091
+ startedAt: z25.string().nullable().optional(),
1092
+ completedAt: z25.string().nullable().optional(),
1093
+ output: z25.record(z25.unknown()).optional()
1094
+ })).optional().describe("Updated phases array"),
1095
+ summary: z25.object({
1096
+ issuesFound: z25.number().optional(),
1097
+ issuesFixed: z25.number().optional(),
1098
+ issuesDeferred: z25.number().optional(),
1099
+ issuesDismissed: z25.number().optional(),
1100
+ prsCreated: z25.number().optional()
1101
+ }).optional().describe("Updated summary counts"),
1102
+ diagnostic: z25.object({
1103
+ healthScore: z25.number().describe("Site health score (0-100)"),
1104
+ totalSessions: z25.number().describe("Number of sessions analyzed"),
1105
+ pagesAnalyzed: z25.number().describe("Number of pages with data"),
1106
+ summary: z25.string().nullable().optional().describe("1-2 sentence summary of the site's current state and key findings")
1107
+ }).optional().describe("Diagnostic data from run_full_diagnostic. Set after diagnosis phase completes."),
1108
+ completed_at: z25.string().optional().describe("ISO timestamp when the run completed"),
1109
+ duration_ms: z25.number().optional().describe("Total duration of the run in milliseconds")
1110
+ }),
1111
+ handler: async (args) => {
1112
+ const { run_id, status, title, phases, summary, diagnostic, completed_at, duration_ms } = args;
1113
+ if (!isApiConfigured()) {
1114
+ return apiNotConfiguredResult();
1115
+ }
1116
+ const { data, error } = await apiPatch(`/improvement-runs/${run_id}`, {
1117
+ status,
1118
+ title,
1119
+ phases,
1120
+ summary,
1121
+ diagnostic,
1122
+ completed_at,
1123
+ duration_ms
1124
+ });
1125
+ if (error) {
1126
+ return errorResult(error);
1127
+ }
1128
+ return successResult(data);
1129
+ }
1130
+ };
1131
+ var recordImprovementActionTool = {
1132
+ name: "record_improvement_action",
1133
+ description: "Record an action taken during an improvement run. Use to track what was done for each issue: code fixes, deferrals, dismissals, etc. IMPORTANT: When recording code_fix actions, the code_changes.diff field must contain ACTUAL SOURCE CODE in unified diff format, not a description of what changed. Include the literal code lines with proper +/- prefixes.",
1134
+ inputSchema: z25.object({
1135
+ run_id: z25.string().describe("The ID of the improvement run"),
1136
+ issue_id: z25.string().optional().describe("The ID of the issue this action relates to (if any)"),
1137
+ action_type: z25.enum([
1138
+ "code_fix",
1139
+ "needs_more_data",
1140
+ "dismissed",
1141
+ "marked_intended",
1142
+ "knowledge_added"
1143
+ ]).describe("Type of action taken"),
1144
+ hypothesis: z25.string().describe("AI's reasoning for this action"),
1145
+ expected_improvement: z25.string().describe("What improvement is expected from this action"),
1146
+ code_changes: z25.array(z25.object({
1147
+ file: z25.string().describe("Path to the changed file"),
1148
+ diff: z25.string().describe("Unified diff format code changes"),
1149
+ startLine: z25.number().describe("Line number where the diff begins"),
1150
+ linesAdded: z25.number().default(0).describe("Count of lines added"),
1151
+ linesRemoved: z25.number().default(0).describe("Count of lines removed")
1152
+ })).optional().describe("Array of code changes with actual source code diffs"),
1153
+ pr_url: z25.string().optional().describe("GitHub PR URL if a PR was created"),
1154
+ pr_number: z25.number().optional().describe("GitHub PR number"),
1155
+ remediation_id: z25.string().optional().describe("ID of the associated remediation record (if any)"),
1156
+ deferral_reason: z25.string().optional().describe("Explanation for why the issue was deferred (for needs_more_data actions)"),
1157
+ dismissal_reason: z25.string().optional().describe("Explanation for why the issue was dismissed (for dismissed/marked_intended actions)"),
1158
+ page_path: z25.string().optional().describe("Page path affected by the fix"),
1159
+ element_selector: z25.string().optional().describe("CSS selector of the affected element"),
1160
+ diagnosis: z25.string().optional().describe("Root cause analysis"),
1161
+ proposed_fix: z25.string().optional().describe("Fix description"),
1162
+ confidence: z25.number().min(0).max(1).optional().describe("Fix confidence 0-1")
1163
+ }),
1164
+ handler: async (args) => {
1165
+ const { run_id, issue_id, action_type, hypothesis, expected_improvement, code_changes, pr_url, pr_number, remediation_id, deferral_reason, dismissal_reason, page_path, element_selector, diagnosis, proposed_fix, confidence } = args;
1166
+ if (!isApiConfigured()) {
1167
+ return apiNotConfiguredResult();
1168
+ }
1169
+ const { data, error } = await apiPost(`/improvement-runs/${run_id}/actions`, {
1170
+ issue_id,
1171
+ action_type,
1172
+ hypothesis,
1173
+ expected_improvement,
1174
+ code_changes,
1175
+ pr_url,
1176
+ pr_number,
1177
+ remediation_id,
1178
+ deferral_reason,
1179
+ dismissal_reason,
1180
+ page_path,
1181
+ element_selector,
1182
+ diagnosis,
1183
+ proposed_fix,
1184
+ confidence
1185
+ });
1186
+ if (error) {
1187
+ return errorResult(error);
1188
+ }
1189
+ return successResult(data);
1190
+ }
1191
+ };
1192
+ var updateImprovementActionTool = {
1193
+ name: "update_improvement_action",
1194
+ description: "Update an existing improvement run action with PR information. Use this after a PR has been created to link it to the action.",
1195
+ inputSchema: z25.object({
1196
+ run_id: z25.string().describe("The ID of the improvement run"),
1197
+ action_id: z25.string().describe("The ID of the action to update"),
1198
+ pr_url: z25.string().optional().describe("GitHub PR URL to link to this action"),
1199
+ pr_number: z25.number().optional().describe("GitHub PR number to link to this action")
1200
+ }),
1201
+ handler: async (args) => {
1202
+ const { run_id, action_id, pr_url, pr_number } = args;
1203
+ if (!isApiConfigured()) {
1204
+ return apiNotConfiguredResult();
1205
+ }
1206
+ const { data, error } = await apiPatch(`/improvement-runs/${run_id}/actions/${action_id}`, {
1207
+ pr_url,
1208
+ pr_number
1209
+ });
1210
+ if (error) {
1211
+ return errorResult(error);
1212
+ }
1213
+ return successResult(data);
1214
+ }
1215
+ };
1216
+ var getImprovementRunTool = {
1217
+ name: "get_improvement_run",
1218
+ description: "Get details of a specific improvement run including all actions taken. Use to review what happened during a run.",
1219
+ inputSchema: z25.object({
1220
+ run_id: z25.string().describe("The ID of the improvement run to retrieve")
1221
+ }),
1222
+ handler: async (args) => {
1223
+ const { run_id } = args;
1224
+ if (!isApiConfigured()) {
1225
+ return apiNotConfiguredResult();
1226
+ }
1227
+ const { data, error } = await apiGet(`/improvement-runs/${run_id}`);
1228
+ if (error) {
1229
+ return errorResult(error);
1230
+ }
1231
+ return successResult(data);
1232
+ }
1233
+ };
1234
+ var listImprovementRunsTool = {
1235
+ name: "list_improvement_runs",
1236
+ description: "List recent improvement runs with optional filters. Use to review past runs and their outcomes.",
1237
+ inputSchema: z25.object({
1238
+ status: z25.enum(["running", "completed", "failed", "cancelled"]).optional().describe("Filter by run status"),
1239
+ trigger_type: z25.enum(["github_actions", "cron", "manual", "api"]).optional().describe("Filter by trigger type"),
1240
+ limit: z25.number().optional().default(20).describe("Maximum number of results (default: 20)"),
1241
+ offset: z25.number().optional().default(0).describe("Offset for pagination")
1242
+ }),
1243
+ handler: async (args) => {
1244
+ const { status, trigger_type, limit, offset } = args;
1245
+ if (!isApiConfigured()) {
1246
+ return apiNotConfiguredResult();
1247
+ }
1248
+ const { data, error } = await apiGet("/improvement-runs", {
1249
+ status,
1250
+ trigger_type,
1251
+ limit: limit ?? 20,
1252
+ offset: offset ?? 0
1253
+ });
1254
+ if (error) {
1255
+ return errorResult(error);
1256
+ }
1257
+ return successResult(data);
1258
+ }
1259
+ };
1260
+
1261
+ // ../../libraries/mcp-tools/dist/tools/knowledge.js
1262
+ import { z as z26 } from "zod";
1263
+ var KNOWLEDGE_CATEGORIES = [
1264
+ "false_positive",
1265
+ "intended_behavior",
1266
+ "fix_pattern",
1267
+ "context"
1268
+ ];
1269
+ var ISSUE_CATEGORIES = [
1270
+ "code_error",
1271
+ "dead_click",
1272
+ "rage_click",
1273
+ "ux_friction",
1274
+ "performance",
1275
+ "form_issue",
1276
+ "behavioral_anomaly",
1277
+ "structural_issue"
1278
+ ];
1279
+ var getSiteKnowledgeTool = {
1280
+ name: "get_site_knowledge",
1281
+ description: "Retrieve site-specific learnings including known false positives, intended behaviors, and successful fix patterns. Use at the start of a self-improvement workflow to avoid re-flagging known issues.",
1282
+ inputSchema: z26.object({
1283
+ category: z26.enum(KNOWLEDGE_CATEGORIES).optional().describe("Filter by knowledge category"),
1284
+ page_path: z26.string().optional().describe("Filter by page path"),
1285
+ issue_category: z26.enum(ISSUE_CATEGORIES).optional().describe("Filter by issue category"),
1286
+ limit: z26.number().optional().default(50).describe("Maximum number of entries to return (default: 50)")
1287
+ }),
1288
+ handler: async (args) => {
1289
+ const { category, page_path, issue_category, limit } = args;
1290
+ if (!isApiConfigured()) {
1291
+ return apiNotConfiguredResult();
1292
+ }
1293
+ const { data, error } = await apiGet("/knowledge", {
1294
+ category,
1295
+ page_path,
1296
+ issue_category,
1297
+ limit: limit ?? 50
1298
+ });
1299
+ if (error) {
1300
+ return errorResult(error);
1301
+ }
1302
+ return successResult(data);
1303
+ }
1304
+ };
1305
+ var addSiteKnowledgeTool = {
1306
+ name: "add_site_knowledge",
1307
+ description: "Store a site-specific learning. Use to record false positives, intended behaviors, successful fix patterns, or important context about the site.",
1308
+ inputSchema: z26.object({
1309
+ category: z26.enum(KNOWLEDGE_CATEGORIES).describe("Category of knowledge: false_positive, intended_behavior, fix_pattern, or context"),
1310
+ subject: z26.string().describe("Unique identifier/title for this knowledge entry"),
1311
+ content: z26.string().describe("Detailed description of the learning"),
1312
+ page_path: z26.string().optional().describe("Associated page path (if applicable)"),
1313
+ element_selector: z26.string().optional().describe("Associated element selector (if applicable)"),
1314
+ issue_category: z26.enum(ISSUE_CATEGORIES).optional().describe("Associated issue category (if applicable)")
1315
+ }),
1316
+ handler: async (args) => {
1317
+ const { category, subject, content, page_path, element_selector, issue_category } = args;
1318
+ if (!isApiConfigured()) {
1319
+ return apiNotConfiguredResult();
1320
+ }
1321
+ const { data, error } = await apiPost("/knowledge", {
1322
+ category,
1323
+ subject,
1324
+ content,
1325
+ page_path,
1326
+ element_selector,
1327
+ issue_category,
1328
+ source: "agent"
1329
+ });
1330
+ if (error) {
1331
+ return errorResult(error);
1332
+ }
1333
+ return successResult(data);
1334
+ }
1335
+ };
1336
+ var deleteSiteKnowledgeTool = {
1337
+ name: "delete_site_knowledge",
1338
+ description: "Delete a site knowledge entry. Use when a learning is no longer valid or was created in error.",
1339
+ inputSchema: z26.object({
1340
+ knowledge_id: z26.string().describe("The ID of the knowledge entry to delete")
1341
+ }),
1342
+ handler: async (args) => {
1343
+ const { knowledge_id } = args;
1344
+ if (!isApiConfigured()) {
1345
+ return apiNotConfiguredResult();
1346
+ }
1347
+ const { data, error } = await apiDelete(`/knowledge/${knowledge_id}`);
1348
+ if (error) {
1349
+ return errorResult(error);
1350
+ }
1351
+ return successResult(data);
1352
+ }
1353
+ };
1354
+ var getSimilarFixesTool = {
1355
+ name: "get_similar_fixes",
1356
+ description: "Find past fixes for similar issues. Returns successful and failed remediation attempts for issues with the same category, page, or element. Use before proposing a fix to learn from past attempts.",
1357
+ inputSchema: z26.object({
1358
+ issue_id: z26.string().describe("The ID of the issue to find similar fixes for"),
1359
+ limit: z26.number().optional().default(5).describe("Maximum number of similar fixes to return (default: 5)")
1360
+ }),
1361
+ handler: async (args) => {
1362
+ const { issue_id, limit } = args;
1363
+ if (!isApiConfigured()) {
1364
+ return apiNotConfiguredResult();
1365
+ }
1366
+ const { data, error } = await apiGet("/knowledge/similar-fixes", {
1367
+ issue_id,
1368
+ limit: limit ?? 5
1369
+ });
1370
+ if (error) {
1371
+ return errorResult(error);
1372
+ }
1373
+ return successResult(data);
1374
+ }
1375
+ };
1376
+
1377
+ // ../../libraries/mcp-tools/dist/tools/listPages.js
1378
+ import { z as z27 } from "zod";
1379
+ var listPagesTool = {
1380
+ name: "list_pages",
1381
+ description: "List all tracked pages with basic metrics. Returns page paths sorted by session count with frustration and health grade. Use this to discover what pages are being tracked before diving into specific pages.",
1382
+ inputSchema: z27.object({
1383
+ days: z27.number().optional().default(7).describe("Number of days to look back (default 7)"),
1384
+ limit: z27.number().optional().default(50).describe("Maximum number of pages to return (default 50)")
1385
+ }),
1386
+ handler: async (args) => {
1387
+ const { days, limit } = args;
1388
+ if (!isApiConfigured()) {
1389
+ return apiNotConfiguredResult();
1390
+ }
1391
+ const { data, error } = await apiGet("/pages/list", {
1392
+ days: days ?? 7,
1393
+ limit: limit ?? 50
1394
+ });
1395
+ if (error) {
1396
+ return errorResult(error);
1397
+ }
1398
+ return successResult(data);
1399
+ }
1400
+ };
1401
+
1402
+ // ../../libraries/mcp-tools/dist/tools/listSessions.js
1403
+ import { z as z28 } from "zod";
1404
+ var listSessionsTool = {
1405
+ name: "list_sessions",
1406
+ description: "List and filter recorded sessions. Returns session metadata including status, duration, device, browser, and domains. Use to find sessions for a domain, check for active sessions, or browse by device type.",
1407
+ inputSchema: z28.object({
1408
+ domain: z28.string().optional().describe("Filter by domain name (partial match supported)"),
1409
+ status: z28.enum(["active", "terminated", "all"]).optional().default("all").describe("Filter by session status"),
1410
+ device: z28.enum(["desktop", "mobile", "tablet"]).optional().describe("Filter by device type"),
1411
+ days: z28.number().optional().default(7).describe("Number of days to look back (default: 7)"),
1412
+ limit: z28.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 200)")
1413
+ }),
1414
+ handler: async (args) => {
1415
+ const { domain, status, device, days, limit } = args;
1416
+ if (!isApiConfigured()) {
1417
+ return apiNotConfiguredResult();
1418
+ }
1419
+ const { data, error } = await apiGet("/sessions", {
1420
+ domain,
1421
+ status: status === "all" ? void 0 : status,
1422
+ device,
1423
+ days: days ?? 7,
1424
+ limit: limit ?? 20
1425
+ });
1426
+ if (error) {
1427
+ return errorResult(error);
1428
+ }
1429
+ return successResult(data);
1430
+ }
1431
+ };
1432
+
1433
+ // ../../libraries/mcp-tools/dist/tools/predictOutcomes.js
1434
+ import { z as z29 } from "zod";
1435
+ var predictOutcomesTool = {
1436
+ name: "predict_outcomes",
1437
+ description: "Predict session outcomes based on early behavioral signals. Provide either a session_id to analyze an existing session, or behavioral_features to predict outcomes for hypothetical scenarios. Returns predicted outcome (COMPLETED, STRUGGLED, BLOCKED, DISENGAGED) with confidence.",
1438
+ inputSchema: z29.object({
1439
+ session_id: z29.string().optional().describe("Session ID to analyze and predict outcome for"),
1440
+ behavioral_features: z29.object({
1441
+ frustration: z29.number().describe("Frustration score (0-1)"),
1442
+ confusion: z29.number().describe("Confusion score (0-1)"),
1443
+ confidence: z29.number().describe("Confidence score (0-1)")
1444
+ }).optional().describe("Behavioral features to use for prediction")
1445
+ }),
1446
+ handler: async (args) => {
1447
+ const { session_id, behavioral_features } = args;
1448
+ if (!isApiConfigured()) {
1449
+ return apiNotConfiguredResult();
1450
+ }
1451
+ if (!session_id && !behavioral_features) {
1452
+ return errorResult("Either session_id or behavioral_features is required");
1453
+ }
1454
+ const { data, error } = await apiPost("/predict", {
1455
+ session_id,
1456
+ behavioral_features
1457
+ });
1458
+ if (error) {
1459
+ return errorResult(error);
1460
+ }
1461
+ return successResult(data);
1462
+ }
1463
+ };
1464
+
1465
+ // ../../libraries/mcp-tools/dist/tools/remediation.js
1466
+ import { z as z30 } from "zod";
1467
+ var proposeFixTool = {
1468
+ name: "propose_fix",
1469
+ description: "Propose a fix for an issue. Creates a remediation record with baseline metrics for later evaluation. Include detailed diagnosis and code suggestions when possible.",
1470
+ inputSchema: z30.object({
1471
+ issue_id: z30.string().describe("The ID of the issue to fix"),
1472
+ diagnosis: z30.string().describe("Detailed analysis of the root cause of the issue"),
1473
+ proposed_fix: z30.string().describe("Description of the proposed fix and how it addresses the root cause"),
1474
+ code_snippet: z30.string().optional().describe("Suggested code changes (if applicable)"),
1475
+ affected_files: z30.array(z30.string()).optional().describe("List of files that need to be modified"),
1476
+ confidence: z30.number().min(0).max(1).describe("Confidence level in the fix (0-1). Use <0.7 when uncertain.")
1477
+ }),
1478
+ handler: async (args) => {
1479
+ const { issue_id, diagnosis, proposed_fix, code_snippet, affected_files, confidence } = args;
1480
+ if (!isApiConfigured()) {
1481
+ return apiNotConfiguredResult();
1482
+ }
1483
+ const { data, error } = await apiPost("/remediations", {
1484
+ issue_id,
1485
+ diagnosis,
1486
+ proposed_fix,
1487
+ code_snippet,
1488
+ affected_files,
1489
+ confidence
1490
+ });
1491
+ if (error) {
1492
+ return errorResult(error);
1493
+ }
1494
+ return successResult(data);
1495
+ }
1496
+ };
1497
+ var listPendingFixesTool = {
1498
+ name: "list_pending_fixes",
1499
+ description: "List all proposed fixes awaiting deployment. Use to show the user what fixes are ready to be deployed.",
1500
+ inputSchema: z30.object({}),
1501
+ handler: async () => {
1502
+ if (!isApiConfigured()) {
1503
+ return apiNotConfiguredResult();
1504
+ }
1505
+ const { data, error } = await apiGet("/remediations/pending");
1506
+ if (error) {
1507
+ return errorResult(error);
1508
+ }
1509
+ return successResult(data);
1510
+ }
1511
+ };
1512
+ var listRemediationsTool = {
1513
+ name: "list_remediations",
1514
+ description: "List remediation records with optional filters. Use to review past and current fix attempts.",
1515
+ inputSchema: z30.object({
1516
+ status: z30.enum([
1517
+ "proposed",
1518
+ "waiting",
1519
+ "dismissed",
1520
+ "deployed",
1521
+ "evaluating",
1522
+ "succeeded",
1523
+ "failed",
1524
+ "reverted"
1525
+ ]).optional().describe("Filter by remediation status"),
1526
+ issue_id: z30.string().optional().describe("Filter by issue ID"),
1527
+ page_path: z30.string().optional().describe("Filter by page path"),
1528
+ limit: z30.number().optional().default(20).describe("Maximum number of results (default: 20)")
1529
+ }),
1530
+ handler: async (args) => {
1531
+ const { status, issue_id, page_path, limit } = args;
1532
+ if (!isApiConfigured()) {
1533
+ return apiNotConfiguredResult();
1534
+ }
1535
+ const { data, error } = await apiGet("/remediations", {
1536
+ status,
1537
+ issue_id,
1538
+ page_path,
1539
+ limit: limit ?? 20
1540
+ });
1541
+ if (error) {
1542
+ return errorResult(error);
1543
+ }
1544
+ return successResult(data);
1545
+ }
1546
+ };
1547
+ var confirmDeploymentTool = {
1548
+ name: "confirm_deployment",
1549
+ description: "Confirm that a proposed fix has been deployed to production. This starts the evaluation timer. Call this when the user confirms they have deployed the fix.",
1550
+ inputSchema: z30.object({
1551
+ remediation_id: z30.string().describe("The ID of the remediation to mark as deployed")
1552
+ }),
1553
+ handler: async (args) => {
1554
+ const { remediation_id } = args;
1555
+ if (!isApiConfigured()) {
1556
+ return apiNotConfiguredResult();
1557
+ }
1558
+ const { data, error } = await apiPatch(`/remediations/${remediation_id}/deploy`, {});
1559
+ if (error) {
1560
+ return errorResult(error);
1561
+ }
1562
+ return successResult(data);
1563
+ }
1564
+ };
1565
+ var evaluateFixTool = {
1566
+ name: "evaluate_fix",
1567
+ description: "Evaluate if a deployed fix improved metrics. Compares post-deployment metrics to baseline. Returns success/partial/failed outcome with detailed verdict. Wait at least 24 hours after deployment for reliable results.",
1568
+ inputSchema: z30.object({
1569
+ remediation_id: z30.string().describe("The ID of the remediation to evaluate"),
1570
+ min_hours: z30.number().optional().default(24).describe("Minimum hours since deployment required (default: 24)")
1571
+ }),
1572
+ handler: async (args) => {
1573
+ const { remediation_id, min_hours } = args;
1574
+ if (!isApiConfigured()) {
1575
+ return apiNotConfiguredResult();
1576
+ }
1577
+ const { data, error } = await apiPost(`/remediations/${remediation_id}/evaluate`, { min_hours: min_hours ?? 24 });
1578
+ if (error) {
1579
+ return errorResult(error);
1580
+ }
1581
+ return successResult(data);
1582
+ }
1583
+ };
1584
+ var getFixHistoryTool = {
1585
+ name: "get_fix_history",
1586
+ description: "Get the full remediation history for an issue. Shows all fix attempts, their outcomes, and current status. Use before proposing a new fix to learn from past attempts.",
1587
+ inputSchema: z30.object({
1588
+ issue_id: z30.string().describe("The ID of the issue to get fix history for")
1589
+ }),
1590
+ handler: async (args) => {
1591
+ const { issue_id } = args;
1592
+ if (!isApiConfigured()) {
1593
+ return apiNotConfiguredResult();
1594
+ }
1595
+ const { data, error } = await apiGet(`/remediations/history/${issue_id}`);
1596
+ if (error) {
1597
+ return errorResult(error);
1598
+ }
1599
+ return successResult(data);
1600
+ }
1601
+ };
1602
+ var updateRemediationStatusTool = {
1603
+ name: "update_remediation_status",
1604
+ description: "Update the status of a remediation based on PR events. Use to mark a fix as 'waiting' (PR created), 'deployed' (PR merged), or 'dismissed' (PR closed without merge).",
1605
+ inputSchema: z30.object({
1606
+ remediation_id: z30.string().describe("The ID of the remediation to update"),
1607
+ status: z30.enum(["waiting", "deployed", "dismissed"]).describe("New status: 'waiting' = PR open, 'deployed' = PR merged, 'dismissed' = PR closed without merge"),
1608
+ pr_url: z30.string().optional().describe("URL of the pull request"),
1609
+ pr_number: z30.number().optional().describe("PR number"),
1610
+ pr_merged_at: z30.string().optional().describe("ISO timestamp when PR was merged"),
1611
+ pr_closed_at: z30.string().optional().describe("ISO timestamp when PR was closed")
1612
+ }),
1613
+ handler: async (args) => {
1614
+ const { remediation_id, status, pr_url, pr_number, pr_merged_at, pr_closed_at } = args;
1615
+ if (!isApiConfigured()) {
1616
+ return apiNotConfiguredResult();
1617
+ }
1618
+ const { data, error } = await apiPatch(`/remediations/${remediation_id}/status`, {
1619
+ status,
1620
+ pr_url,
1621
+ pr_number,
1622
+ pr_merged_at,
1623
+ pr_closed_at
1624
+ });
1625
+ if (error) {
1626
+ return errorResult(error);
1627
+ }
1628
+ return successResult(data);
1629
+ }
1630
+ };
1631
+ var listRemediationsByStatusTool = {
1632
+ name: "list_remediations_by_status",
1633
+ description: "List remediations filtered by one or more statuses. Use to find all 'waiting' PRs or 'deployed' fixes ready for evaluation.",
1634
+ inputSchema: z30.object({
1635
+ statuses: z30.array(z30.enum([
1636
+ "proposed",
1637
+ "waiting",
1638
+ "dismissed",
1639
+ "deployed",
1640
+ "evaluating",
1641
+ "succeeded",
1642
+ "failed",
1643
+ "reverted"
1644
+ ])).describe("List of statuses to filter by")
1645
+ }),
1646
+ handler: async (args) => {
1647
+ const { statuses } = args;
1648
+ if (!isApiConfigured()) {
1649
+ return apiNotConfiguredResult();
1650
+ }
1651
+ const { data, error } = await apiGet(`/remediations/by-status`, {
1652
+ statuses: statuses.join(",")
1653
+ });
1654
+ if (error) {
1655
+ return errorResult(error);
1656
+ }
1657
+ return successResult(data);
1658
+ }
1659
+ };
1660
+ var getRemediationByPrTool = {
1661
+ name: "get_remediation_by_pr",
1662
+ description: "Get a remediation by its PR number. Use to look up fix details when processing PR events.",
1663
+ inputSchema: z30.object({
1664
+ pr_number: z30.number().describe("The PR number to look up")
1665
+ }),
1666
+ handler: async (args) => {
1667
+ const { pr_number } = args;
1668
+ if (!isApiConfigured()) {
1669
+ return apiNotConfiguredResult();
1670
+ }
1671
+ const { data, error } = await apiGet(`/remediations/by-pr/${pr_number}`);
1672
+ if (error) {
1673
+ return errorResult(error);
1674
+ }
1675
+ return successResult(data);
1676
+ }
1677
+ };
1678
+
1679
+ // ../../libraries/mcp-tools/dist/tools/scanSite.js
1680
+ import { z as z31 } from "zod";
1681
+ var scanSiteTool = {
1682
+ name: "scan_site",
1683
+ description: "One-shot site health scan. Lists all pages and computes aggregate metrics for the top-N pages (default 10) sorted by session count. Returns a ranked table with frustration and health grade per page. Use this as the FIRST tool when the user asks a broad question without naming a specific page.",
1684
+ inputSchema: z31.object({
1685
+ top_n: z31.number().optional().default(10).describe("Number of top pages to analyze (default 10)"),
1686
+ offset: z31.number().optional().default(0).describe("Skip first N pages for pagination (default 0)")
1687
+ }),
1688
+ handler: async (args) => {
1689
+ const { top_n, offset } = args;
1690
+ if (!isApiConfigured()) {
1691
+ return apiNotConfiguredResult();
1692
+ }
1693
+ const { data, error } = await apiGet("/site/overview", {
1694
+ top_n: top_n ?? 10,
1695
+ offset: offset ?? 0
1696
+ });
1697
+ if (error) {
1698
+ return errorResult(error);
1699
+ }
1700
+ return successResult(data);
1701
+ }
1702
+ };
1703
+
1704
+ // ../../libraries/mcp-tools/dist/tools/searchSessions.js
1705
+ import { z as z32 } from "zod";
1706
+ var searchSessionsTool = {
1707
+ name: "search_sessions",
1708
+ description: 'Search sessions by natural language query. Examples: "frustrated users on checkout", "rage clicks on pricing page", "confused users who abandoned cart".',
1709
+ inputSchema: z32.object({
1710
+ query: z32.string().describe("Natural language search query describing the sessions you want to find"),
1711
+ page_path: z32.string().optional().describe("Optional: filter to a specific page path"),
1712
+ limit: z32.number().optional().default(10).describe("Maximum number of sessions to return (default: 10)")
1713
+ }),
1714
+ handler: async (args) => {
1715
+ const { query, page_path, limit } = args;
1716
+ if (!isApiConfigured()) {
1717
+ return apiNotConfiguredResult();
1718
+ }
1719
+ const { data, error } = await apiPost("/sessions/search", {
1720
+ query,
1721
+ page_path,
1722
+ limit: limit ?? 10
1723
+ });
1724
+ if (error) {
1725
+ return errorResult(error);
1726
+ }
1727
+ return successResult(data);
1728
+ }
1729
+ };
1730
+
1731
+ // ../../libraries/mcp-tools/dist/tools/triage.js
1732
+ import { z as z33 } from "zod";
1733
+ var DISMISS_REASONS = [
1734
+ "stale",
1735
+ "intended",
1736
+ "duplicate",
1737
+ "false_positive"
1738
+ ];
1739
+ var dismissIssueTool = {
1740
+ name: "dismiss_issue",
1741
+ description: "Dismiss an issue as a false positive. Creates site knowledge to prevent re-flagging. Use when an issue is stale, intended behavior, a duplicate, or a false positive. Always confirm with user before dismissing.",
1742
+ inputSchema: z33.object({
1743
+ issue_id: z33.string().describe("The ID of the issue to dismiss"),
1744
+ reason: z33.enum(DISMISS_REASONS).describe("Reason for dismissal: stale, intended, duplicate, or false_positive"),
1745
+ explanation: z33.string().describe("Detailed explanation of why this issue is being dismissed"),
1746
+ create_knowledge: z33.boolean().optional().default(true).describe("Whether to create site knowledge entry (default: true)")
1747
+ }),
1748
+ handler: async (args) => {
1749
+ const { issue_id, reason, explanation, create_knowledge } = args;
1750
+ if (!isApiConfigured()) {
1751
+ return apiNotConfiguredResult();
1752
+ }
1753
+ const { data, error } = await apiPost(`/issues/${issue_id}/dismiss`, {
1754
+ reason,
1755
+ explanation,
1756
+ create_knowledge: create_knowledge ?? true
1757
+ });
1758
+ if (error) {
1759
+ return errorResult(error);
1760
+ }
1761
+ return successResult(data);
1762
+ }
1763
+ };
1764
+ var markIntendedBehaviorTool = {
1765
+ name: "mark_intended_behavior",
1766
+ description: "Mark an issue as intended behavior. This is a shortcut for dismissing with reason 'intended' and always creates site knowledge. Use when the detected behavior is by design (e.g., disabled button clicks are expected).",
1767
+ inputSchema: z33.object({
1768
+ issue_id: z33.string().describe("The ID of the issue to mark as intended"),
1769
+ explanation: z33.string().describe("Explanation of why this behavior is intended")
1770
+ }),
1771
+ handler: async (args) => {
1772
+ const { issue_id, explanation } = args;
1773
+ if (!isApiConfigured()) {
1774
+ return apiNotConfiguredResult();
1775
+ }
1776
+ const { data, error } = await apiPost(`/issues/${issue_id}/mark-intended`, {
1777
+ explanation
1778
+ });
1779
+ if (error) {
1780
+ return errorResult(error);
1781
+ }
1782
+ return successResult(data);
1783
+ }
1784
+ };
1785
+ var getIssueHistoryTool = {
1786
+ name: "get_issue_history",
1787
+ description: "Get the full history of an issue including status changes, remediation attempts, and outcomes. Use to understand past fix attempts before proposing new ones.",
1788
+ inputSchema: z33.object({
1789
+ issue_id: z33.string().describe("The ID of the issue to get history for")
1790
+ }),
1791
+ handler: async (args) => {
1792
+ const { issue_id } = args;
1793
+ if (!isApiConfigured()) {
1794
+ return apiNotConfiguredResult();
1795
+ }
1796
+ const { data, error } = await apiGet(`/issues/${issue_id}/history`);
1797
+ if (error) {
1798
+ return errorResult(error);
1799
+ }
1800
+ return successResult(data);
1801
+ }
1802
+ };
1803
+
1804
+ // ../../libraries/mcp-tools/dist/tools/triageSessions.js
1805
+ import { z as z34 } from "zod";
1806
+ function formatDuration(seconds) {
1807
+ if (!seconds)
1808
+ return "unknown";
1809
+ if (seconds < 60)
1810
+ return `${seconds}s`;
1811
+ if (seconds < 3600)
1812
+ return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
1813
+ const hours = Math.floor(seconds / 3600);
1814
+ const mins = Math.floor(seconds % 3600 / 60);
1815
+ return `${hours}h ${mins}m`;
1816
+ }
1817
+ function getActionReason(session) {
1818
+ const reasons = [];
1819
+ if (session.user_comments.length > 0) {
1820
+ reasons.push(`USER FEEDBACK: ${session.user_comments.length} comment(s) - "${session.user_comments[0].text.slice(0, 100)}${session.user_comments[0].text.length > 100 ? "..." : ""}"`);
1821
+ }
1822
+ if (session.behavioral_signals.frustration >= 0.5) {
1823
+ reasons.push(`HIGH FRUSTRATION: ${(session.behavioral_signals.frustration * 100).toFixed(0)}%`);
1824
+ } else if (session.behavioral_signals.frustration >= 0.3) {
1825
+ reasons.push(`MODERATE FRUSTRATION: ${(session.behavioral_signals.frustration * 100).toFixed(0)}%`);
1826
+ }
1827
+ if (session.behavioral_signals.has_rage_clicks) {
1828
+ reasons.push("RAGE CLICKS detected");
1829
+ }
1830
+ if (session.console_errors.length > 0) {
1831
+ const errorCount = session.console_errors.reduce((sum, e) => sum + e.count, 0);
1832
+ reasons.push(`JS ERRORS: ${errorCount} error(s) - "${session.console_errors[0].message.slice(0, 80)}..."`);
1833
+ }
1834
+ if (!session.behavioral_signals.has_behavioral_data) {
1835
+ reasons.push("NOTE: No behavioral analysis data available for this session");
1836
+ }
1837
+ if (reasons.length === 0) {
1838
+ reasons.push("Low severity - no major issues detected");
1839
+ }
1840
+ return reasons.join("\n - ");
1841
+ }
1842
+ function formatTriageOutput(data) {
1843
+ const lines = [];
1844
+ lines.push("# SESSION TRIAGE RESULTS\n");
1845
+ lines.push(`Total sessions analyzed: ${data.summary.total_sessions}`);
1846
+ lines.push(`Sessions with issues: ${data.summary.flagged_sessions}`);
1847
+ lines.push(`Sessions with user comments: ${data.summary.sessions_with_comments}`);
1848
+ if (data.sessions.length === 0) {
1849
+ lines.push("\nNo sessions require attention.");
1850
+ return lines.join("\n");
1851
+ }
1852
+ const critical = data.sessions.filter((s) => s.severity === "critical");
1853
+ const high = data.sessions.filter((s) => s.severity === "high");
1854
+ const medium = data.sessions.filter((s) => s.severity === "medium");
1855
+ const low = data.sessions.filter((s) => s.severity === "low");
1856
+ lines.push("\n## PRIORITY SUMMARY");
1857
+ lines.push(`- CRITICAL: ${critical.length} session(s) - investigate immediately`);
1858
+ lines.push(`- HIGH: ${high.length} session(s) - investigate soon`);
1859
+ lines.push(`- MEDIUM: ${medium.length} session(s) - review when possible`);
1860
+ lines.push(`- LOW: ${low.length} session(s) - likely okay`);
1861
+ lines.push("\n## RECOMMENDED ACTIONS\n");
1862
+ if (critical.length > 0) {
1863
+ lines.push("### CRITICAL - Investigate Immediately\n");
1864
+ for (const s of critical) {
1865
+ lines.push(`**Session ${s.session_id.slice(-8)}** (${formatDuration(s.duration_seconds)}, ${s.device})`);
1866
+ lines.push(` Replay: ${s.replay_url}`);
1867
+ lines.push(` Score: ${(s.triage_score * 100).toFixed(0)}%`);
1868
+ lines.push(` Why: ${getActionReason(s)}`);
1869
+ if (s.pages_visited.length > 0) {
1870
+ lines.push(` Pages: ${s.pages_visited.slice(0, 3).join(", ")}${s.pages_visited.length > 3 ? ` (+${s.pages_visited.length - 3} more)` : ""}`);
1871
+ }
1872
+ lines.push("");
1873
+ }
1874
+ }
1875
+ if (high.length > 0) {
1876
+ lines.push("### HIGH - Investigate Soon\n");
1877
+ for (const s of high) {
1878
+ lines.push(`**Session ${s.session_id.slice(-8)}** (${formatDuration(s.duration_seconds)}, ${s.device})`);
1879
+ lines.push(` Replay: ${s.replay_url}`);
1880
+ lines.push(` Score: ${(s.triage_score * 100).toFixed(0)}%`);
1881
+ lines.push(` Why: ${getActionReason(s)}`);
1882
+ if (s.pages_visited.length > 0) {
1883
+ lines.push(` Pages: ${s.pages_visited.slice(0, 3).join(", ")}${s.pages_visited.length > 3 ? ` (+${s.pages_visited.length - 3} more)` : ""}`);
1884
+ }
1885
+ lines.push("");
1886
+ }
1887
+ }
1888
+ if (medium.length > 0) {
1889
+ lines.push("### MEDIUM - Review When Possible\n");
1890
+ for (const s of medium) {
1891
+ lines.push(`- Session ${s.session_id.slice(-8)} (${formatDuration(s.duration_seconds)}): ${getActionReason(s).split("\n")[0]}`);
1892
+ lines.push(` ${s.replay_url}`);
1893
+ }
1894
+ lines.push("");
1895
+ }
1896
+ if (low.length > 0) {
1897
+ lines.push("### LOW - Likely Okay\n");
1898
+ lines.push(`${low.length} session(s) with minor or no issues detected.`);
1899
+ if (low.length <= 3) {
1900
+ for (const s of low) {
1901
+ lines.push(`- Session ${s.session_id.slice(-8)}: ${s.replay_url}`);
1902
+ }
1903
+ }
1904
+ lines.push("");
1905
+ }
1906
+ lines.push("\n## RAW DATA (for detailed analysis)\n");
1907
+ lines.push("```json");
1908
+ lines.push(JSON.stringify({
1909
+ summary: data.summary,
1910
+ sessions: data.sessions.map((s) => ({
1911
+ id: s.session_id,
1912
+ severity: s.severity,
1913
+ score: s.triage_score,
1914
+ url: s.replay_url,
1915
+ comments: s.user_comments.length,
1916
+ frustration: s.behavioral_signals.frustration,
1917
+ rage_clicks: s.behavioral_signals.has_rage_clicks,
1918
+ errors: s.console_errors.length,
1919
+ has_data: s.behavioral_signals.has_behavioral_data
1920
+ }))
1921
+ }, null, 2));
1922
+ lines.push("```");
1923
+ return lines.join("\n");
1924
+ }
1925
+ var triageSessionsTool = {
1926
+ name: "triage_sessions",
1927
+ description: "Automatically triage sessions to find compromised user experiences. Analyzes user comments, frustration signals, rage clicks, and console errors. Returns flagged sessions with evidence and replay links. Use this to identify sessions that need attention without manually reviewing every recording. Can triage specific sessions by ID(s) or scan for problematic sessions. NOTE: Session replay URLs require a paid plan (Starter+). Free tier users can see session summaries and triage scores but not watch replays.",
1928
+ inputSchema: z34.object({
1929
+ session_ids: z34.array(z34.string()).optional().describe("Triage specific sessions by ID. If provided, other filters are ignored."),
1930
+ days: z34.number().optional().default(7).describe("Lookback period in days (default: 7). Ignored if session_ids is provided."),
1931
+ page_path: z34.string().optional().describe("Filter to sessions that visited a specific page"),
1932
+ min_severity: z34.enum(["critical", "high", "medium", "low"]).optional().describe("Minimum severity to include in results"),
1933
+ limit: z34.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 100)")
1934
+ }),
1935
+ handler: async (args) => {
1936
+ const { session_ids, days, page_path, min_severity, limit } = args;
1937
+ if (!isApiConfigured()) {
1938
+ return apiNotConfiguredResult();
1939
+ }
1940
+ const { data, error } = await apiGet("/sessions/triage", {
1941
+ session_ids: session_ids?.join(","),
1942
+ days: session_ids?.length ? void 0 : days ?? 7,
1943
+ page_path: session_ids?.length ? void 0 : page_path,
1944
+ min_severity,
1945
+ limit: session_ids?.length ? session_ids.length : limit ?? 20
1946
+ });
1947
+ if (error) {
1948
+ return errorResult(error);
1949
+ }
1950
+ const formattedOutput = formatTriageOutput(data);
1951
+ return { content: [{ type: "text", text: formattedOutput }] };
1952
+ }
1953
+ };
1954
+
1955
+ // ../../libraries/mcp-tools/dist/tools/proposal.js
1956
+ import { z as z35 } from "zod";
1957
+ var ISSUE_CATEGORIES2 = [
1958
+ "code_error",
1959
+ "dead_click",
1960
+ "rage_click",
1961
+ "ux_friction",
1962
+ "performance",
1963
+ "form_issue",
1964
+ "behavioral_anomaly",
1965
+ "structural_issue"
1966
+ ];
1967
+ var ISSUE_SEVERITIES = ["critical", "high", "medium", "low", "info"];
1968
+ var PROPOSAL_STATUSES = [
1969
+ "pending",
1970
+ "resolved",
1971
+ "dismissed",
1972
+ "evaluating",
1973
+ "succeeded",
1974
+ "failed"
1975
+ ];
1976
+ var createProposalTool = {
1977
+ name: "create_proposal",
1978
+ description: "Create a fix proposal for audit mode. Proposals are suggestions that developers review and implement manually. Use this in audit mode instead of propose_fix when you cannot make code changes directly.",
1979
+ inputSchema: z35.object({
1980
+ issue_id: z35.string().optional().describe("The ID of the related issue (if any)"),
1981
+ improvement_run_id: z35.string().optional().describe("The ID of the improvement run creating this proposal"),
1982
+ title: z35.string().describe("Short title describing the proposed fix"),
1983
+ diagnosis: z35.string().describe("Detailed analysis of the root cause of the issue"),
1984
+ proposed_fix: z35.string().describe("Description of the proposed fix and how it addresses the root cause"),
1985
+ affected_files: z35.array(z35.string()).optional().describe("List of files that likely need to be modified"),
1986
+ confidence: z35.number().min(0).max(1).describe("Confidence level in the fix (0-1). Use <0.7 when uncertain."),
1987
+ page_path: z35.string().describe("The page path where the issue occurs"),
1988
+ element_selector: z35.string().optional().describe("CSS selector of the affected element (if applicable)"),
1989
+ category: z35.enum(ISSUE_CATEGORIES2).describe("Category of the issue being addressed"),
1990
+ severity: z35.enum(ISSUE_SEVERITIES).describe("Severity of the issue being addressed")
1991
+ }),
1992
+ handler: async (args) => {
1993
+ const { issue_id, improvement_run_id, title, diagnosis, proposed_fix, affected_files, confidence, page_path, element_selector, category, severity } = args;
1994
+ if (!isApiConfigured()) {
1995
+ return apiNotConfiguredResult();
1996
+ }
1997
+ const { data, error } = await apiPost("/proposals", {
1998
+ issue_id,
1999
+ improvement_run_id,
2000
+ title,
2001
+ diagnosis,
2002
+ proposed_fix,
2003
+ affected_files,
2004
+ confidence,
2005
+ page_path,
2006
+ element_selector,
2007
+ category,
2008
+ severity
2009
+ });
2010
+ if (error) {
2011
+ return errorResult(error);
2012
+ }
2013
+ return successResult(data);
2014
+ }
2015
+ };
2016
+ var listProposalsTool = {
2017
+ name: "list_proposals",
2018
+ description: "List proposals with optional filters. Use to review pending proposals, check resolved proposals, or filter by page/category/severity.",
2019
+ inputSchema: z35.object({
2020
+ status: z35.enum(PROPOSAL_STATUSES).optional().describe("Filter by single status"),
2021
+ statuses: z35.array(z35.enum(PROPOSAL_STATUSES)).optional().describe("Filter by multiple statuses"),
2022
+ issue_id: z35.string().optional().describe("Filter by related issue ID"),
2023
+ page_path: z35.string().optional().describe("Filter by page path"),
2024
+ category: z35.enum(ISSUE_CATEGORIES2).optional().describe("Filter by category"),
2025
+ severity: z35.enum(ISSUE_SEVERITIES).optional().describe("Filter by severity"),
2026
+ limit: z35.number().optional().default(20).describe("Maximum number of results (default: 20)")
2027
+ }),
2028
+ handler: async (args) => {
2029
+ const { status, statuses, issue_id, page_path, category, severity, limit } = args;
2030
+ if (!isApiConfigured()) {
2031
+ return apiNotConfiguredResult();
2032
+ }
2033
+ const params = {
2034
+ limit: limit ?? 20
2035
+ };
2036
+ if (status)
2037
+ params.status = status;
2038
+ if (statuses && statuses.length > 0)
2039
+ params.statuses = statuses.join(",");
2040
+ if (issue_id)
2041
+ params.issue_id = issue_id;
2042
+ if (page_path)
2043
+ params.page_path = page_path;
2044
+ if (category)
2045
+ params.category = category;
2046
+ if (severity)
2047
+ params.severity = severity;
2048
+ const { data, error } = await apiGet("/proposals", params);
2049
+ if (error) {
2050
+ return errorResult(error);
2051
+ }
2052
+ return successResult(data);
2053
+ }
2054
+ };
2055
+ var getProposalTool = {
2056
+ name: "get_proposal",
2057
+ description: "Get details of a specific proposal by ID. Use to review the full diagnosis and proposed fix before implementing.",
2058
+ inputSchema: z35.object({
2059
+ proposal_id: z35.string().describe("The ID of the proposal to retrieve")
2060
+ }),
2061
+ handler: async (args) => {
2062
+ const { proposal_id } = args;
2063
+ if (!isApiConfigured()) {
2064
+ return apiNotConfiguredResult();
2065
+ }
2066
+ const { data, error } = await apiGet(`/proposals/${proposal_id}`);
2067
+ if (error) {
2068
+ return errorResult(error);
2069
+ }
2070
+ return successResult(data);
2071
+ }
2072
+ };
2073
+ var resolveProposalTool = {
2074
+ name: "resolve_proposal",
2075
+ description: "Mark a proposal as resolved after implementing the fix. This captures baseline metrics for later evaluation. Call this after you have implemented the proposed fix in code.",
2076
+ inputSchema: z35.object({
2077
+ proposal_id: z35.string().describe("The ID of the proposal to mark as resolved"),
2078
+ member_id: z35.string().optional().describe("The ID of the member who resolved it (optional)")
2079
+ }),
2080
+ handler: async (args) => {
2081
+ const { proposal_id, member_id } = args;
2082
+ if (!isApiConfigured()) {
2083
+ return apiNotConfiguredResult();
2084
+ }
2085
+ const { data, error } = await apiPost(`/proposals/${proposal_id}/resolve`, { member_id });
2086
+ if (error) {
2087
+ return errorResult(error);
2088
+ }
2089
+ return successResult(data);
2090
+ }
2091
+ };
2092
+ var dismissProposalTool = {
2093
+ name: "dismiss_proposal",
2094
+ description: "Dismiss a proposal that won't be implemented. Use when the proposal is not applicable, the issue is intended behavior, or a different approach is preferred.",
2095
+ inputSchema: z35.object({
2096
+ proposal_id: z35.string().describe("The ID of the proposal to dismiss"),
2097
+ reason: z35.string().describe("Explanation of why the proposal is being dismissed"),
2098
+ member_id: z35.string().optional().describe("The ID of the member who dismissed it (optional)")
2099
+ }),
2100
+ handler: async (args) => {
2101
+ const { proposal_id, reason, member_id } = args;
2102
+ if (!isApiConfigured()) {
2103
+ return apiNotConfiguredResult();
2104
+ }
2105
+ const { data, error } = await apiPost(`/proposals/${proposal_id}/dismiss`, { reason, member_id });
2106
+ if (error) {
2107
+ return errorResult(error);
2108
+ }
2109
+ return successResult(data);
2110
+ }
2111
+ };
2112
+ var evaluateProposalTool = {
2113
+ name: "evaluate_proposal",
2114
+ description: "Evaluate if a resolved proposal improved metrics. Compares post-resolution metrics to baseline. Returns success/partial/failed outcome with detailed verdict. Wait at least 24 hours after resolution for reliable results.",
2115
+ inputSchema: z35.object({
2116
+ proposal_id: z35.string().describe("The ID of the proposal to evaluate"),
2117
+ min_hours: z35.number().optional().default(24).describe("Minimum hours since resolution required (default: 24)")
2118
+ }),
2119
+ handler: async (args) => {
2120
+ const { proposal_id, min_hours } = args;
2121
+ if (!isApiConfigured()) {
2122
+ return apiNotConfiguredResult();
2123
+ }
2124
+ const minHoursParam = min_hours ?? 24;
2125
+ const { data, error } = await apiPost(`/proposals/${proposal_id}/evaluate?min_hours=${minHoursParam}`, {});
2126
+ if (error) {
2127
+ return errorResult(error);
2128
+ }
2129
+ return successResult(data);
2130
+ }
2131
+ };
2132
+ var listPendingProposalsTool = {
2133
+ name: "list_pending_proposals",
2134
+ description: "List all pending proposals awaiting review. Use to see what fix suggestions are available for implementation.",
2135
+ inputSchema: z35.object({}),
2136
+ handler: async () => {
2137
+ if (!isApiConfigured()) {
2138
+ return apiNotConfiguredResult();
2139
+ }
2140
+ const { data, error } = await apiGet("/proposals/pending");
2141
+ if (error) {
2142
+ return errorResult(error);
2143
+ }
2144
+ return successResult(data);
2145
+ }
2146
+ };
2147
+ var listProposalsForEvaluationTool = {
2148
+ name: "list_proposals_for_evaluation",
2149
+ description: "List resolved proposals that are ready for evaluation (24+ hours since resolution). Use during audit runs to check outcomes of previously resolved proposals.",
2150
+ inputSchema: z35.object({
2151
+ min_hours: z35.number().optional().default(24).describe("Minimum hours since resolution (default: 24)")
2152
+ }),
2153
+ handler: async (args) => {
2154
+ const { min_hours } = args;
2155
+ if (!isApiConfigured()) {
2156
+ return apiNotConfiguredResult();
2157
+ }
2158
+ const { data, error } = await apiGet("/proposals/ready-for-evaluation", { min_hours: min_hours ?? 24 });
2159
+ if (error) {
2160
+ return errorResult(error);
2161
+ }
2162
+ return successResult(data);
2163
+ }
2164
+ };
2165
+
2166
+ // ../../libraries/mcp-tools/dist/tools/git.js
2167
+ import { z as z36 } from "zod";
2168
+ var gitContext = null;
2169
+ function getGitContext() {
2170
+ if (!gitContext) {
2171
+ throw new Error("Git context not configured. Connect GitHub or GitLab first.");
2172
+ }
2173
+ return gitContext;
2174
+ }
2175
+ async function githubRequest(endpoint, options = {}) {
2176
+ const ctx = getGitContext();
2177
+ const url = `https://api.github.com${endpoint}`;
2178
+ return fetch(url, {
2179
+ ...options,
2180
+ headers: {
2181
+ Authorization: `Bearer ${ctx.token}`,
2182
+ Accept: "application/vnd.github.v3+json",
2183
+ "X-GitHub-Api-Version": "2022-11-28",
2184
+ "Content-Type": "application/json",
2185
+ ...options.headers
2186
+ }
2187
+ });
2188
+ }
2189
+ async function gitlabRequest(endpoint, options = {}) {
2190
+ const ctx = getGitContext();
2191
+ const baseUrl = ctx.baseUrl || "https://gitlab.com";
2192
+ const url = `${baseUrl}/api/v4${endpoint}`;
2193
+ return fetch(url, {
2194
+ ...options,
2195
+ headers: {
2196
+ "PRIVATE-TOKEN": ctx.token,
2197
+ "Content-Type": "application/json",
2198
+ ...options.headers
2199
+ }
2200
+ });
2201
+ }
2202
+ var createBranchTool = {
2203
+ name: "create_branch",
2204
+ description: "Create a new branch from the default branch. Works with both GitHub and GitLab.",
2205
+ inputSchema: z36.object({
2206
+ branch_name: z36.string().describe("Name of the new branch to create"),
2207
+ from_branch: z36.string().optional().default("main").describe("Base branch to create from (default: main)")
2208
+ }),
2209
+ handler: async (args) => {
2210
+ try {
2211
+ const ctx = getGitContext();
2212
+ const branchName = args.branch_name;
2213
+ const fromBranch = args.from_branch || "main";
2214
+ if (ctx.provider === "github") {
2215
+ const refRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs/heads/${fromBranch}`);
2216
+ if (!refRes.ok) {
2217
+ return errorResult(`Failed to get base branch: ${refRes.statusText}`);
2218
+ }
2219
+ const refData = await refRes.json();
2220
+ const sha = refData.object.sha;
2221
+ const createRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs`, {
2222
+ method: "POST",
2223
+ body: JSON.stringify({
2224
+ ref: `refs/heads/${branchName}`,
2225
+ sha
2226
+ })
2227
+ });
2228
+ if (!createRes.ok) {
2229
+ const err = await createRes.json();
2230
+ return errorResult(err.message || "Failed to create branch");
2231
+ }
2232
+ return successResult({
2233
+ success: true,
2234
+ branch: branchName,
2235
+ from: fromBranch,
2236
+ provider: "github"
2237
+ });
2238
+ } else {
2239
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
2240
+ const createRes = await gitlabRequest(`/projects/${encodedPath}/repository/branches`, {
2241
+ method: "POST",
2242
+ body: JSON.stringify({
2243
+ branch: branchName,
2244
+ ref: fromBranch
2245
+ })
2246
+ });
2247
+ if (!createRes.ok) {
2248
+ const err = await createRes.json();
2249
+ return errorResult(err.message || "Failed to create branch");
2250
+ }
2251
+ return successResult({
2252
+ success: true,
2253
+ branch: branchName,
2254
+ from: fromBranch,
2255
+ provider: "gitlab"
2256
+ });
2257
+ }
2258
+ } catch (error) {
2259
+ return errorResult(error instanceof Error ? error.message : "Failed to create branch");
2260
+ }
2261
+ }
2262
+ };
2263
+ var getFileContentTool = {
2264
+ name: "get_file_content",
2265
+ description: "Get the content of a file from the repository. Works with both GitHub and GitLab.",
2266
+ inputSchema: z36.object({
2267
+ path: z36.string().describe("Path to the file in the repository"),
2268
+ branch: z36.string().optional().describe("Branch to read from (default: main)")
2269
+ }),
2270
+ handler: async (args) => {
2271
+ try {
2272
+ const ctx = getGitContext();
2273
+ const filePath = args.path;
2274
+ const branch = args.branch || "main";
2275
+ if (ctx.provider === "github") {
2276
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
2277
+ if (!res.ok) {
2278
+ if (res.status === 404) {
2279
+ return errorResult(`File not found: ${filePath}`);
2280
+ }
2281
+ return errorResult(`Failed to get file: ${res.statusText}`);
2282
+ }
2283
+ const data = await res.json();
2284
+ const content = Buffer.from(data.content, "base64").toString("utf-8");
2285
+ return successResult({
2286
+ path: filePath,
2287
+ content,
2288
+ sha: data.sha,
2289
+ provider: "github"
2290
+ });
2291
+ } else {
2292
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
2293
+ const encodedFilePath = encodeURIComponent(filePath);
2294
+ const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
2295
+ if (!res.ok) {
2296
+ if (res.status === 404) {
2297
+ return errorResult(`File not found: ${filePath}`);
2298
+ }
2299
+ return errorResult(`Failed to get file: ${res.statusText}`);
2300
+ }
2301
+ const data = await res.json();
2302
+ const content = Buffer.from(data.content, "base64").toString("utf-8");
2303
+ return successResult({
2304
+ path: filePath,
2305
+ content,
2306
+ sha: data.content_sha256,
2307
+ provider: "gitlab"
2308
+ });
2309
+ }
2310
+ } catch (error) {
2311
+ return errorResult(error instanceof Error ? error.message : "Failed to get file content");
2312
+ }
2313
+ }
2314
+ };
2315
+ var updateFileTool = {
2316
+ name: "update_file",
2317
+ description: "Update or create a file in the repository. Works with both GitHub and GitLab.",
2318
+ inputSchema: z36.object({
2319
+ path: z36.string().describe("Path to the file in the repository"),
2320
+ content: z36.string().describe("New content for the file"),
2321
+ message: z36.string().describe("Commit message"),
2322
+ branch: z36.string().describe("Branch to commit to"),
2323
+ sha: z36.string().optional().describe("SHA of the file being replaced (required for updates)")
2324
+ }),
2325
+ handler: async (args) => {
2326
+ try {
2327
+ const ctx = getGitContext();
2328
+ const filePath = args.path;
2329
+ const content = args.content;
2330
+ const message = args.message;
2331
+ const branch = args.branch;
2332
+ const sha = args.sha;
2333
+ if (ctx.provider === "github") {
2334
+ const body = {
2335
+ message,
2336
+ content: Buffer.from(content).toString("base64"),
2337
+ branch
2338
+ };
2339
+ if (sha) {
2340
+ body.sha = sha;
2341
+ }
2342
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}`, {
2343
+ method: "PUT",
2344
+ body: JSON.stringify(body)
2345
+ });
2346
+ if (!res.ok) {
2347
+ const err = await res.json();
2348
+ return errorResult(err.message || "Failed to update file");
2349
+ }
2350
+ const data = await res.json();
2351
+ return successResult({
2352
+ success: true,
2353
+ path: filePath,
2354
+ sha: data.content.sha,
2355
+ commit: data.commit.sha,
2356
+ provider: "github"
2357
+ });
2358
+ } else {
2359
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
2360
+ const encodedFilePath = encodeURIComponent(filePath);
2361
+ const method = sha ? "PUT" : "POST";
2362
+ const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}`, {
2363
+ method,
2364
+ body: JSON.stringify({
2365
+ branch,
2366
+ content,
2367
+ commit_message: message,
2368
+ encoding: "text"
2369
+ })
2370
+ });
2371
+ if (!res.ok) {
2372
+ const err = await res.json();
2373
+ return errorResult(err.message || "Failed to update file");
2374
+ }
2375
+ const data = await res.json();
2376
+ return successResult({
2377
+ success: true,
2378
+ path: filePath,
2379
+ sha: data.content_sha256,
2380
+ provider: "gitlab"
2381
+ });
2382
+ }
2383
+ } catch (error) {
2384
+ return errorResult(error instanceof Error ? error.message : "Failed to update file");
2385
+ }
2386
+ }
2387
+ };
2388
+ var createMergeRequestTool = {
2389
+ name: "create_merge_request",
2390
+ description: "Create a merge request (GitHub: Pull Request, GitLab: Merge Request). Works with both providers.",
2391
+ inputSchema: z36.object({
2392
+ title: z36.string().describe("Title of the merge request"),
2393
+ description: z36.string().describe("Description/body of the merge request"),
2394
+ source_branch: z36.string().describe("Branch containing the changes"),
2395
+ target_branch: z36.string().optional().default("main").describe("Branch to merge into (default: main)")
2396
+ }),
2397
+ handler: async (args) => {
2398
+ try {
2399
+ const ctx = getGitContext();
2400
+ const title = args.title;
2401
+ const description = args.description;
2402
+ const sourceBranch = args.source_branch;
2403
+ const targetBranch = args.target_branch || "main";
2404
+ if (ctx.provider === "github") {
2405
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls`, {
2406
+ method: "POST",
2407
+ body: JSON.stringify({
2408
+ title,
2409
+ body: description,
2410
+ head: sourceBranch,
2411
+ base: targetBranch
2412
+ })
2413
+ });
2414
+ if (!res.ok) {
2415
+ const err = await res.json();
2416
+ return errorResult(err.message || "Failed to create pull request");
2417
+ }
2418
+ const data = await res.json();
2419
+ return successResult({
2420
+ success: true,
2421
+ mr_number: data.number,
2422
+ url: data.html_url,
2423
+ state: data.state,
2424
+ provider: "github",
2425
+ type: "pull_request"
2426
+ });
2427
+ } else {
2428
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
2429
+ const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests`, {
2430
+ method: "POST",
2431
+ body: JSON.stringify({
2432
+ title,
2433
+ description,
2434
+ source_branch: sourceBranch,
2435
+ target_branch: targetBranch
2436
+ })
2437
+ });
2438
+ if (!res.ok) {
2439
+ const err = await res.json();
2440
+ return errorResult(err.message || "Failed to create merge request");
2441
+ }
2442
+ const data = await res.json();
2443
+ return successResult({
2444
+ success: true,
2445
+ mr_number: data.iid,
2446
+ url: data.web_url,
2447
+ state: data.state,
2448
+ provider: "gitlab",
2449
+ type: "merge_request"
2450
+ });
2451
+ }
2452
+ } catch (error) {
2453
+ return errorResult(error instanceof Error ? error.message : "Failed to create merge request");
2454
+ }
2455
+ }
2456
+ };
2457
+ var checkMrStatusTool = {
2458
+ name: "check_mr_status",
2459
+ description: "Check the status of a merge request. Works with both GitHub and GitLab.",
2460
+ inputSchema: z36.object({
2461
+ mr_number: z36.number().describe("The merge request number")
2462
+ }),
2463
+ handler: async (args) => {
2464
+ try {
2465
+ const ctx = getGitContext();
2466
+ const mrNumber = args.mr_number;
2467
+ if (ctx.provider === "github") {
2468
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls/${mrNumber}`);
2469
+ if (!res.ok) {
2470
+ if (res.status === 404) {
2471
+ return errorResult(`Pull request #${mrNumber} not found`);
2472
+ }
2473
+ return errorResult(`Failed to get pull request: ${res.statusText}`);
2474
+ }
2475
+ const data = await res.json();
2476
+ let status;
2477
+ if (data.merged) {
2478
+ status = "merged";
2479
+ } else if (data.state === "closed") {
2480
+ status = "closed";
2481
+ } else {
2482
+ status = "open";
2483
+ }
2484
+ return successResult({
2485
+ mr_number: data.number,
2486
+ title: data.title,
2487
+ status,
2488
+ url: data.html_url,
2489
+ source_branch: data.head.ref,
2490
+ target_branch: data.base.ref,
2491
+ created_at: data.created_at,
2492
+ merged_at: data.merged_at,
2493
+ closed_at: data.closed_at,
2494
+ provider: "github"
2495
+ });
2496
+ } else {
2497
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
2498
+ const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests/${mrNumber}`);
2499
+ if (!res.ok) {
2500
+ if (res.status === 404) {
2501
+ return errorResult(`Merge request !${mrNumber} not found`);
2502
+ }
2503
+ return errorResult(`Failed to get merge request: ${res.statusText}`);
2504
+ }
2505
+ const data = await res.json();
2506
+ let status;
2507
+ if (data.state === "merged") {
2508
+ status = "merged";
2509
+ } else if (data.state === "closed") {
2510
+ status = "closed";
2511
+ } else {
2512
+ status = "open";
2513
+ }
2514
+ return successResult({
2515
+ mr_number: data.iid,
2516
+ title: data.title,
2517
+ status,
2518
+ url: data.web_url,
2519
+ source_branch: data.source_branch,
2520
+ target_branch: data.target_branch,
2521
+ created_at: data.created_at,
2522
+ merged_at: data.merged_at,
2523
+ closed_at: data.closed_at,
2524
+ provider: "gitlab"
2525
+ });
2526
+ }
2527
+ } catch (error) {
2528
+ return errorResult(error instanceof Error ? error.message : "Failed to check MR status");
2529
+ }
2530
+ }
2531
+ };
2532
+ var listOpenMrsTool = {
2533
+ name: "list_open_mrs",
2534
+ description: "List open merge requests for the repository. Works with both GitHub and GitLab.",
2535
+ inputSchema: z36.object({
2536
+ limit: z36.number().optional().default(10).describe("Maximum number of MRs to return (default: 10)")
2537
+ }),
2538
+ handler: async (args) => {
2539
+ try {
2540
+ const ctx = getGitContext();
2541
+ const limit = args.limit || 10;
2542
+ if (ctx.provider === "github") {
2543
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls?state=open&per_page=${limit}`);
2544
+ if (!res.ok) {
2545
+ return errorResult(`Failed to list pull requests: ${res.statusText}`);
2546
+ }
2547
+ const data = await res.json();
2548
+ const mrs = data.map((pr) => ({
2549
+ mr_number: pr.number,
2550
+ title: pr.title,
2551
+ url: pr.html_url,
2552
+ source_branch: pr.head.ref,
2553
+ target_branch: pr.base.ref,
2554
+ created_at: pr.created_at
2555
+ }));
2556
+ return successResult({
2557
+ mrs,
2558
+ count: mrs.length,
2559
+ provider: "github"
2560
+ });
2561
+ } else {
2562
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
2563
+ const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests?state=opened&per_page=${limit}`);
2564
+ if (!res.ok) {
2565
+ return errorResult(`Failed to list merge requests: ${res.statusText}`);
2566
+ }
2567
+ const data = await res.json();
2568
+ const mrs = data.map((mr) => ({
2569
+ mr_number: mr.iid,
2570
+ title: mr.title,
2571
+ url: mr.web_url,
2572
+ source_branch: mr.source_branch,
2573
+ target_branch: mr.target_branch,
2574
+ created_at: mr.created_at
2575
+ }));
2576
+ return successResult({
2577
+ mrs,
2578
+ count: mrs.length,
2579
+ provider: "gitlab"
2580
+ });
2581
+ }
2582
+ } catch (error) {
2583
+ return errorResult(error instanceof Error ? error.message : "Failed to list open MRs");
2584
+ }
2585
+ }
2586
+ };
2587
+ var listRepositoryFilesTool = {
2588
+ name: "list_repository_files",
2589
+ description: "List files and directories in the repository. Use this to discover the file structure before reading or modifying files. Works with both GitHub and GitLab.",
2590
+ inputSchema: z36.object({
2591
+ path: z36.string().optional().default("").describe("Directory path to list (empty for root)"),
2592
+ branch: z36.string().optional().describe("Branch to list from (default: main)"),
2593
+ recursive: z36.boolean().optional().default(false).describe("If true, list all files recursively (may be slow for large repos)")
2594
+ }),
2595
+ handler: async (args) => {
2596
+ try {
2597
+ const ctx = getGitContext();
2598
+ const dirPath = args.path || "";
2599
+ const branch = args.branch || "main";
2600
+ const recursive = args.recursive || false;
2601
+ if (ctx.provider === "github") {
2602
+ if (recursive) {
2603
+ const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
2604
+ if (!res.ok) {
2605
+ return errorResult(`Failed to list repository: ${res.statusText}`);
2606
+ }
2607
+ const data = await res.json();
2608
+ let items = data.tree;
2609
+ if (dirPath) {
2610
+ const prefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
2611
+ items = items.filter((item) => item.path.startsWith(prefix));
2612
+ }
2613
+ const files = items.filter((item) => item.type === "blob").map((item) => ({
2614
+ path: item.path,
2615
+ type: "file",
2616
+ size: item.size
2617
+ }));
2618
+ const dirs = items.filter((item) => item.type === "tree").map((item) => ({
2619
+ path: item.path,
2620
+ type: "directory"
2621
+ }));
2622
+ return successResult({
2623
+ path: dirPath || "/",
2624
+ files: files.slice(0, 200),
2625
+ directories: dirs.slice(0, 100),
2626
+ total_files: files.length,
2627
+ total_directories: dirs.length,
2628
+ truncated: data.truncated || files.length > 200,
2629
+ provider: "github"
2630
+ });
2631
+ } else {
2632
+ const endpoint = dirPath ? `/repos/${ctx.repoFullName}/contents/${dirPath}?ref=${branch}` : `/repos/${ctx.repoFullName}/contents?ref=${branch}`;
2633
+ const res = await githubRequest(endpoint);
2634
+ if (!res.ok) {
2635
+ if (res.status === 404) {
2636
+ return errorResult(`Directory not found: ${dirPath || "/"}`);
2637
+ }
2638
+ return errorResult(`Failed to list directory: ${res.statusText}`);
2639
+ }
2640
+ const data = await res.json();
2641
+ const files = data.filter((item) => item.type === "file").map((item) => ({
2642
+ name: item.name,
2643
+ path: item.path,
2644
+ type: "file",
2645
+ size: item.size
2646
+ }));
2647
+ const directories = data.filter((item) => item.type === "dir").map((item) => ({
2648
+ name: item.name,
2649
+ path: item.path,
2650
+ type: "directory"
2651
+ }));
2652
+ return successResult({
2653
+ path: dirPath || "/",
2654
+ files,
2655
+ directories,
2656
+ provider: "github"
2657
+ });
2658
+ }
2659
+ } else {
2660
+ const encodedPath = encodeURIComponent(ctx.repoFullName);
2661
+ const pathParam = dirPath ? `&path=${encodeURIComponent(dirPath)}` : "";
2662
+ const recursiveParam = recursive ? "&recursive=true" : "";
2663
+ const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}${pathParam}${recursiveParam}&per_page=100`);
2664
+ if (!res.ok) {
2665
+ if (res.status === 404) {
2666
+ return errorResult(`Directory not found: ${dirPath || "/"}`);
2667
+ }
2668
+ return errorResult(`Failed to list directory: ${res.statusText}`);
2669
+ }
2670
+ const data = await res.json();
2671
+ const files = data.filter((item) => item.type === "blob").map((item) => ({
2672
+ name: item.name,
2673
+ path: item.path,
2674
+ type: "file"
2675
+ }));
2676
+ const directories = data.filter((item) => item.type === "tree").map((item) => ({
2677
+ name: item.name,
2678
+ path: item.path,
2679
+ type: "directory"
2680
+ }));
2681
+ return successResult({
2682
+ path: dirPath || "/",
2683
+ files,
2684
+ directories,
2685
+ provider: "gitlab"
2686
+ });
2687
+ }
2688
+ } catch (error) {
2689
+ return errorResult(error instanceof Error ? error.message : "Failed to list repository files");
2690
+ }
2691
+ }
2692
+ };
2693
+
2694
+ // ../../libraries/mcp-tools/dist/tools/index.js
2695
+ var allTools = [
2696
+ // Analysis
2697
+ analyzeFlowTool,
2698
+ analyzeFunnelTool,
2699
+ // Comparison
2700
+ compareCohortsTool,
2701
+ comparePeriodsTool,
2702
+ // Detection
2703
+ detectDriftTool,
2704
+ detectRegressionsTool,
2705
+ // Diagnostic
2706
+ runFullDiagnosticTool,
2707
+ investigateIssueTool,
2708
+ validateIssueTool,
2709
+ // Discovery
2710
+ discoverPersonasTool,
2711
+ // Get tools
2712
+ getActionableIssuesTool,
2713
+ getAnomaliesTool,
2714
+ getConsoleErrorsTool,
2715
+ getDeadClicksTool,
2716
+ getDomainsTool,
2717
+ getElementFrictionTool,
2718
+ getFlowFrictionTool,
2719
+ getFormFrictionTool,
2720
+ getIssuesTool,
2721
+ getJourneyPatternsTool,
2722
+ getPageMetricsTool,
2723
+ getPageTrendsTool,
2724
+ getSessionDetailsTool,
2725
+ getSessionPagesTool,
2726
+ getUxHealthReportTool,
2727
+ getUpgradeOptionsTool,
2728
+ // Improvement run
2729
+ startImprovementRunTool,
2730
+ updateImprovementRunTool,
2731
+ recordImprovementActionTool,
2732
+ updateImprovementActionTool,
2733
+ getImprovementRunTool,
2734
+ listImprovementRunsTool,
2735
+ // Knowledge
2736
+ getSiteKnowledgeTool,
2737
+ addSiteKnowledgeTool,
2738
+ deleteSiteKnowledgeTool,
2739
+ getSimilarFixesTool,
2740
+ // List
2741
+ listPagesTool,
2742
+ listSessionsTool,
2743
+ // Prediction
2744
+ predictOutcomesTool,
2745
+ // Remediation
2746
+ proposeFixTool,
2747
+ listPendingFixesTool,
2748
+ listRemediationsTool,
2749
+ confirmDeploymentTool,
2750
+ evaluateFixTool,
2751
+ getFixHistoryTool,
2752
+ updateRemediationStatusTool,
2753
+ listRemediationsByStatusTool,
2754
+ getRemediationByPrTool,
2755
+ // Scan
2756
+ scanSiteTool,
2757
+ // Search
2758
+ searchSessionsTool,
2759
+ // Triage
2760
+ dismissIssueTool,
2761
+ markIntendedBehaviorTool,
2762
+ getIssueHistoryTool,
2763
+ triageSessionsTool,
2764
+ // Proposal (audit mode)
2765
+ createProposalTool,
2766
+ listProposalsTool,
2767
+ getProposalTool,
2768
+ resolveProposalTool,
2769
+ dismissProposalTool,
2770
+ evaluateProposalTool,
2771
+ listPendingProposalsTool,
2772
+ listProposalsForEvaluationTool,
2773
+ // Git (provider-agnostic)
2774
+ createBranchTool,
2775
+ getFileContentTool,
2776
+ updateFileTool,
2777
+ createMergeRequestTool,
2778
+ checkMrStatusTool,
2779
+ listOpenMrsTool,
2780
+ listRepositoryFilesTool
2781
+ ];
2782
+ var EXPOSED_TOOL_NAMES = [
2783
+ "get_domains",
2784
+ "run_full_diagnostic",
2785
+ "triage_sessions",
2786
+ "get_upgrade_options",
2787
+ "search_tools",
2788
+ "call_tool",
2789
+ "memory_save",
2790
+ "memory_recall",
2791
+ "memory_list",
2792
+ "memory_delete",
2793
+ "memory_clear"
2794
+ ];
2795
+ var exposedTools = allTools.filter((t) => EXPOSED_TOOL_NAMES.includes(t.name));
2796
+ var hiddenTools = allTools.filter((t) => !EXPOSED_TOOL_NAMES.includes(t.name));
2797
+
2798
+ // src/tools/catalog/searchTools.ts
2799
+ import { z as z37 } from "zod";
2800
+ import { readFileSync } from "fs";
2801
+ import { fileURLToPath } from "url";
2802
+ import { dirname, join } from "path";
2803
+ var __filename2 = fileURLToPath(import.meta.url);
2804
+ var __dirname2 = dirname(__filename2);
2805
+ var _catalog = null;
2806
+ function loadCatalog() {
2807
+ if (_catalog) return _catalog;
2808
+ try {
2809
+ const catalogPath = join(__dirname2, "toolCatalog.json");
2810
+ const raw = readFileSync(catalogPath, "utf-8");
2811
+ _catalog = JSON.parse(raw);
2812
+ return _catalog;
2813
+ } catch {
2814
+ console.warn(
2815
+ "[searchTools] Failed to load toolCatalog.json, using empty catalog"
2816
+ );
2817
+ _catalog = [];
2818
+ return _catalog;
2819
+ }
2820
+ }
2821
+ function cosineSimilarity(a, b) {
2822
+ if (a.length === 0 || b.length === 0 || a.length !== b.length) return 0;
2823
+ let dot = 0;
2824
+ for (let i = 0; i < a.length; i++) {
2825
+ dot += a[i] * b[i];
2826
+ }
2827
+ return dot;
2828
+ }
2829
+ function searchByKeyword(query, limit) {
2830
+ const catalog = loadCatalog();
2831
+ const normalizedQuery = query.toLowerCase();
2832
+ const queryWords = normalizedQuery.split(/[\s_-]+/).filter((w) => w.length >= 2);
2833
+ const queryPhrases = [normalizedQuery];
2834
+ const synonyms = {
2835
+ error: ["console", "js", "javascript", "bug", "crash", "exception"],
2836
+ page: ["pages", "route", "url", "path"],
2837
+ user: ["users", "session", "visitor"],
2838
+ click: ["clicks", "tap", "press", "rage"],
2839
+ form: ["forms", "input", "field", "submit"],
2840
+ flow: ["flows", "journey", "funnel", "navigation", "path"],
2841
+ issue: ["issues", "problem", "bug", "friction"],
2842
+ fix: ["fixes", "remediation", "repair", "resolve"],
2843
+ compare: ["comparison", "diff", "versus", "cohort"],
2844
+ health: ["score", "metrics", "ux"],
2845
+ dead: ["unresponsive", "broken", "stuck"],
2846
+ rage: ["angry", "frustrated", "frustration"]
2847
+ };
2848
+ const expandedWords = new Set(queryWords);
2849
+ for (const word of queryWords) {
2850
+ if (synonyms[word]) {
2851
+ synonyms[word].forEach((syn) => expandedWords.add(syn));
2852
+ }
2853
+ for (const [key, values] of Object.entries(synonyms)) {
2854
+ if (values.includes(word)) {
2855
+ expandedWords.add(key);
2856
+ }
2857
+ }
2858
+ }
2859
+ return catalog.map((tool) => {
2860
+ const toolName = tool.name.toLowerCase().replace(/_/g, " ");
2861
+ const toolNameParts = tool.name.toLowerCase().split("_");
2862
+ const text = `${toolName} ${tool.description} ${tool.category}`.toLowerCase();
2863
+ let score = 0;
2864
+ for (const phrase of queryPhrases) {
2865
+ if (phrase.length > 3 && text.includes(phrase)) score += 5;
2866
+ }
2867
+ for (const word of expandedWords) {
2868
+ if (toolNameParts.includes(word)) score += 4;
2869
+ if (toolName.includes(word)) score += 3;
2870
+ if (tool.category.toLowerCase() === word) score += 2;
2871
+ if (text.includes(word)) score += 1;
2872
+ }
2873
+ return { tool, score };
2874
+ }).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.tool);
2875
+ }
2876
+ async function searchBySemantic(query, limit, queryEmbedding) {
2877
+ const catalog = loadCatalog();
2878
+ return catalog.filter((t) => t.embedding && t.embedding.length > 0).map((tool) => ({
2879
+ tool,
2880
+ score: cosineSimilarity(queryEmbedding, tool.embedding)
2881
+ })).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.tool);
2882
+ }
2883
+ async function searchTools2(query, limit = 5, queryEmbedding) {
2884
+ if (queryEmbedding && queryEmbedding.length > 0) {
2885
+ return searchBySemantic(query, limit, queryEmbedding);
2886
+ }
2887
+ return searchByKeyword(query, limit);
2888
+ }
2889
+ function getToolByName3(name) {
2890
+ return loadCatalog().find((t) => t.name === name);
2891
+ }
2892
+ var DEFAULT_TOOL_LIMIT = 5;
2893
+ var searchToolsSchema = z37.object({
2894
+ query: z37.string().describe(
2895
+ "Natural language description of what data or analysis capability you need. Describe what you want to learn or investigate."
2896
+ ),
2897
+ limit: z37.number().optional().describe("Maximum number of tools to return (default 5)")
2898
+ });
2899
+ function registerSearchTools(server) {
2900
+ server.registerTool(
2901
+ "search_tools",
2902
+ {
2903
+ description: "Discover analysis tools by describing what data or capability you need. You have access to 40+ specialized tools covering sessions, pages, behaviors, journeys, forms, errors, performance, cohorts, issues, and remediation. Keyword search matches your intent to relevant tools. Returns tool names, descriptions, categories, and parameters. Use call_tool to execute discovered tools.",
2904
+ inputSchema: searchToolsSchema
2905
+ },
2906
+ async ({ query, limit }) => {
2907
+ const searchLimit = limit ?? DEFAULT_TOOL_LIMIT;
2908
+ const matches = await searchTools2(query, searchLimit);
2909
+ if (matches.length === 0) {
2910
+ return {
2911
+ content: [
2912
+ {
2913
+ type: "text",
2914
+ text: JSON.stringify({
2915
+ tools: [],
2916
+ message: "No matching tools found. Try rephrasing your query or use broader terms like 'page', 'session', 'issue', 'flow', or 'form'."
2917
+ })
2918
+ }
2919
+ ]
2920
+ };
2921
+ }
2922
+ const tools = matches.map((tool) => ({
2923
+ name: tool.name,
2924
+ description: tool.description.length > 250 ? tool.description.slice(0, 250) + "..." : tool.description,
2925
+ category: tool.category,
2926
+ parameters: Object.entries(tool.parameters).map(([name, prop]) => ({
2927
+ name,
2928
+ type: prop.type,
2929
+ required: prop.required ?? false,
2930
+ description: prop.description
2931
+ }))
2932
+ }));
2933
+ return {
2934
+ content: [
2935
+ {
2936
+ type: "text",
2937
+ text: JSON.stringify({
2938
+ tools,
2939
+ usage: "Use call_tool with the tool name and arguments to execute. Example: call_tool({ tool_name: 'get_page_metrics', arguments: { page_path: '/checkout' } })"
2940
+ })
2941
+ }
2942
+ ]
2943
+ };
2944
+ }
2945
+ );
2946
+ }
2947
+
2948
+ // src/tools/catalog/callTool.ts
2949
+ import { z as z38 } from "zod";
2950
+ var toolRegistry = /* @__PURE__ */ new Map();
2951
+ function registerToolHandler(name, handler) {
2952
+ toolRegistry.set(name, handler);
2953
+ }
2954
+ var callToolSchema = z38.object({
2955
+ tool_name: z38.string().describe(
2956
+ "The exact name of the tool to call (e.g., 'get_page_metrics', 'analyze_flow', 'compare_cohorts')"
2957
+ ),
2958
+ arguments: z38.record(z38.string(), z38.unknown()).describe(
2959
+ "The arguments to pass to the tool as a JSON object. Check the tool description from search_tools for required parameters."
2960
+ )
2961
+ });
2962
+ function registerCallTool(server) {
2963
+ server.registerTool(
2964
+ "call_tool",
2965
+ {
2966
+ description: "Execute any analysis tool by name. Use search_tools first to discover available tools, then call them through this proxy. Pass the exact tool name and arguments as a JSON object. Example: call_tool({ tool_name: 'get_page_metrics', arguments: { page_path: '/checkout' } })",
2967
+ inputSchema: callToolSchema
2968
+ },
2969
+ async ({ tool_name, arguments: args }) => {
2970
+ const handler = toolRegistry.get(tool_name);
2971
+ if (!handler) {
2972
+ const catalogEntry = getToolByName3(tool_name);
2973
+ if (catalogEntry) {
2974
+ return {
2975
+ content: [
2976
+ {
2977
+ type: "text",
2978
+ text: JSON.stringify({
2979
+ error: `Tool "${tool_name}" exists in catalog but is not registered. This may be a server configuration issue.`,
2980
+ tool_info: {
2981
+ name: catalogEntry.name,
2982
+ description: catalogEntry.description,
2983
+ category: catalogEntry.category
2984
+ }
2985
+ })
2986
+ }
2987
+ ],
2988
+ isError: true
2989
+ };
2990
+ }
2991
+ return {
2992
+ content: [
2993
+ {
2994
+ type: "text",
2995
+ text: JSON.stringify({
2996
+ error: `Unknown tool: ${tool_name}`,
2997
+ hint: "Use search_tools to discover available tools first."
2998
+ })
2999
+ }
3000
+ ],
3001
+ isError: true
3002
+ };
3003
+ }
3004
+ try {
3005
+ return await handler(args);
3006
+ } catch (err) {
3007
+ const message = err instanceof Error ? err.message : String(err);
3008
+ return {
3009
+ content: [
3010
+ {
3011
+ type: "text",
3012
+ text: JSON.stringify({
3013
+ error: `Tool execution failed: ${message}`,
3014
+ tool_name,
3015
+ arguments: args
3016
+ })
3017
+ }
3018
+ ],
3019
+ isError: true
3020
+ };
3021
+ }
3022
+ }
3023
+ );
3024
+ }
3025
+
3026
+ // src/tools/memory.ts
3027
+ import { z as z39 } from "zod";
3028
+ var memoryStore = /* @__PURE__ */ new Map();
3029
+ function registerMemoryTools(server) {
3030
+ server.registerTool(
3031
+ "memory_save",
3032
+ {
3033
+ description: "Save a value to working memory for later retrieval. Use this to store intermediate results from tool calls that you want to reference later.",
3034
+ inputSchema: z39.object({
3035
+ key: z39.string().describe("Unique key to store the value under"),
3036
+ value: z39.string().describe("Value to store (will be stored as string)"),
3037
+ annotation: z39.string().optional().describe("Optional note about what this data represents")
3038
+ })
3039
+ },
3040
+ async ({
3041
+ key,
3042
+ value,
3043
+ annotation
3044
+ }) => {
3045
+ memoryStore.set(key, {
3046
+ key,
3047
+ value,
3048
+ timestamp: Date.now(),
3049
+ annotation
3050
+ });
3051
+ return {
3052
+ content: [
3053
+ {
3054
+ type: "text",
3055
+ text: JSON.stringify({ success: true, key, size: value.length })
3056
+ }
3057
+ ]
3058
+ };
3059
+ }
3060
+ );
3061
+ server.registerTool(
3062
+ "memory_recall",
3063
+ {
3064
+ description: "Retrieve a value from working memory by key. Use this to access data you previously stored with memory_save.",
3065
+ inputSchema: z39.object({
3066
+ key: z39.string().describe("Key to retrieve")
3067
+ })
3068
+ },
3069
+ async ({ key }) => {
3070
+ const entry = memoryStore.get(key);
3071
+ if (!entry) {
3072
+ return {
3073
+ content: [
3074
+ {
3075
+ type: "text",
3076
+ text: JSON.stringify({
3077
+ error: `Key "${key}" not found in memory`
3078
+ })
3079
+ }
3080
+ ],
3081
+ isError: true
3082
+ };
3083
+ }
3084
+ return {
3085
+ content: [
3086
+ {
3087
+ type: "text",
3088
+ text: entry.value
3089
+ }
3090
+ ]
3091
+ };
3092
+ }
3093
+ );
3094
+ server.registerTool(
3095
+ "memory_list",
3096
+ {
3097
+ description: "List all keys currently stored in working memory. Returns key names, sizes, timestamps, and annotations.",
3098
+ inputSchema: z39.object({})
3099
+ },
3100
+ async () => {
3101
+ const entries = [...memoryStore.values()].map((entry) => ({
3102
+ key: entry.key,
3103
+ size: entry.value.length,
3104
+ timestamp: new Date(entry.timestamp).toISOString(),
3105
+ annotation: entry.annotation
3106
+ }));
3107
+ return {
3108
+ content: [
3109
+ {
3110
+ type: "text",
3111
+ text: JSON.stringify({ count: entries.length, entries })
3112
+ }
3113
+ ]
3114
+ };
3115
+ }
3116
+ );
3117
+ server.registerTool(
3118
+ "memory_delete",
3119
+ {
3120
+ description: "Delete a value from working memory by key.",
3121
+ inputSchema: z39.object({
3122
+ key: z39.string().describe("Key to delete")
3123
+ })
3124
+ },
3125
+ async ({ key }) => {
3126
+ const existed = memoryStore.delete(key);
3127
+ return {
3128
+ content: [
3129
+ {
3130
+ type: "text",
3131
+ text: JSON.stringify({ success: true, deleted: existed })
3132
+ }
3133
+ ]
3134
+ };
3135
+ }
3136
+ );
3137
+ server.registerTool(
3138
+ "memory_clear",
3139
+ {
3140
+ description: "Clear all values from working memory. Use with caution.",
3141
+ inputSchema: z39.object({})
3142
+ },
3143
+ async () => {
3144
+ const count = memoryStore.size;
3145
+ memoryStore.clear();
3146
+ return {
3147
+ content: [
3148
+ {
3149
+ type: "text",
3150
+ text: JSON.stringify({ success: true, cleared: count })
3151
+ }
3152
+ ]
3153
+ };
3154
+ }
3155
+ );
3156
+ }
3157
+
3158
+ // src/index.ts
35
3159
  function registerToolWithServer(server, tool) {
36
- // Use type assertion to avoid "Type instantiation is excessively deep" error
37
- // The MCP SDK's complex generic types cause TypeScript to struggle
38
- server.tool(tool.name, tool.description, tool.inputSchema.shape, tool.handler);
3160
+ server.tool(
3161
+ tool.name,
3162
+ tool.description,
3163
+ tool.inputSchema.shape,
3164
+ tool.handler
3165
+ );
39
3166
  }
40
- /**
41
- * Register all hidden tools with the call_tool registry.
42
- */
43
3167
  function registerHiddenTools() {
44
- for (const tool of hiddenTools) {
45
- registerToolHandler(tool.name, tool.handler);
46
- }
3168
+ for (const tool of hiddenTools) {
3169
+ registerToolHandler(tool.name, tool.handler);
3170
+ }
47
3171
  }
48
- // ─────────────────────────────────────────────────────────────────────────────
49
- // Server Instructions
50
- // ─────────────────────────────────────────────────────────────────────────────
51
- const SERVER_INSTRUCTIONS = `# Recapt Behavioral Intelligence
3172
+ var SERVER_INSTRUCTIONS = `# Recapt Behavioral Intelligence
52
3173
 
53
3174
  This server provides behavioral data from session recordings. Use these tools to understand user behavior, identify UX issues, and find friction points.
54
3175
 
@@ -56,26 +3177,26 @@ This server provides behavioral data from session recordings. Use these tools to
56
3177
 
57
3178
  You have access to 40+ analysis tools. Most are hidden to reduce context size. Use this pattern:
58
3179
 
59
- 1. **search_tools(query)** Describe what you need in natural language
60
- 2. **call_tool(tool_name, arguments)** Execute the discovered tool
3180
+ 1. **search_tools(query)** \u2014 Describe what you need in natural language
3181
+ 2. **call_tool(tool_name, arguments)** \u2014 Execute the discovered tool
61
3182
 
62
3183
  Example:
63
3184
  \`\`\`
64
3185
  search_tools({ query: "analyze user navigation between pages" })
65
- Returns: analyze_flow, analyze_funnel, get_journey_patterns...
3186
+ \u2192 Returns: analyze_flow, analyze_funnel, get_journey_patterns...
66
3187
 
67
3188
  call_tool({ tool_name: "analyze_flow", arguments: { start_page: "/pricing", end_page: "/checkout" } })
68
- Returns: flow analysis with paths, bottlenecks, friction scores
3189
+ \u2192 Returns: flow analysis with paths, bottlenecks, friction scores
69
3190
  \`\`\`
70
3191
 
71
3192
  ## Always-Available Tools
72
3193
 
73
3194
  These tools are always visible (no search needed):
74
- - **get_domains** List tracked domains (good starting point)
75
- - **run_full_diagnostic** Quick site health overview (lightweight, fast)
76
- - **triage_sessions** Find sessions needing attention
77
- - **get_upgrade_options** Check available plan upgrades and features
78
- - **memory_*** Store/retrieve intermediate results
3195
+ - **get_domains** \u2014 List tracked domains (good starting point)
3196
+ - **run_full_diagnostic** \u2014 Quick site health overview (lightweight, fast)
3197
+ - **triage_sessions** \u2014 Find sessions needing attention
3198
+ - **get_upgrade_options** \u2014 Check available plan upgrades and features
3199
+ - **memory_*** \u2014 Store/retrieve intermediate results
79
3200
 
80
3201
  ## Tier-Based Access
81
3202
 
@@ -98,9 +3219,9 @@ Use \`get_upgrade_options\` to explain the benefits of upgrading.
98
3219
  ## Comprehensive Site Analysis
99
3220
 
100
3221
  For a full site analysis, run these tools in sequence:
101
- 1. **run_full_diagnostic** Get health score, issues, problem pages
102
- 2. **detect_regressions** Check for recent behavioral regressions (separate call)
103
- 3. **get_ux_health_report** Detailed per-page breakdown if needed
3222
+ 1. **run_full_diagnostic** \u2014 Get health score, issues, problem pages
3223
+ 2. **detect_regressions** \u2014 Check for recent behavioral regressions (separate call)
3224
+ 3. **get_ux_health_report** \u2014 Detailed per-page breakdown if needed
104
3225
 
105
3226
  This split approach is faster and more reliable than a single heavy query.
106
3227
 
@@ -138,41 +3259,41 @@ npx @recapt/mcp skill list
138
3259
  npx @recapt/mcp skill install self-improvement
139
3260
  \`\`\`
140
3261
  Skills provide step-by-step guidance for complex multi-tool workflows.`;
141
- // ─────────────────────────────────────────────────────────────────────────────
142
- // Main
143
- // ─────────────────────────────────────────────────────────────────────────────
144
3262
  async function main() {
145
- console.error("[MCP] Starting recapt behavioral intelligence server...");
146
- if (!isApiConfigured()) {
147
- console.error("[MCP] ERROR: RECAPT_SECRET_KEY environment variable is required");
148
- process.exit(1);
149
- }
150
- console.error(`[MCP] API URL: ${getApiUrl()}`);
151
- const server = new McpServer({
152
- name: "recapt",
153
- version: "1.0.0",
154
- }, {
155
- capabilities: { logging: {} },
156
- instructions: SERVER_INSTRUCTIONS,
157
- });
158
- // Register hidden tool handlers first (for call_tool to use)
159
- registerHiddenTools();
160
- // Register search_tools and call_tool (MCP-specific implementations)
161
- registerSearchTools(server);
162
- registerCallTool(server);
163
- // Register exposed tools from shared library
164
- registerToolWithServer(server, getDomainsTool);
165
- registerToolWithServer(server, runFullDiagnosticTool);
166
- registerToolWithServer(server, triageSessionsTool);
167
- registerToolWithServer(server, getUpgradeOptionsTool);
168
- // Register memory tools (MCP-specific, stateful per session)
169
- registerMemoryTools(server);
170
- const transport = new StdioServerTransport();
171
- await server.connect(transport);
172
- console.error(`[MCP] Server running on stdio (11 exposed tools, ${hiddenTools.length} hidden)`);
173
- console.error(`[MCP] Total tools available: ${allTools.length}`);
3263
+ console.error("[MCP] Starting recapt behavioral intelligence server...");
3264
+ if (!isApiConfigured()) {
3265
+ console.error(
3266
+ "[MCP] ERROR: RECAPT_SECRET_KEY environment variable is required"
3267
+ );
3268
+ process.exit(1);
3269
+ }
3270
+ console.error(`[MCP] API URL: ${getApiUrl()}`);
3271
+ const server = new McpServer(
3272
+ {
3273
+ name: "recapt",
3274
+ version: "1.0.0"
3275
+ },
3276
+ {
3277
+ capabilities: { logging: {} },
3278
+ instructions: SERVER_INSTRUCTIONS
3279
+ }
3280
+ );
3281
+ registerHiddenTools();
3282
+ registerSearchTools(server);
3283
+ registerCallTool(server);
3284
+ registerToolWithServer(server, getDomainsTool);
3285
+ registerToolWithServer(server, runFullDiagnosticTool);
3286
+ registerToolWithServer(server, triageSessionsTool);
3287
+ registerToolWithServer(server, getUpgradeOptionsTool);
3288
+ registerMemoryTools(server);
3289
+ const transport = new StdioServerTransport();
3290
+ await server.connect(transport);
3291
+ console.error(
3292
+ `[MCP] Server running on stdio (11 exposed tools, ${hiddenTools.length} hidden)`
3293
+ );
3294
+ console.error(`[MCP] Total tools available: ${allTools.length}`);
174
3295
  }
175
3296
  main().catch((err) => {
176
- console.error("[MCP] Fatal error:", err);
177
- process.exit(1);
3297
+ console.error("[MCP] Fatal error:", err);
3298
+ process.exit(1);
178
3299
  });