@recapt/mcp 0.0.45 → 0.0.46
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.
- package/dist/index.js +1254 -1187
- package/dist/tools/catalog/anthropicToolCatalog.json +50 -4
- package/dist/tools/catalog/toolCatalog.json +1193 -765
- package/package.json +2 -3
- package/skills/self-improvement.md +254 -75
package/dist/index.js
CHANGED
|
@@ -114,6 +114,711 @@ function apiDelete(path) {
|
|
|
114
114
|
return request({ method: "DELETE", path });
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
// ../../libraries/mcp-tools/dist/tools/git.js
|
|
118
|
+
import { z } from "zod";
|
|
119
|
+
|
|
120
|
+
// ../../libraries/mcp-tools/dist/tools/_utils.js
|
|
121
|
+
function errorResult(message) {
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
124
|
+
isError: true
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function successResult(data) {
|
|
128
|
+
return {
|
|
129
|
+
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function apiNotConfiguredResult() {
|
|
133
|
+
return errorResult("API not configured");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ../../libraries/mcp-tools/dist/tools/git.js
|
|
137
|
+
var gitContext = null;
|
|
138
|
+
function getGitContext() {
|
|
139
|
+
if (!gitContext) {
|
|
140
|
+
throw new Error("Git context not configured. Connect GitHub or GitLab first.");
|
|
141
|
+
}
|
|
142
|
+
return gitContext;
|
|
143
|
+
}
|
|
144
|
+
async function githubRequest(endpoint, options = {}) {
|
|
145
|
+
const ctx = getGitContext();
|
|
146
|
+
const url = `https://api.github.com${endpoint}`;
|
|
147
|
+
return fetch(url, {
|
|
148
|
+
...options,
|
|
149
|
+
headers: {
|
|
150
|
+
Authorization: `Bearer ${ctx.token}`,
|
|
151
|
+
Accept: "application/vnd.github.v3+json",
|
|
152
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
...options.headers
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async function gitlabRequest(endpoint, options = {}) {
|
|
159
|
+
const ctx = getGitContext();
|
|
160
|
+
const baseUrl = ctx.baseUrl || "https://gitlab.com";
|
|
161
|
+
const url = `${baseUrl}/api/v4${endpoint}`;
|
|
162
|
+
const authHeader = ctx.token.startsWith("glpat-") ? { "PRIVATE-TOKEN": ctx.token } : { Authorization: `Bearer ${ctx.token}` };
|
|
163
|
+
return fetch(url, {
|
|
164
|
+
...options,
|
|
165
|
+
headers: {
|
|
166
|
+
...authHeader,
|
|
167
|
+
"Content-Type": "application/json",
|
|
168
|
+
...options.headers
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
var createBranchTool = {
|
|
173
|
+
name: "create_branch",
|
|
174
|
+
description: "Create a new branch from the default branch. Works with both GitHub and GitLab.",
|
|
175
|
+
inputSchema: z.object({
|
|
176
|
+
branch_name: z.string().describe("Name of the new branch to create"),
|
|
177
|
+
from_branch: z.string().optional().default("main").describe("Base branch to create from (default: main)")
|
|
178
|
+
}),
|
|
179
|
+
handler: async (args) => {
|
|
180
|
+
try {
|
|
181
|
+
const ctx = getGitContext();
|
|
182
|
+
const branchName = args.branch_name;
|
|
183
|
+
const fromBranch = args.from_branch || "main";
|
|
184
|
+
if (ctx.provider === "github") {
|
|
185
|
+
const refRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs/heads/${fromBranch}`);
|
|
186
|
+
if (!refRes.ok) {
|
|
187
|
+
return errorResult(`Failed to get base branch: ${refRes.statusText}`);
|
|
188
|
+
}
|
|
189
|
+
const refData = await refRes.json();
|
|
190
|
+
const sha = refData.object.sha;
|
|
191
|
+
const createRes = await githubRequest(`/repos/${ctx.repoFullName}/git/refs`, {
|
|
192
|
+
method: "POST",
|
|
193
|
+
body: JSON.stringify({
|
|
194
|
+
ref: `refs/heads/${branchName}`,
|
|
195
|
+
sha
|
|
196
|
+
})
|
|
197
|
+
});
|
|
198
|
+
if (!createRes.ok) {
|
|
199
|
+
const err = await createRes.json();
|
|
200
|
+
return errorResult(err.message || "Failed to create branch");
|
|
201
|
+
}
|
|
202
|
+
return successResult({
|
|
203
|
+
success: true,
|
|
204
|
+
branch: branchName,
|
|
205
|
+
from: fromBranch,
|
|
206
|
+
provider: "github"
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
210
|
+
const createRes = await gitlabRequest(`/projects/${encodedPath}/repository/branches`, {
|
|
211
|
+
method: "POST",
|
|
212
|
+
body: JSON.stringify({
|
|
213
|
+
branch: branchName,
|
|
214
|
+
ref: fromBranch
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
if (!createRes.ok) {
|
|
218
|
+
const err = await createRes.json();
|
|
219
|
+
return errorResult(err.message || "Failed to create branch");
|
|
220
|
+
}
|
|
221
|
+
return successResult({
|
|
222
|
+
success: true,
|
|
223
|
+
branch: branchName,
|
|
224
|
+
from: fromBranch,
|
|
225
|
+
provider: "gitlab"
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
return errorResult(error instanceof Error ? error.message : "Failed to create branch");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
var getFileContentTool = {
|
|
234
|
+
name: "get_file_content",
|
|
235
|
+
description: "Get the content of a file from the repository. Works with both GitHub and GitLab.",
|
|
236
|
+
inputSchema: z.object({
|
|
237
|
+
path: z.string().describe("Path to the file in the repository"),
|
|
238
|
+
branch: z.string().optional().describe("Branch to read from (default: main)")
|
|
239
|
+
}),
|
|
240
|
+
handler: async (args) => {
|
|
241
|
+
try {
|
|
242
|
+
const ctx = getGitContext();
|
|
243
|
+
const filePath = args.path;
|
|
244
|
+
const branch = args.branch || "main";
|
|
245
|
+
if (ctx.provider === "github") {
|
|
246
|
+
const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
|
|
247
|
+
if (!res.ok) {
|
|
248
|
+
if (res.status === 404) {
|
|
249
|
+
return errorResult(`File not found: ${filePath}`);
|
|
250
|
+
}
|
|
251
|
+
return errorResult(`Failed to get file: ${res.statusText}`);
|
|
252
|
+
}
|
|
253
|
+
const data = await res.json();
|
|
254
|
+
const content = Buffer.from(data.content, "base64").toString("utf-8");
|
|
255
|
+
return successResult({
|
|
256
|
+
path: filePath,
|
|
257
|
+
content,
|
|
258
|
+
sha: data.sha,
|
|
259
|
+
provider: "github"
|
|
260
|
+
});
|
|
261
|
+
} else {
|
|
262
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
263
|
+
const encodedFilePath = encodeURIComponent(filePath);
|
|
264
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
|
|
265
|
+
if (!res.ok) {
|
|
266
|
+
if (res.status === 404) {
|
|
267
|
+
return errorResult(`File not found: ${filePath}`);
|
|
268
|
+
}
|
|
269
|
+
return errorResult(`Failed to get file: ${res.statusText}`);
|
|
270
|
+
}
|
|
271
|
+
const data = await res.json();
|
|
272
|
+
const content = Buffer.from(data.content, "base64").toString("utf-8");
|
|
273
|
+
return successResult({
|
|
274
|
+
path: filePath,
|
|
275
|
+
content,
|
|
276
|
+
sha: data.content_sha256,
|
|
277
|
+
provider: "gitlab"
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
return errorResult(error instanceof Error ? error.message : "Failed to get file content");
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
var updateFileTool = {
|
|
286
|
+
name: "update_file",
|
|
287
|
+
description: "Update or create a file in the repository. Works with both GitHub and GitLab. SHA is auto-fetched for existing files.",
|
|
288
|
+
inputSchema: z.object({
|
|
289
|
+
path: z.string().describe("Path to the file in the repository"),
|
|
290
|
+
content: z.string().describe("New content for the file"),
|
|
291
|
+
message: z.string().describe("Commit message"),
|
|
292
|
+
branch: z.string().describe("Branch to commit to"),
|
|
293
|
+
sha: z.string().optional().describe("SHA of the file being replaced (auto-fetched if not provided)")
|
|
294
|
+
}),
|
|
295
|
+
handler: async (args) => {
|
|
296
|
+
try {
|
|
297
|
+
const ctx = getGitContext();
|
|
298
|
+
const filePath = args.path;
|
|
299
|
+
const content = args.content;
|
|
300
|
+
const message = args.message;
|
|
301
|
+
const branch = args.branch;
|
|
302
|
+
let sha = args.sha;
|
|
303
|
+
if (ctx.provider === "github") {
|
|
304
|
+
if (!sha) {
|
|
305
|
+
const checkRes = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
|
|
306
|
+
if (checkRes.ok) {
|
|
307
|
+
const existingFile = await checkRes.json();
|
|
308
|
+
sha = existingFile.sha;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
const body = {
|
|
312
|
+
message,
|
|
313
|
+
content: Buffer.from(content).toString("base64"),
|
|
314
|
+
branch
|
|
315
|
+
};
|
|
316
|
+
if (sha) {
|
|
317
|
+
body.sha = sha;
|
|
318
|
+
}
|
|
319
|
+
const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}`, {
|
|
320
|
+
method: "PUT",
|
|
321
|
+
body: JSON.stringify(body)
|
|
322
|
+
});
|
|
323
|
+
if (!res.ok) {
|
|
324
|
+
const err = await res.json();
|
|
325
|
+
return errorResult(err.message || "Failed to update file");
|
|
326
|
+
}
|
|
327
|
+
const data = await res.json();
|
|
328
|
+
return successResult({
|
|
329
|
+
success: true,
|
|
330
|
+
path: filePath,
|
|
331
|
+
sha: data.content.sha,
|
|
332
|
+
commit: data.commit.sha,
|
|
333
|
+
provider: "github"
|
|
334
|
+
});
|
|
335
|
+
} else {
|
|
336
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
337
|
+
const encodedFilePath = encodeURIComponent(filePath);
|
|
338
|
+
let method = "POST";
|
|
339
|
+
if (!sha) {
|
|
340
|
+
const checkRes = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
|
|
341
|
+
if (checkRes.ok) {
|
|
342
|
+
method = "PUT";
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
method = "PUT";
|
|
346
|
+
}
|
|
347
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}`, {
|
|
348
|
+
method,
|
|
349
|
+
body: JSON.stringify({
|
|
350
|
+
branch,
|
|
351
|
+
content,
|
|
352
|
+
commit_message: message,
|
|
353
|
+
encoding: "text"
|
|
354
|
+
})
|
|
355
|
+
});
|
|
356
|
+
if (!res.ok) {
|
|
357
|
+
const err = await res.json();
|
|
358
|
+
return errorResult(err.message || "Failed to update file");
|
|
359
|
+
}
|
|
360
|
+
const data = await res.json();
|
|
361
|
+
return successResult({
|
|
362
|
+
success: true,
|
|
363
|
+
path: filePath,
|
|
364
|
+
sha: data.content_sha256,
|
|
365
|
+
provider: "gitlab"
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return errorResult(error instanceof Error ? error.message : "Failed to update file");
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
var createMergeRequestTool = {
|
|
374
|
+
name: "create_merge_request",
|
|
375
|
+
description: "Create a merge request (GitHub: Pull Request, GitLab: Merge Request). Works with both providers.",
|
|
376
|
+
inputSchema: z.object({
|
|
377
|
+
title: z.string().describe("Title of the merge request"),
|
|
378
|
+
description: z.string().describe("Description/body of the merge request"),
|
|
379
|
+
source_branch: z.string().describe("Branch containing the changes"),
|
|
380
|
+
target_branch: z.string().optional().default("main").describe("Branch to merge into (default: main)")
|
|
381
|
+
}),
|
|
382
|
+
handler: async (args) => {
|
|
383
|
+
try {
|
|
384
|
+
const ctx = getGitContext();
|
|
385
|
+
const title = args.title;
|
|
386
|
+
const description = args.description;
|
|
387
|
+
const sourceBranch = args.source_branch;
|
|
388
|
+
const targetBranch = args.target_branch || "main";
|
|
389
|
+
if (ctx.provider === "github") {
|
|
390
|
+
const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls`, {
|
|
391
|
+
method: "POST",
|
|
392
|
+
body: JSON.stringify({
|
|
393
|
+
title,
|
|
394
|
+
body: description,
|
|
395
|
+
head: sourceBranch,
|
|
396
|
+
base: targetBranch
|
|
397
|
+
})
|
|
398
|
+
});
|
|
399
|
+
if (!res.ok) {
|
|
400
|
+
const err = await res.json();
|
|
401
|
+
return errorResult(err.message || "Failed to create pull request");
|
|
402
|
+
}
|
|
403
|
+
const data = await res.json();
|
|
404
|
+
return successResult({
|
|
405
|
+
success: true,
|
|
406
|
+
mr_number: data.number,
|
|
407
|
+
url: data.html_url,
|
|
408
|
+
state: data.state,
|
|
409
|
+
provider: "github",
|
|
410
|
+
type: "pull_request"
|
|
411
|
+
});
|
|
412
|
+
} else {
|
|
413
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
414
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests`, {
|
|
415
|
+
method: "POST",
|
|
416
|
+
body: JSON.stringify({
|
|
417
|
+
title,
|
|
418
|
+
description,
|
|
419
|
+
source_branch: sourceBranch,
|
|
420
|
+
target_branch: targetBranch
|
|
421
|
+
})
|
|
422
|
+
});
|
|
423
|
+
if (!res.ok) {
|
|
424
|
+
const err = await res.json();
|
|
425
|
+
return errorResult(err.message || "Failed to create merge request");
|
|
426
|
+
}
|
|
427
|
+
const data = await res.json();
|
|
428
|
+
return successResult({
|
|
429
|
+
success: true,
|
|
430
|
+
mr_number: data.iid,
|
|
431
|
+
url: data.web_url,
|
|
432
|
+
state: data.state,
|
|
433
|
+
provider: "gitlab",
|
|
434
|
+
type: "merge_request"
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
} catch (error) {
|
|
438
|
+
return errorResult(error instanceof Error ? error.message : "Failed to create merge request");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var checkMrStatusTool = {
|
|
443
|
+
name: "check_mr_status",
|
|
444
|
+
description: "Check the status of a merge request. Works with both GitHub and GitLab.",
|
|
445
|
+
inputSchema: z.object({
|
|
446
|
+
mr_number: z.number().describe("The merge request number")
|
|
447
|
+
}),
|
|
448
|
+
handler: async (args) => {
|
|
449
|
+
try {
|
|
450
|
+
const ctx = getGitContext();
|
|
451
|
+
const mrNumber = args.mr_number;
|
|
452
|
+
if (ctx.provider === "github") {
|
|
453
|
+
const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls/${mrNumber}`);
|
|
454
|
+
if (!res.ok) {
|
|
455
|
+
if (res.status === 404) {
|
|
456
|
+
return errorResult(`Pull request #${mrNumber} not found`);
|
|
457
|
+
}
|
|
458
|
+
return errorResult(`Failed to get pull request: ${res.statusText}`);
|
|
459
|
+
}
|
|
460
|
+
const data = await res.json();
|
|
461
|
+
let status;
|
|
462
|
+
if (data.merged) {
|
|
463
|
+
status = "merged";
|
|
464
|
+
} else if (data.state === "closed") {
|
|
465
|
+
status = "closed";
|
|
466
|
+
} else {
|
|
467
|
+
status = "open";
|
|
468
|
+
}
|
|
469
|
+
return successResult({
|
|
470
|
+
mr_number: data.number,
|
|
471
|
+
title: data.title,
|
|
472
|
+
status,
|
|
473
|
+
url: data.html_url,
|
|
474
|
+
source_branch: data.head.ref,
|
|
475
|
+
target_branch: data.base.ref,
|
|
476
|
+
created_at: data.created_at,
|
|
477
|
+
merged_at: data.merged_at,
|
|
478
|
+
closed_at: data.closed_at,
|
|
479
|
+
provider: "github"
|
|
480
|
+
});
|
|
481
|
+
} else {
|
|
482
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
483
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests/${mrNumber}`);
|
|
484
|
+
if (!res.ok) {
|
|
485
|
+
if (res.status === 404) {
|
|
486
|
+
return errorResult(`Merge request !${mrNumber} not found`);
|
|
487
|
+
}
|
|
488
|
+
return errorResult(`Failed to get merge request: ${res.statusText}`);
|
|
489
|
+
}
|
|
490
|
+
const data = await res.json();
|
|
491
|
+
let status;
|
|
492
|
+
if (data.state === "merged") {
|
|
493
|
+
status = "merged";
|
|
494
|
+
} else if (data.state === "closed") {
|
|
495
|
+
status = "closed";
|
|
496
|
+
} else {
|
|
497
|
+
status = "open";
|
|
498
|
+
}
|
|
499
|
+
return successResult({
|
|
500
|
+
mr_number: data.iid,
|
|
501
|
+
title: data.title,
|
|
502
|
+
status,
|
|
503
|
+
url: data.web_url,
|
|
504
|
+
source_branch: data.source_branch,
|
|
505
|
+
target_branch: data.target_branch,
|
|
506
|
+
created_at: data.created_at,
|
|
507
|
+
merged_at: data.merged_at,
|
|
508
|
+
closed_at: data.closed_at,
|
|
509
|
+
provider: "gitlab"
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
} catch (error) {
|
|
513
|
+
return errorResult(error instanceof Error ? error.message : "Failed to check MR status");
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
var listOpenMrsTool = {
|
|
518
|
+
name: "list_open_mrs",
|
|
519
|
+
description: "List open merge requests for the repository. Works with both GitHub and GitLab.",
|
|
520
|
+
inputSchema: z.object({
|
|
521
|
+
limit: z.number().optional().default(10).describe("Maximum number of MRs to return (default: 10)")
|
|
522
|
+
}),
|
|
523
|
+
handler: async (args) => {
|
|
524
|
+
try {
|
|
525
|
+
const ctx = getGitContext();
|
|
526
|
+
const limit = args.limit || 10;
|
|
527
|
+
if (ctx.provider === "github") {
|
|
528
|
+
const res = await githubRequest(`/repos/${ctx.repoFullName}/pulls?state=open&per_page=${limit}`);
|
|
529
|
+
if (!res.ok) {
|
|
530
|
+
return errorResult(`Failed to list pull requests: ${res.statusText}`);
|
|
531
|
+
}
|
|
532
|
+
const data = await res.json();
|
|
533
|
+
const mrs = data.map((pr) => ({
|
|
534
|
+
mr_number: pr.number,
|
|
535
|
+
title: pr.title,
|
|
536
|
+
url: pr.html_url,
|
|
537
|
+
source_branch: pr.head.ref,
|
|
538
|
+
target_branch: pr.base.ref,
|
|
539
|
+
created_at: pr.created_at
|
|
540
|
+
}));
|
|
541
|
+
return successResult({
|
|
542
|
+
mrs,
|
|
543
|
+
count: mrs.length,
|
|
544
|
+
provider: "github"
|
|
545
|
+
});
|
|
546
|
+
} else {
|
|
547
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
548
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests?state=opened&per_page=${limit}`);
|
|
549
|
+
if (!res.ok) {
|
|
550
|
+
return errorResult(`Failed to list merge requests: ${res.statusText}`);
|
|
551
|
+
}
|
|
552
|
+
const data = await res.json();
|
|
553
|
+
const mrs = data.map((mr) => ({
|
|
554
|
+
mr_number: mr.iid,
|
|
555
|
+
title: mr.title,
|
|
556
|
+
url: mr.web_url,
|
|
557
|
+
source_branch: mr.source_branch,
|
|
558
|
+
target_branch: mr.target_branch,
|
|
559
|
+
created_at: mr.created_at
|
|
560
|
+
}));
|
|
561
|
+
return successResult({
|
|
562
|
+
mrs,
|
|
563
|
+
count: mrs.length,
|
|
564
|
+
provider: "gitlab"
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
} catch (error) {
|
|
568
|
+
return errorResult(error instanceof Error ? error.message : "Failed to list open MRs");
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
var listRepositoryFilesTool = {
|
|
573
|
+
name: "list_repository_files",
|
|
574
|
+
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.",
|
|
575
|
+
inputSchema: z.object({
|
|
576
|
+
path: z.string().optional().default("").describe("Directory path to list (empty for root)"),
|
|
577
|
+
branch: z.string().optional().describe("Branch to list from (default: main)"),
|
|
578
|
+
recursive: z.boolean().optional().default(false).describe("If true, list all files recursively (may be slow for large repos)")
|
|
579
|
+
}),
|
|
580
|
+
handler: async (args) => {
|
|
581
|
+
try {
|
|
582
|
+
const ctx = getGitContext();
|
|
583
|
+
const dirPath = args.path || "";
|
|
584
|
+
const branch = args.branch || "main";
|
|
585
|
+
const recursive = args.recursive || false;
|
|
586
|
+
if (ctx.provider === "github") {
|
|
587
|
+
if (recursive) {
|
|
588
|
+
const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
|
|
589
|
+
if (!res.ok) {
|
|
590
|
+
return errorResult(`Failed to list repository: ${res.statusText}`);
|
|
591
|
+
}
|
|
592
|
+
const data = await res.json();
|
|
593
|
+
let items = data.tree;
|
|
594
|
+
if (dirPath) {
|
|
595
|
+
const prefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
|
|
596
|
+
items = items.filter((item) => item.path.startsWith(prefix));
|
|
597
|
+
}
|
|
598
|
+
const files = items.filter((item) => item.type === "blob").map((item) => ({
|
|
599
|
+
path: item.path,
|
|
600
|
+
type: "file",
|
|
601
|
+
size: item.size
|
|
602
|
+
}));
|
|
603
|
+
const dirs = items.filter((item) => item.type === "tree").map((item) => ({
|
|
604
|
+
path: item.path,
|
|
605
|
+
type: "directory"
|
|
606
|
+
}));
|
|
607
|
+
return successResult({
|
|
608
|
+
path: dirPath || "/",
|
|
609
|
+
files: files.slice(0, 200),
|
|
610
|
+
directories: dirs.slice(0, 100),
|
|
611
|
+
total_files: files.length,
|
|
612
|
+
total_directories: dirs.length,
|
|
613
|
+
truncated: data.truncated || files.length > 200,
|
|
614
|
+
provider: "github"
|
|
615
|
+
});
|
|
616
|
+
} else {
|
|
617
|
+
const endpoint = dirPath ? `/repos/${ctx.repoFullName}/contents/${dirPath}?ref=${branch}` : `/repos/${ctx.repoFullName}/contents?ref=${branch}`;
|
|
618
|
+
const res = await githubRequest(endpoint);
|
|
619
|
+
if (!res.ok) {
|
|
620
|
+
if (res.status === 404) {
|
|
621
|
+
return errorResult(`Directory not found: ${dirPath || "/"}`);
|
|
622
|
+
}
|
|
623
|
+
return errorResult(`Failed to list directory: ${res.statusText}`);
|
|
624
|
+
}
|
|
625
|
+
const data = await res.json();
|
|
626
|
+
const files = data.filter((item) => item.type === "file").map((item) => ({
|
|
627
|
+
name: item.name,
|
|
628
|
+
path: item.path,
|
|
629
|
+
type: "file",
|
|
630
|
+
size: item.size
|
|
631
|
+
}));
|
|
632
|
+
const directories = data.filter((item) => item.type === "dir").map((item) => ({
|
|
633
|
+
name: item.name,
|
|
634
|
+
path: item.path,
|
|
635
|
+
type: "directory"
|
|
636
|
+
}));
|
|
637
|
+
return successResult({
|
|
638
|
+
path: dirPath || "/",
|
|
639
|
+
files,
|
|
640
|
+
directories,
|
|
641
|
+
provider: "github"
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
646
|
+
const pathParam = dirPath ? `&path=${encodeURIComponent(dirPath)}` : "";
|
|
647
|
+
const recursiveParam = recursive ? "&recursive=true" : "";
|
|
648
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}${pathParam}${recursiveParam}&per_page=100`);
|
|
649
|
+
if (!res.ok) {
|
|
650
|
+
if (res.status === 404) {
|
|
651
|
+
return errorResult(`Directory not found: ${dirPath || "/"}`);
|
|
652
|
+
}
|
|
653
|
+
return errorResult(`Failed to list directory: ${res.statusText}`);
|
|
654
|
+
}
|
|
655
|
+
const data = await res.json();
|
|
656
|
+
const files = data.filter((item) => item.type === "blob").map((item) => ({
|
|
657
|
+
name: item.name,
|
|
658
|
+
path: item.path,
|
|
659
|
+
type: "file"
|
|
660
|
+
}));
|
|
661
|
+
const directories = data.filter((item) => item.type === "tree").map((item) => ({
|
|
662
|
+
name: item.name,
|
|
663
|
+
path: item.path,
|
|
664
|
+
type: "directory"
|
|
665
|
+
}));
|
|
666
|
+
return successResult({
|
|
667
|
+
path: dirPath || "/",
|
|
668
|
+
files,
|
|
669
|
+
directories,
|
|
670
|
+
provider: "gitlab"
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
} catch (error) {
|
|
674
|
+
return errorResult(error instanceof Error ? error.message : "Failed to list repository files");
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
var getRepoTreeTool = {
|
|
679
|
+
name: "get_repo_tree",
|
|
680
|
+
description: "Get a condensed directory-only tree of the repository structure. Returns a nested object showing the folder hierarchy up to a configurable depth. Much faster than recursive list_repository_files for understanding project layout. Use this FIRST to orient yourself, then list_repository_files on specific directories.",
|
|
681
|
+
inputSchema: z.object({
|
|
682
|
+
max_depth: z.number().optional().default(4).describe("Maximum directory depth to return (default: 4)"),
|
|
683
|
+
branch: z.string().optional().describe("Branch to read from (default: main)")
|
|
684
|
+
}),
|
|
685
|
+
handler: async (args) => {
|
|
686
|
+
try {
|
|
687
|
+
const ctx = getGitContext();
|
|
688
|
+
const maxDepth = args.max_depth || 4;
|
|
689
|
+
const branch = args.branch || "main";
|
|
690
|
+
if (ctx.provider === "github") {
|
|
691
|
+
const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
|
|
692
|
+
if (!res.ok) {
|
|
693
|
+
return errorResult(`Failed to get repo tree: ${res.statusText}`);
|
|
694
|
+
}
|
|
695
|
+
const data = await res.json();
|
|
696
|
+
const dirs = data.tree.filter((item) => item.type === "tree").map((item) => item.path);
|
|
697
|
+
const tree = buildNestedTree(dirs, maxDepth);
|
|
698
|
+
const totalDirs = dirs.length;
|
|
699
|
+
return successResult({
|
|
700
|
+
tree,
|
|
701
|
+
total_directories: totalDirs,
|
|
702
|
+
depth: maxDepth,
|
|
703
|
+
provider: "github"
|
|
704
|
+
});
|
|
705
|
+
} else {
|
|
706
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
707
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}&recursive=true&per_page=100`);
|
|
708
|
+
if (!res.ok) {
|
|
709
|
+
return errorResult(`Failed to get repo tree: ${res.statusText}`);
|
|
710
|
+
}
|
|
711
|
+
const data = await res.json();
|
|
712
|
+
const dirs = data.filter((item) => item.type === "tree").map((item) => item.path);
|
|
713
|
+
const tree = buildNestedTree(dirs, maxDepth);
|
|
714
|
+
const totalDirs = dirs.length;
|
|
715
|
+
return successResult({
|
|
716
|
+
tree,
|
|
717
|
+
total_directories: totalDirs,
|
|
718
|
+
depth: maxDepth,
|
|
719
|
+
provider: "gitlab"
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
} catch (error) {
|
|
723
|
+
return errorResult(error instanceof Error ? error.message : "Failed to get repo tree");
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
var searchCodeTool = {
|
|
728
|
+
name: "search_code",
|
|
729
|
+
description: "Search for code patterns, symbols, or imports across the repository. Use this to find existing conventions, verify import paths, or discover reusable utilities before writing code. Rate-limited (~10 req/min on GitHub) \u2014 use sparingly and with specific queries.",
|
|
730
|
+
inputSchema: z.object({
|
|
731
|
+
query: z.string().describe('Search terms (e.g., "import { Button }", "useMemo", "className={styles.")'),
|
|
732
|
+
path_filter: z.string().optional().describe('Restrict search to a directory (e.g., "client/src/components")'),
|
|
733
|
+
extension: z.string().optional().describe('Filter by file extension (e.g., "tsx", "ts", "css")')
|
|
734
|
+
}),
|
|
735
|
+
handler: async (args) => {
|
|
736
|
+
try {
|
|
737
|
+
const ctx = getGitContext();
|
|
738
|
+
const query = args.query;
|
|
739
|
+
const pathFilter = args.path_filter;
|
|
740
|
+
const extension = args.extension;
|
|
741
|
+
if (ctx.provider === "github") {
|
|
742
|
+
let q = `${query} repo:${ctx.repoFullName}`;
|
|
743
|
+
if (pathFilter)
|
|
744
|
+
q += ` path:${pathFilter}`;
|
|
745
|
+
if (extension)
|
|
746
|
+
q += ` extension:${extension}`;
|
|
747
|
+
const res = await githubRequest(`/search/code?q=${encodeURIComponent(q)}&per_page=10`, {
|
|
748
|
+
headers: {
|
|
749
|
+
Accept: "application/vnd.github.text-match+json"
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
if (!res.ok) {
|
|
753
|
+
const err = await res.json();
|
|
754
|
+
return errorResult(err.message || `Code search failed: ${res.status}`);
|
|
755
|
+
}
|
|
756
|
+
const data = await res.json();
|
|
757
|
+
const results = data.items.map((item) => ({
|
|
758
|
+
path: item.path,
|
|
759
|
+
score: item.score,
|
|
760
|
+
matched_lines: (item.text_matches ?? []).map((m) => m.fragment),
|
|
761
|
+
url: item.html_url
|
|
762
|
+
}));
|
|
763
|
+
return successResult({
|
|
764
|
+
total_count: data.total_count,
|
|
765
|
+
results,
|
|
766
|
+
provider: "github"
|
|
767
|
+
});
|
|
768
|
+
} else {
|
|
769
|
+
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
770
|
+
const searchQuery = pathFilter ? `${query} filename:${pathFilter}` : query;
|
|
771
|
+
const res = await gitlabRequest(`/projects/${encodedPath}/search?scope=blobs&search=${encodeURIComponent(searchQuery)}&per_page=10`);
|
|
772
|
+
if (!res.ok) {
|
|
773
|
+
const err = await res.json();
|
|
774
|
+
return errorResult(err.message || `Code search failed: ${res.status}`);
|
|
775
|
+
}
|
|
776
|
+
const data = await res.json();
|
|
777
|
+
const results = data.map((item) => ({
|
|
778
|
+
path: item.path,
|
|
779
|
+
matched_lines: [item.data],
|
|
780
|
+
startline: item.startline
|
|
781
|
+
}));
|
|
782
|
+
return successResult({
|
|
783
|
+
total_count: results.length,
|
|
784
|
+
results,
|
|
785
|
+
provider: "gitlab"
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
} catch (error) {
|
|
789
|
+
return errorResult(error instanceof Error ? error.message : "Code search failed");
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
function buildNestedTree(dirs, maxDepth) {
|
|
794
|
+
const tree = {};
|
|
795
|
+
for (const dir of dirs) {
|
|
796
|
+
const parts = dir.split("/");
|
|
797
|
+
if (parts.length > maxDepth)
|
|
798
|
+
continue;
|
|
799
|
+
let current = tree;
|
|
800
|
+
for (const part of parts) {
|
|
801
|
+
const key = `${part}/`;
|
|
802
|
+
if (!current[key]) {
|
|
803
|
+
current[key] = {};
|
|
804
|
+
}
|
|
805
|
+
current = current[key];
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return tree;
|
|
809
|
+
}
|
|
810
|
+
var gitTools = [
|
|
811
|
+
createBranchTool,
|
|
812
|
+
getFileContentTool,
|
|
813
|
+
updateFileTool,
|
|
814
|
+
createMergeRequestTool,
|
|
815
|
+
checkMrStatusTool,
|
|
816
|
+
listOpenMrsTool,
|
|
817
|
+
listRepositoryFilesTool,
|
|
818
|
+
getRepoTreeTool,
|
|
819
|
+
searchCodeTool
|
|
820
|
+
];
|
|
821
|
+
|
|
117
822
|
// ../../libraries/mcp-tools/dist/handlers.js
|
|
118
823
|
function createApiHandler(method, endpoint, buildParams) {
|
|
119
824
|
return async (args) => {
|
|
@@ -260,6 +965,10 @@ function registerAllToolHandlers() {
|
|
|
260
965
|
toolHandlers.set("list_remediations_by_status", createApiHandler("GET", "/remediations/by-status"));
|
|
261
966
|
toolHandlers.set("get_remediation_by_pr", createApiHandler("GET", (args) => `/remediations/by-pr/${args.pr_number}`));
|
|
262
967
|
toolHandlers.set("update_remediation_status", createApiHandler("PATCH", (args) => `/remediations/${args.remediation_id}/status`));
|
|
968
|
+
toolHandlers.set("update_remediation", createApiHandler("PATCH", (args) => `/remediations/${args.remediation_id}`, (args) => {
|
|
969
|
+
const { remediation_id, ...rest } = args;
|
|
970
|
+
return rest;
|
|
971
|
+
}));
|
|
263
972
|
toolHandlers.set("get_site_knowledge", createApiHandler("GET", "/knowledge"));
|
|
264
973
|
toolHandlers.set("add_site_knowledge", createApiHandler("POST", "/knowledge", (args) => ({
|
|
265
974
|
...args,
|
|
@@ -284,6 +993,11 @@ function registerAllToolHandlers() {
|
|
|
284
993
|
};
|
|
285
994
|
}));
|
|
286
995
|
toolHandlers.set("get_upgrade_options", createApiHandler("GET", "/upgrade-options"));
|
|
996
|
+
for (const tool of gitTools) {
|
|
997
|
+
if (tool.handler) {
|
|
998
|
+
toolHandlers.set(tool.name, tool.handler);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
287
1001
|
}
|
|
288
1002
|
registerAllToolHandlers();
|
|
289
1003
|
|
|
@@ -474,32 +1188,14 @@ import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind2 } from "zod/v3";
|
|
|
474
1188
|
var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
|
|
475
1189
|
|
|
476
1190
|
// ../../libraries/mcp-tools/dist/tools/analyzeFlow.js
|
|
477
|
-
import { z } from "zod";
|
|
478
|
-
|
|
479
|
-
// ../../libraries/mcp-tools/dist/tools/_utils.js
|
|
480
|
-
function errorResult(message) {
|
|
481
|
-
return {
|
|
482
|
-
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
483
|
-
isError: true
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
function successResult(data) {
|
|
487
|
-
return {
|
|
488
|
-
content: [{ type: "text", text: JSON.stringify(data) }]
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
function apiNotConfiguredResult() {
|
|
492
|
-
return errorResult("API not configured");
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// ../../libraries/mcp-tools/dist/tools/analyzeFlow.js
|
|
1191
|
+
import { z as z2 } from "zod";
|
|
496
1192
|
var analyzeFlowTool = {
|
|
497
1193
|
name: "analyze_flow",
|
|
498
1194
|
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.",
|
|
499
|
-
inputSchema:
|
|
500
|
-
start_page:
|
|
501
|
-
end_page:
|
|
502
|
-
days:
|
|
1195
|
+
inputSchema: z2.object({
|
|
1196
|
+
start_page: z2.string().optional().describe("Starting page path (e.g., /pricing). Partial match supported."),
|
|
1197
|
+
end_page: z2.string().optional().describe("Ending page path (e.g., /checkout). Partial match supported."),
|
|
1198
|
+
days: z2.number().optional().default(7).describe("Number of days to analyze (default: 7)")
|
|
503
1199
|
}),
|
|
504
1200
|
handler: async (args) => {
|
|
505
1201
|
const { start_page, end_page, days } = args;
|
|
@@ -522,13 +1218,13 @@ var analyzeFlowTool = {
|
|
|
522
1218
|
};
|
|
523
1219
|
|
|
524
1220
|
// ../../libraries/mcp-tools/dist/tools/analyzeFunnel.js
|
|
525
|
-
import { z as
|
|
1221
|
+
import { z as z3 } from "zod";
|
|
526
1222
|
var analyzeFunnelTool = {
|
|
527
1223
|
name: "analyze_funnel",
|
|
528
1224
|
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.',
|
|
529
|
-
inputSchema:
|
|
530
|
-
steps:
|
|
531
|
-
days:
|
|
1225
|
+
inputSchema: z3.object({
|
|
1226
|
+
steps: z3.array(z3.string()).min(2).describe('Ordered page paths forming the funnel (e.g., ["/cart", "/checkout", "/payment"])'),
|
|
1227
|
+
days: z3.number().optional().default(7).describe("Number of days to analyze (default: 7)")
|
|
532
1228
|
}),
|
|
533
1229
|
handler: async (args) => {
|
|
534
1230
|
const { steps, days } = args;
|
|
@@ -550,26 +1246,26 @@ var analyzeFunnelTool = {
|
|
|
550
1246
|
};
|
|
551
1247
|
|
|
552
1248
|
// ../../libraries/mcp-tools/dist/tools/compareCohorts.js
|
|
553
|
-
import { z as
|
|
554
|
-
var cohortFilterSchema =
|
|
555
|
-
device:
|
|
556
|
-
outcome:
|
|
557
|
-
pattern:
|
|
558
|
-
page_path:
|
|
559
|
-
has_friction:
|
|
560
|
-
has_rage_clicks:
|
|
1249
|
+
import { z as z4 } from "zod";
|
|
1250
|
+
var cohortFilterSchema = z4.object({
|
|
1251
|
+
device: z4.enum(["desktop", "tablet", "mobile"]).optional().describe("Filter by device type"),
|
|
1252
|
+
outcome: z4.enum(["COMPLETED", "STRUGGLED", "BLOCKED", "DISENGAGED"]).optional().describe("Filter by session outcome"),
|
|
1253
|
+
pattern: z4.string().optional().describe("Filter by behavioral pattern"),
|
|
1254
|
+
page_path: z4.string().optional().describe("Filter by page path"),
|
|
1255
|
+
has_friction: z4.boolean().optional().describe("Filter for sessions with friction"),
|
|
1256
|
+
has_rage_clicks: z4.boolean().optional().describe("Filter for sessions with rage clicks")
|
|
561
1257
|
});
|
|
562
1258
|
var compareCohortsTool = {
|
|
563
1259
|
name: "compare_cohorts",
|
|
564
1260
|
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?"',
|
|
565
|
-
inputSchema:
|
|
1261
|
+
inputSchema: z4.object({
|
|
566
1262
|
cohort_a: cohortFilterSchema.describe('Filters for cohort A, e.g. {"device": "mobile"} or {"outcome": "COMPLETED"}'),
|
|
567
1263
|
cohort_b: cohortFilterSchema.describe('Filters for cohort B, e.g. {"device": "desktop"} or {"outcome": "BLOCKED"}'),
|
|
568
|
-
cohort_a_label:
|
|
569
|
-
cohort_b_label:
|
|
570
|
-
page_path:
|
|
571
|
-
date_from:
|
|
572
|
-
date_to:
|
|
1264
|
+
cohort_a_label: z4.string().optional().describe("Human label for cohort A"),
|
|
1265
|
+
cohort_b_label: z4.string().optional().describe("Human label for cohort B"),
|
|
1266
|
+
page_path: z4.string().optional().describe("Scope comparison to a specific page"),
|
|
1267
|
+
date_from: z4.string().optional().describe("Start date (ISO format)"),
|
|
1268
|
+
date_to: z4.string().optional().describe("End date (ISO format)")
|
|
573
1269
|
}),
|
|
574
1270
|
handler: async (args) => {
|
|
575
1271
|
const { cohort_a, cohort_b, cohort_a_label, cohort_b_label, page_path, date_from, date_to } = args;
|
|
@@ -593,19 +1289,19 @@ var compareCohortsTool = {
|
|
|
593
1289
|
};
|
|
594
1290
|
|
|
595
1291
|
// ../../libraries/mcp-tools/dist/tools/comparePeriods.js
|
|
596
|
-
import { z as
|
|
1292
|
+
import { z as z5 } from "zod";
|
|
597
1293
|
var comparePeriodsTool = {
|
|
598
1294
|
name: "compare_periods",
|
|
599
1295
|
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.",
|
|
600
|
-
inputSchema:
|
|
601
|
-
page_path:
|
|
602
|
-
period_a:
|
|
603
|
-
from:
|
|
604
|
-
to:
|
|
1296
|
+
inputSchema: z5.object({
|
|
1297
|
+
page_path: z5.string().describe("Page path to compare"),
|
|
1298
|
+
period_a: z5.object({
|
|
1299
|
+
from: z5.string().describe("Start date for period A (ISO format)"),
|
|
1300
|
+
to: z5.string().describe("End date for period A (ISO format)")
|
|
605
1301
|
}).describe("First time period"),
|
|
606
|
-
period_b:
|
|
607
|
-
from:
|
|
608
|
-
to:
|
|
1302
|
+
period_b: z5.object({
|
|
1303
|
+
from: z5.string().describe("Start date for period B (ISO format)"),
|
|
1304
|
+
to: z5.string().describe("End date for period B (ISO format)")
|
|
609
1305
|
}).describe("Second time period")
|
|
610
1306
|
}),
|
|
611
1307
|
handler: async (args) => {
|
|
@@ -626,14 +1322,14 @@ var comparePeriodsTool = {
|
|
|
626
1322
|
};
|
|
627
1323
|
|
|
628
1324
|
// ../../libraries/mcp-tools/dist/tools/detectDrift.js
|
|
629
|
-
import { z as
|
|
1325
|
+
import { z as z6 } from "zod";
|
|
630
1326
|
var detectDriftTool = {
|
|
631
1327
|
name: "detect_drift",
|
|
632
1328
|
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.",
|
|
633
|
-
inputSchema:
|
|
634
|
-
page_path:
|
|
635
|
-
days:
|
|
636
|
-
window_size:
|
|
1329
|
+
inputSchema: z6.object({
|
|
1330
|
+
page_path: z6.string().optional().describe("Scope drift detection to a specific page"),
|
|
1331
|
+
days: z6.number().optional().default(30).describe("Number of days to analyze (default 30)"),
|
|
1332
|
+
window_size: z6.number().optional().default(7).describe("Size of each time window in days (default 7)")
|
|
637
1333
|
}),
|
|
638
1334
|
handler: async (args) => {
|
|
639
1335
|
const { page_path, days, window_size } = args;
|
|
@@ -653,15 +1349,15 @@ var detectDriftTool = {
|
|
|
653
1349
|
};
|
|
654
1350
|
|
|
655
1351
|
// ../../libraries/mcp-tools/dist/tools/detectRegressions.js
|
|
656
|
-
import { z as
|
|
1352
|
+
import { z as z7 } from "zod";
|
|
657
1353
|
var detectRegressionsTool = {
|
|
658
1354
|
name: "detect_regressions",
|
|
659
1355
|
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.",
|
|
660
|
-
inputSchema:
|
|
661
|
-
window_hours:
|
|
662
|
-
limit:
|
|
663
|
-
offset:
|
|
664
|
-
sort_by:
|
|
1356
|
+
inputSchema: z7.object({
|
|
1357
|
+
window_hours: z7.number().optional().default(24).describe("How many recent hours to compare against baseline (default 24)"),
|
|
1358
|
+
limit: z7.number().optional().default(50).describe("Number of pages to check per request (default 50, max 100)"),
|
|
1359
|
+
offset: z7.number().optional().default(0).describe("Skip this many pages for pagination (default 0)"),
|
|
1360
|
+
sort_by: z7.enum(["traffic", "low_traffic"]).optional().default("traffic").describe("Sort order: 'traffic' (default) or 'low_traffic' to check overlooked pages")
|
|
665
1361
|
}),
|
|
666
1362
|
handler: async (args) => {
|
|
667
1363
|
const { window_hours, limit, offset, sort_by } = args;
|
|
@@ -682,12 +1378,12 @@ var detectRegressionsTool = {
|
|
|
682
1378
|
};
|
|
683
1379
|
|
|
684
1380
|
// ../../libraries/mcp-tools/dist/tools/diagnostic.js
|
|
685
|
-
import { z as
|
|
1381
|
+
import { z as z8 } from "zod";
|
|
686
1382
|
var runFullDiagnosticTool = {
|
|
687
1383
|
name: "run_full_diagnostic",
|
|
688
1384
|
description: "Get a quick site health overview with 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, pages needing attention, and investigation order.",
|
|
689
|
-
inputSchema:
|
|
690
|
-
days:
|
|
1385
|
+
inputSchema: z8.object({
|
|
1386
|
+
days: z8.number().optional().default(7).describe("Number of days to analyze (default: 7)")
|
|
691
1387
|
}),
|
|
692
1388
|
handler: async (args) => {
|
|
693
1389
|
const { days } = args;
|
|
@@ -705,13 +1401,13 @@ var runFullDiagnosticTool = {
|
|
|
705
1401
|
};
|
|
706
1402
|
|
|
707
1403
|
// ../../libraries/mcp-tools/dist/tools/discoverPersonas.js
|
|
708
|
-
import { z as
|
|
1404
|
+
import { z as z9 } from "zod";
|
|
709
1405
|
var discoverPersonasTool = {
|
|
710
1406
|
name: "discover_personas",
|
|
711
1407
|
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.",
|
|
712
|
-
inputSchema:
|
|
713
|
-
page_path:
|
|
714
|
-
num_personas:
|
|
1408
|
+
inputSchema: z9.object({
|
|
1409
|
+
page_path: z9.string().optional().describe("Scope persona discovery to a specific page"),
|
|
1410
|
+
num_personas: z9.number().optional().default(4).describe("Number of personas to discover (default 4, max 8)")
|
|
715
1411
|
}),
|
|
716
1412
|
handler: async (args) => {
|
|
717
1413
|
const { page_path, num_personas } = args;
|
|
@@ -730,14 +1426,14 @@ var discoverPersonasTool = {
|
|
|
730
1426
|
};
|
|
731
1427
|
|
|
732
1428
|
// ../../libraries/mcp-tools/dist/tools/getAnomalies.js
|
|
733
|
-
import { z as
|
|
1429
|
+
import { z as z10 } from "zod";
|
|
734
1430
|
var getAnomaliesTool = {
|
|
735
1431
|
name: "get_anomalies",
|
|
736
1432
|
description: "Detect anomalous sessions and frustration spikes. Finds sessions that deviate from normal behavior patterns.",
|
|
737
|
-
inputSchema:
|
|
738
|
-
threshold:
|
|
739
|
-
days:
|
|
740
|
-
page_path:
|
|
1433
|
+
inputSchema: z10.object({
|
|
1434
|
+
threshold: z10.number().optional().default(0.35).describe("Anomaly threshold (0-1, default: 0.35)"),
|
|
1435
|
+
days: z10.number().optional().default(7).describe("Days to analyze for frustration spikes (default: 7)"),
|
|
1436
|
+
page_path: z10.string().optional().describe("Optional: filter to a specific page")
|
|
741
1437
|
}),
|
|
742
1438
|
handler: async (args) => {
|
|
743
1439
|
const { threshold, days, page_path } = args;
|
|
@@ -757,15 +1453,15 @@ var getAnomaliesTool = {
|
|
|
757
1453
|
};
|
|
758
1454
|
|
|
759
1455
|
// ../../libraries/mcp-tools/dist/tools/getConsoleErrors.js
|
|
760
|
-
import { z as
|
|
1456
|
+
import { z as z11 } from "zod";
|
|
761
1457
|
var getConsoleErrorsTool = {
|
|
762
1458
|
name: "get_console_errors",
|
|
763
1459
|
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.",
|
|
764
|
-
inputSchema:
|
|
765
|
-
page_path:
|
|
766
|
-
session_id:
|
|
767
|
-
severity:
|
|
768
|
-
limit:
|
|
1460
|
+
inputSchema: z11.object({
|
|
1461
|
+
page_path: z11.string().optional().describe("Filter errors to a specific page path"),
|
|
1462
|
+
session_id: z11.string().optional().describe("Filter errors to a specific session"),
|
|
1463
|
+
severity: z11.enum(["error", "warn", "all"]).optional().default("error").describe('Filter by severity: "error" (default), "warn", or "all"'),
|
|
1464
|
+
limit: z11.number().optional().default(200).describe("Max log documents to scan (default 200)")
|
|
769
1465
|
}),
|
|
770
1466
|
handler: async (args) => {
|
|
771
1467
|
const { page_path, session_id, severity, limit } = args;
|
|
@@ -786,17 +1482,17 @@ var getConsoleErrorsTool = {
|
|
|
786
1482
|
};
|
|
787
1483
|
|
|
788
1484
|
// ../../libraries/mcp-tools/dist/tools/getCustomEvents.js
|
|
789
|
-
import { z as
|
|
1485
|
+
import { z as z12 } from "zod";
|
|
790
1486
|
var getCustomEventsTool = {
|
|
791
1487
|
name: "get_custom_events",
|
|
792
1488
|
description: "Query custom events tracked via Recapt.track(). Returns event occurrence counts, which pages they fire on, property distributions, and temporal patterns. Use to answer 'what custom events are being tracked?', 'how often does event X fire?', 'what properties does event Y have?', or 'which pages trigger the most events?'. These are app-specific events the developer explicitly tracks (purchases, signups, feature usage, etc.), not automatically captured behavioral data.",
|
|
793
|
-
inputSchema:
|
|
794
|
-
event_name:
|
|
795
|
-
page_path:
|
|
796
|
-
session_id:
|
|
797
|
-
property_filters:
|
|
798
|
-
days:
|
|
799
|
-
limit:
|
|
1489
|
+
inputSchema: z12.object({
|
|
1490
|
+
event_name: z12.string().optional().describe("Filter by event name (case-insensitive partial match). If omitted, returns all event types."),
|
|
1491
|
+
page_path: z12.string().optional().describe("Filter events to a specific page path (partial match)"),
|
|
1492
|
+
session_id: z12.string().optional().describe("Filter events to a specific session"),
|
|
1493
|
+
property_filters: z12.record(z12.string(), z12.union([z12.string(), z12.number(), z12.boolean(), z12.null()])).optional().describe("Filter events by property values, e.g. { plan: 'pro', amount: 99 }"),
|
|
1494
|
+
days: z12.number().optional().describe("Look back N days (default: 30)"),
|
|
1495
|
+
limit: z12.number().optional().describe("Max documents to scan (default: 500, max: 2000)")
|
|
800
1496
|
}),
|
|
801
1497
|
handler: async (args) => {
|
|
802
1498
|
const { event_name, page_path, session_id, property_filters, days, limit } = args;
|
|
@@ -823,8 +1519,8 @@ var getCustomEventsTool = {
|
|
|
823
1519
|
var listEventNamesTool = {
|
|
824
1520
|
name: "list_event_names",
|
|
825
1521
|
description: "List all unique custom event names tracked in the last N days. Use this to discover what events are available before querying specific events.",
|
|
826
|
-
inputSchema:
|
|
827
|
-
days:
|
|
1522
|
+
inputSchema: z12.object({
|
|
1523
|
+
days: z12.number().optional().describe("Look back N days (default: 30)")
|
|
828
1524
|
}),
|
|
829
1525
|
handler: async (args) => {
|
|
830
1526
|
const { days } = args;
|
|
@@ -840,16 +1536,16 @@ var listEventNamesTool = {
|
|
|
840
1536
|
};
|
|
841
1537
|
|
|
842
1538
|
// ../../libraries/mcp-tools/dist/tools/getSessionComments.js
|
|
843
|
-
import { z as
|
|
1539
|
+
import { z as z13 } from "zod";
|
|
844
1540
|
var getSessionCommentsTool = {
|
|
845
1541
|
name: "get_session_comments",
|
|
846
1542
|
description: "Query user comments and feedback submitted during sessions. These are explicit comments users submit via the feedback widget or Recapt.comment() API - direct user voice about their experience. Use to find sessions where users reported issues, understand user sentiment, or identify pages generating the most feedback. Comments often contain valuable context about what frustrated or confused users.",
|
|
847
|
-
inputSchema:
|
|
848
|
-
session_id:
|
|
849
|
-
page_path:
|
|
850
|
-
search:
|
|
851
|
-
days:
|
|
852
|
-
limit:
|
|
1543
|
+
inputSchema: z13.object({
|
|
1544
|
+
session_id: z13.string().optional().describe("Filter comments to a specific session"),
|
|
1545
|
+
page_path: z13.string().optional().describe("Filter comments to a specific page path (partial match)"),
|
|
1546
|
+
search: z13.string().optional().describe("Search comments by text content (case-insensitive partial match)"),
|
|
1547
|
+
days: z13.number().optional().describe("Look back N days (default: 30)"),
|
|
1548
|
+
limit: z13.number().optional().describe("Max comments to return (default: 100, max: 500)")
|
|
853
1549
|
}),
|
|
854
1550
|
handler: async (args) => {
|
|
855
1551
|
const { session_id, page_path, search, days, limit } = args;
|
|
@@ -872,8 +1568,8 @@ var getSessionCommentsTool = {
|
|
|
872
1568
|
var getCommentStatsTool = {
|
|
873
1569
|
name: "get_comment_stats",
|
|
874
1570
|
description: "Get statistics about user comments/feedback. Returns total comment count, which pages receive the most feedback, and daily trend. Use to understand feedback volume and identify pages where users are most vocal.",
|
|
875
|
-
inputSchema:
|
|
876
|
-
days:
|
|
1571
|
+
inputSchema: z13.object({
|
|
1572
|
+
days: z13.number().optional().describe("Look back N days (default: 30)")
|
|
877
1573
|
}),
|
|
878
1574
|
handler: async (args) => {
|
|
879
1575
|
const { days } = args;
|
|
@@ -889,12 +1585,12 @@ var getCommentStatsTool = {
|
|
|
889
1585
|
};
|
|
890
1586
|
|
|
891
1587
|
// ../../libraries/mcp-tools/dist/tools/getDeadClicks.js
|
|
892
|
-
import { z as
|
|
1588
|
+
import { z as z14 } from "zod";
|
|
893
1589
|
var getDeadClicksTool = {
|
|
894
1590
|
name: "get_dead_clicks",
|
|
895
1591
|
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.",
|
|
896
|
-
inputSchema:
|
|
897
|
-
page_path:
|
|
1592
|
+
inputSchema: z14.object({
|
|
1593
|
+
page_path: z14.string().describe("Page path to check for dead clicks (required)")
|
|
898
1594
|
}),
|
|
899
1595
|
handler: async (args) => {
|
|
900
1596
|
const { page_path } = args;
|
|
@@ -912,11 +1608,11 @@ var getDeadClicksTool = {
|
|
|
912
1608
|
};
|
|
913
1609
|
|
|
914
1610
|
// ../../libraries/mcp-tools/dist/tools/getDomains.js
|
|
915
|
-
import { z as
|
|
1611
|
+
import { z as z15 } from "zod";
|
|
916
1612
|
var getDomainsTool = {
|
|
917
1613
|
name: "get_domains",
|
|
918
1614
|
description: "Get the list of domains configured for the organization. Domains represent different websites or applications being tracked.",
|
|
919
|
-
inputSchema:
|
|
1615
|
+
inputSchema: z15.object({}),
|
|
920
1616
|
handler: async () => {
|
|
921
1617
|
if (!isApiConfigured()) {
|
|
922
1618
|
return apiNotConfiguredResult();
|
|
@@ -930,13 +1626,13 @@ var getDomainsTool = {
|
|
|
930
1626
|
};
|
|
931
1627
|
|
|
932
1628
|
// ../../libraries/mcp-tools/dist/tools/getElementFriction.js
|
|
933
|
-
import { z as
|
|
1629
|
+
import { z as z16 } from "zod";
|
|
934
1630
|
var getElementFrictionTool = {
|
|
935
1631
|
name: "get_element_friction",
|
|
936
1632
|
description: "Get per-element friction data for a page. Shows which elements cause user frustration, with click counts and dominant frustration signals.",
|
|
937
|
-
inputSchema:
|
|
938
|
-
page_path:
|
|
939
|
-
selector_filter:
|
|
1633
|
+
inputSchema: z16.object({
|
|
1634
|
+
page_path: z16.string().describe("Page path to analyze (e.g., /checkout)"),
|
|
1635
|
+
selector_filter: z16.string().optional().describe("Optional: filter elements by selector substring")
|
|
940
1636
|
}),
|
|
941
1637
|
handler: async (args) => {
|
|
942
1638
|
const { page_path, selector_filter } = args;
|
|
@@ -955,14 +1651,14 @@ var getElementFrictionTool = {
|
|
|
955
1651
|
};
|
|
956
1652
|
|
|
957
1653
|
// ../../libraries/mcp-tools/dist/tools/getFlowFriction.js
|
|
958
|
-
import { z as
|
|
1654
|
+
import { z as z17 } from "zod";
|
|
959
1655
|
var getFlowFrictionTool = {
|
|
960
1656
|
name: "get_flow_friction",
|
|
961
1657
|
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.",
|
|
962
|
-
inputSchema:
|
|
963
|
-
page_path:
|
|
964
|
-
days:
|
|
965
|
-
limit:
|
|
1658
|
+
inputSchema: z17.object({
|
|
1659
|
+
page_path: z17.string().optional().describe("Focus on flows involving this page (e.g., /checkout)"),
|
|
1660
|
+
days: z17.number().optional().default(7).describe("Number of days to analyze (default: 7)"),
|
|
1661
|
+
limit: z17.number().optional().default(10).describe("Maximum number of flows to return (default: 10)")
|
|
966
1662
|
}),
|
|
967
1663
|
handler: async (args) => {
|
|
968
1664
|
const { page_path, days, limit } = args;
|
|
@@ -982,12 +1678,12 @@ var getFlowFrictionTool = {
|
|
|
982
1678
|
};
|
|
983
1679
|
|
|
984
1680
|
// ../../libraries/mcp-tools/dist/tools/getFormFriction.js
|
|
985
|
-
import { z as
|
|
1681
|
+
import { z as z18 } from "zod";
|
|
986
1682
|
var getFormFrictionTool = {
|
|
987
1683
|
name: "get_form_friction",
|
|
988
1684
|
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.",
|
|
989
|
-
inputSchema:
|
|
990
|
-
page_path:
|
|
1685
|
+
inputSchema: z18.object({
|
|
1686
|
+
page_path: z18.string().describe("Page path containing the form to analyze (required)")
|
|
991
1687
|
}),
|
|
992
1688
|
handler: async (args) => {
|
|
993
1689
|
const { page_path } = args;
|
|
@@ -1005,13 +1701,13 @@ var getFormFrictionTool = {
|
|
|
1005
1701
|
};
|
|
1006
1702
|
|
|
1007
1703
|
// ../../libraries/mcp-tools/dist/tools/getJourneyPatterns.js
|
|
1008
|
-
import { z as
|
|
1704
|
+
import { z as z19 } from "zod";
|
|
1009
1705
|
var getJourneyPatternsTool = {
|
|
1010
1706
|
name: "get_journey_patterns",
|
|
1011
1707
|
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.",
|
|
1012
|
-
inputSchema:
|
|
1013
|
-
page_path:
|
|
1014
|
-
days:
|
|
1708
|
+
inputSchema: z19.object({
|
|
1709
|
+
page_path: z19.string().optional().describe("Filter to transitions involving this page (e.g., /pricing)"),
|
|
1710
|
+
days: z19.number().optional().default(7).describe("Number of days to analyze (default: 7)")
|
|
1015
1711
|
}),
|
|
1016
1712
|
handler: async (args) => {
|
|
1017
1713
|
const { page_path, days } = args;
|
|
@@ -1030,13 +1726,13 @@ var getJourneyPatternsTool = {
|
|
|
1030
1726
|
};
|
|
1031
1727
|
|
|
1032
1728
|
// ../../libraries/mcp-tools/dist/tools/getPageMetrics.js
|
|
1033
|
-
import { z as
|
|
1729
|
+
import { z as z20 } from "zod";
|
|
1034
1730
|
var getPageMetricsTool = {
|
|
1035
1731
|
name: "get_page_metrics",
|
|
1036
1732
|
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.",
|
|
1037
|
-
inputSchema:
|
|
1038
|
-
page_path:
|
|
1039
|
-
days:
|
|
1733
|
+
inputSchema: z20.object({
|
|
1734
|
+
page_path: z20.string().optional().describe("Page path to get metrics for (e.g., /checkout). Omit to get all pages."),
|
|
1735
|
+
days: z20.number().optional().default(7).describe("Number of days to look back (default: 7)")
|
|
1040
1736
|
}),
|
|
1041
1737
|
handler: async (args) => {
|
|
1042
1738
|
const { page_path, days } = args;
|
|
@@ -1055,13 +1751,13 @@ var getPageMetricsTool = {
|
|
|
1055
1751
|
};
|
|
1056
1752
|
|
|
1057
1753
|
// ../../libraries/mcp-tools/dist/tools/getPageTrends.js
|
|
1058
|
-
import { z as
|
|
1754
|
+
import { z as z21 } from "zod";
|
|
1059
1755
|
var getPageTrendsTool = {
|
|
1060
1756
|
name: "get_page_trends",
|
|
1061
1757
|
description: "Get daily behavioral trends for a page. Shows how frustration, confusion, and confidence change over time.",
|
|
1062
|
-
inputSchema:
|
|
1063
|
-
page_path:
|
|
1064
|
-
days:
|
|
1758
|
+
inputSchema: z21.object({
|
|
1759
|
+
page_path: z21.string().describe("Page path to analyze (e.g., /checkout)"),
|
|
1760
|
+
days: z21.number().optional().default(14).describe("Number of days to look back (default: 14)")
|
|
1065
1761
|
}),
|
|
1066
1762
|
handler: async (args) => {
|
|
1067
1763
|
const { page_path, days } = args;
|
|
@@ -1080,13 +1776,13 @@ var getPageTrendsTool = {
|
|
|
1080
1776
|
};
|
|
1081
1777
|
|
|
1082
1778
|
// ../../libraries/mcp-tools/dist/tools/getSessionDetails.js
|
|
1083
|
-
import { z as
|
|
1779
|
+
import { z as z22 } from "zod";
|
|
1084
1780
|
var getSessionDetailsTool = {
|
|
1085
1781
|
name: "get_session_details",
|
|
1086
1782
|
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).",
|
|
1087
|
-
inputSchema:
|
|
1088
|
-
session_id:
|
|
1089
|
-
session_ids:
|
|
1783
|
+
inputSchema: z22.object({
|
|
1784
|
+
session_id: z22.string().optional().describe("Single session ID to get details for"),
|
|
1785
|
+
session_ids: z22.preprocess((val) => {
|
|
1090
1786
|
if (typeof val === "string") {
|
|
1091
1787
|
try {
|
|
1092
1788
|
return JSON.parse(val);
|
|
@@ -1095,7 +1791,7 @@ var getSessionDetailsTool = {
|
|
|
1095
1791
|
}
|
|
1096
1792
|
}
|
|
1097
1793
|
return val;
|
|
1098
|
-
},
|
|
1794
|
+
}, z22.array(z22.string()).max(20).optional()).describe("Array of session IDs to get details for (max 20)")
|
|
1099
1795
|
}),
|
|
1100
1796
|
handler: async (args) => {
|
|
1101
1797
|
const { session_id, session_ids } = args;
|
|
@@ -1124,13 +1820,13 @@ var getSessionDetailsTool = {
|
|
|
1124
1820
|
};
|
|
1125
1821
|
|
|
1126
1822
|
// ../../libraries/mcp-tools/dist/tools/getSessionPages.js
|
|
1127
|
-
import { z as
|
|
1823
|
+
import { z as z23 } from "zod";
|
|
1128
1824
|
var getSessionPagesTool = {
|
|
1129
1825
|
name: "get_session_pages",
|
|
1130
1826
|
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).",
|
|
1131
|
-
inputSchema:
|
|
1132
|
-
session_id:
|
|
1133
|
-
session_ids:
|
|
1827
|
+
inputSchema: z23.object({
|
|
1828
|
+
session_id: z23.string().optional().describe("Single session ID to get pages for"),
|
|
1829
|
+
session_ids: z23.preprocess((val) => {
|
|
1134
1830
|
if (typeof val === "string") {
|
|
1135
1831
|
try {
|
|
1136
1832
|
return JSON.parse(val);
|
|
@@ -1139,7 +1835,7 @@ var getSessionPagesTool = {
|
|
|
1139
1835
|
}
|
|
1140
1836
|
}
|
|
1141
1837
|
return val;
|
|
1142
|
-
},
|
|
1838
|
+
}, z23.array(z23.string()).max(20).optional()).describe("Array of session IDs to get pages for (max 20)")
|
|
1143
1839
|
}),
|
|
1144
1840
|
handler: async (args) => {
|
|
1145
1841
|
const { session_id, session_ids } = args;
|
|
@@ -1168,13 +1864,13 @@ var getSessionPagesTool = {
|
|
|
1168
1864
|
};
|
|
1169
1865
|
|
|
1170
1866
|
// ../../libraries/mcp-tools/dist/tools/getUxHealthReport.js
|
|
1171
|
-
import { z as
|
|
1867
|
+
import { z as z24 } from "zod";
|
|
1172
1868
|
var getUxHealthReportTool = {
|
|
1173
1869
|
name: "get_ux_health_report",
|
|
1174
1870
|
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.",
|
|
1175
|
-
inputSchema:
|
|
1176
|
-
page_path:
|
|
1177
|
-
days:
|
|
1871
|
+
inputSchema: z24.object({
|
|
1872
|
+
page_path: z24.string().optional().describe("Focus on a specific page (omit for site-wide report)"),
|
|
1873
|
+
days: z24.number().optional().default(7).describe("Number of days to analyze (default: 7)")
|
|
1178
1874
|
}),
|
|
1179
1875
|
handler: async (args) => {
|
|
1180
1876
|
const { page_path, days } = args;
|
|
@@ -1193,7 +1889,7 @@ var getUxHealthReportTool = {
|
|
|
1193
1889
|
};
|
|
1194
1890
|
|
|
1195
1891
|
// ../../libraries/mcp-tools/dist/tools/upgradeOptions.js
|
|
1196
|
-
import { z as
|
|
1892
|
+
import { z as z25 } from "zod";
|
|
1197
1893
|
function formatUpgradeOptions(data) {
|
|
1198
1894
|
const lines = [];
|
|
1199
1895
|
lines.push("# UPGRADE OPTIONS\n");
|
|
@@ -1225,7 +1921,7 @@ function formatUpgradeOptions(data) {
|
|
|
1225
1921
|
var getUpgradeOptionsTool = {
|
|
1226
1922
|
name: "get_upgrade_options",
|
|
1227
1923
|
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.",
|
|
1228
|
-
inputSchema:
|
|
1924
|
+
inputSchema: z25.object({}),
|
|
1229
1925
|
handler: async () => {
|
|
1230
1926
|
if (!isApiConfigured()) {
|
|
1231
1927
|
return apiNotConfiguredResult();
|
|
@@ -1240,16 +1936,16 @@ var getUpgradeOptionsTool = {
|
|
|
1240
1936
|
};
|
|
1241
1937
|
|
|
1242
1938
|
// ../../libraries/mcp-tools/dist/tools/improvementRun.js
|
|
1243
|
-
import { z as
|
|
1939
|
+
import { z as z26 } from "zod";
|
|
1244
1940
|
var startImprovementRunTool = {
|
|
1245
1941
|
name: "start_improvement_run",
|
|
1246
1942
|
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.",
|
|
1247
|
-
inputSchema:
|
|
1248
|
-
trigger_type:
|
|
1249
|
-
trigger_metadata:
|
|
1250
|
-
phases:
|
|
1251
|
-
name:
|
|
1252
|
-
status:
|
|
1943
|
+
inputSchema: z26.object({
|
|
1944
|
+
trigger_type: z26.enum(["github_actions", "cron", "manual", "api"]).describe("What triggered this improvement run"),
|
|
1945
|
+
trigger_metadata: z26.record(z26.unknown()).optional().describe("Additional metadata about the trigger (e.g., GitHub run ID, branch, actor)"),
|
|
1946
|
+
phases: z26.array(z26.object({
|
|
1947
|
+
name: z26.string(),
|
|
1948
|
+
status: z26.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending")
|
|
1253
1949
|
})).optional().describe("Initial phases to track (e.g., diagnose, investigate, fix)")
|
|
1254
1950
|
}),
|
|
1255
1951
|
handler: async (args) => {
|
|
@@ -1271,38 +1967,38 @@ var startImprovementRunTool = {
|
|
|
1271
1967
|
var updateImprovementRunTool = {
|
|
1272
1968
|
name: "update_improvement_run",
|
|
1273
1969
|
description: "Update an improvement run's status, phases, summary, or diagnostic data. Use to track progress through the workflow phases and record final outcomes.",
|
|
1274
|
-
inputSchema:
|
|
1275
|
-
run_id:
|
|
1276
|
-
status:
|
|
1277
|
-
title:
|
|
1278
|
-
phases:
|
|
1279
|
-
name:
|
|
1280
|
-
status:
|
|
1970
|
+
inputSchema: z26.object({
|
|
1971
|
+
run_id: z26.string().describe("The ID of the improvement run to update"),
|
|
1972
|
+
status: z26.enum(["running", "completed", "failed", "cancelled"]).optional().describe("New status for the run"),
|
|
1973
|
+
title: z26.string().optional().describe("A user-friendly title summarizing what was improved (e.g., 'Fixed checkout responsiveness, improved mobile navigation'). Avoid technical jargon - write for non-developers."),
|
|
1974
|
+
phases: z26.array(z26.object({
|
|
1975
|
+
name: z26.string(),
|
|
1976
|
+
status: z26.enum([
|
|
1281
1977
|
"pending",
|
|
1282
1978
|
"running",
|
|
1283
1979
|
"completed",
|
|
1284
1980
|
"skipped",
|
|
1285
1981
|
"failed"
|
|
1286
1982
|
]),
|
|
1287
|
-
startedAt:
|
|
1288
|
-
completedAt:
|
|
1289
|
-
output:
|
|
1983
|
+
startedAt: z26.string().nullable().optional(),
|
|
1984
|
+
completedAt: z26.string().nullable().optional(),
|
|
1985
|
+
output: z26.record(z26.unknown()).optional()
|
|
1290
1986
|
})).optional().describe("Updated phases array"),
|
|
1291
|
-
summary:
|
|
1292
|
-
issuesFound:
|
|
1293
|
-
issuesFixed:
|
|
1294
|
-
issuesDeferred:
|
|
1295
|
-
issuesDismissed:
|
|
1296
|
-
prsCreated:
|
|
1987
|
+
summary: z26.object({
|
|
1988
|
+
issuesFound: z26.number().optional(),
|
|
1989
|
+
issuesFixed: z26.number().optional(),
|
|
1990
|
+
issuesDeferred: z26.number().optional(),
|
|
1991
|
+
issuesDismissed: z26.number().optional(),
|
|
1992
|
+
prsCreated: z26.number().optional()
|
|
1297
1993
|
}).optional().describe("Updated summary counts"),
|
|
1298
|
-
diagnostic:
|
|
1299
|
-
healthScore:
|
|
1300
|
-
totalSessions:
|
|
1301
|
-
pagesAnalyzed:
|
|
1302
|
-
summary:
|
|
1994
|
+
diagnostic: z26.object({
|
|
1995
|
+
healthScore: z26.number().describe("Site health score (0-100)"),
|
|
1996
|
+
totalSessions: z26.number().describe("Number of sessions analyzed"),
|
|
1997
|
+
pagesAnalyzed: z26.number().describe("Number of pages with data"),
|
|
1998
|
+
summary: z26.string().nullable().optional().describe("1-2 sentence summary of the site's current state and key findings")
|
|
1303
1999
|
}).optional().describe("Diagnostic data from run_full_diagnostic. Set after diagnosis phase completes."),
|
|
1304
|
-
completed_at:
|
|
1305
|
-
duration_ms:
|
|
2000
|
+
completed_at: z26.string().optional().describe("ISO timestamp when the run completed"),
|
|
2001
|
+
duration_ms: z26.number().optional().describe("Total duration of the run in milliseconds")
|
|
1306
2002
|
}),
|
|
1307
2003
|
handler: async (args) => {
|
|
1308
2004
|
const { run_id, status, title, phases, summary, diagnostic, completed_at, duration_ms } = args;
|
|
@@ -1327,35 +2023,35 @@ var updateImprovementRunTool = {
|
|
|
1327
2023
|
var recordImprovementActionTool = {
|
|
1328
2024
|
name: "record_improvement_action",
|
|
1329
2025
|
description: "Record an action taken during an improvement run. REQUIRED: run_id, action_type, hypothesis, expected_improvement. For code_fix: include code_changes array with actual unified diff format (not descriptions). For needs_more_data: include deferral_reason. For dismissed: include dismissal_reason. Use expected_improvement='N/A' for non-fix actions.",
|
|
1330
|
-
inputSchema:
|
|
1331
|
-
run_id:
|
|
1332
|
-
issue_id:
|
|
1333
|
-
action_type:
|
|
2026
|
+
inputSchema: z26.object({
|
|
2027
|
+
run_id: z26.string().describe("REQUIRED. The ID of the improvement run"),
|
|
2028
|
+
issue_id: z26.string().optional().describe("The ID of the issue this action relates to (if any)"),
|
|
2029
|
+
action_type: z26.enum([
|
|
1334
2030
|
"code_fix",
|
|
1335
2031
|
"needs_more_data",
|
|
1336
2032
|
"dismissed",
|
|
1337
2033
|
"marked_intended",
|
|
1338
2034
|
"knowledge_added"
|
|
1339
2035
|
]).describe("REQUIRED. Type of action taken"),
|
|
1340
|
-
hypothesis:
|
|
1341
|
-
expected_improvement:
|
|
1342
|
-
code_changes:
|
|
1343
|
-
file:
|
|
1344
|
-
diff:
|
|
1345
|
-
startLine:
|
|
1346
|
-
linesAdded:
|
|
1347
|
-
linesRemoved:
|
|
2036
|
+
hypothesis: z26.string().describe("REQUIRED. AI's reasoning for this action"),
|
|
2037
|
+
expected_improvement: z26.string().describe("REQUIRED. What improvement is expected (use 'N/A' for non-fix actions)"),
|
|
2038
|
+
code_changes: z26.array(z26.object({
|
|
2039
|
+
file: z26.string().describe("Path to the changed file"),
|
|
2040
|
+
diff: z26.string().describe("Unified diff format code changes"),
|
|
2041
|
+
startLine: z26.number().describe("Line number where the diff begins"),
|
|
2042
|
+
linesAdded: z26.number().default(0).describe("Count of lines added"),
|
|
2043
|
+
linesRemoved: z26.number().default(0).describe("Count of lines removed")
|
|
1348
2044
|
})).optional().describe("Array of code changes with actual source code diffs"),
|
|
1349
|
-
pr_url:
|
|
1350
|
-
pr_number:
|
|
1351
|
-
remediation_id:
|
|
1352
|
-
deferral_reason:
|
|
1353
|
-
dismissal_reason:
|
|
1354
|
-
page_path:
|
|
1355
|
-
element_selector:
|
|
1356
|
-
diagnosis:
|
|
1357
|
-
proposed_fix:
|
|
1358
|
-
confidence:
|
|
2045
|
+
pr_url: z26.string().optional().describe("GitHub PR URL if a PR was created"),
|
|
2046
|
+
pr_number: z26.number().optional().describe("GitHub PR number"),
|
|
2047
|
+
remediation_id: z26.string().optional().describe("ID of the associated remediation record (if any)"),
|
|
2048
|
+
deferral_reason: z26.string().optional().describe("Explanation for why the issue was deferred (for needs_more_data actions)"),
|
|
2049
|
+
dismissal_reason: z26.string().optional().describe("Explanation for why the issue was dismissed (for dismissed/marked_intended actions)"),
|
|
2050
|
+
page_path: z26.string().optional().describe("Page path affected by the fix"),
|
|
2051
|
+
element_selector: z26.string().optional().describe("CSS selector of the affected element"),
|
|
2052
|
+
diagnosis: z26.string().optional().describe("Root cause analysis"),
|
|
2053
|
+
proposed_fix: z26.string().optional().describe("Fix description"),
|
|
2054
|
+
confidence: z26.number().min(0).max(1).optional().describe("Fix confidence 0-1")
|
|
1359
2055
|
}),
|
|
1360
2056
|
handler: async (args) => {
|
|
1361
2057
|
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;
|
|
@@ -1394,11 +2090,11 @@ var recordImprovementActionTool = {
|
|
|
1394
2090
|
var updateImprovementActionTool = {
|
|
1395
2091
|
name: "update_improvement_action",
|
|
1396
2092
|
description: "Update an existing improvement run action with PR information. Use this after a PR has been created to link it to the action.",
|
|
1397
|
-
inputSchema:
|
|
1398
|
-
run_id:
|
|
1399
|
-
action_id:
|
|
1400
|
-
pr_url:
|
|
1401
|
-
pr_number:
|
|
2093
|
+
inputSchema: z26.object({
|
|
2094
|
+
run_id: z26.string().describe("The ID of the improvement run"),
|
|
2095
|
+
action_id: z26.string().describe("The ID of the action to update"),
|
|
2096
|
+
pr_url: z26.string().optional().describe("GitHub PR URL to link to this action"),
|
|
2097
|
+
pr_number: z26.number().optional().describe("GitHub PR number to link to this action")
|
|
1402
2098
|
}),
|
|
1403
2099
|
handler: async (args) => {
|
|
1404
2100
|
const { run_id, action_id, pr_url, pr_number } = args;
|
|
@@ -1418,8 +2114,8 @@ var updateImprovementActionTool = {
|
|
|
1418
2114
|
var getImprovementRunTool = {
|
|
1419
2115
|
name: "get_improvement_run",
|
|
1420
2116
|
description: "Get details of a specific improvement run including all actions taken. Use to review what happened during a run.",
|
|
1421
|
-
inputSchema:
|
|
1422
|
-
run_id:
|
|
2117
|
+
inputSchema: z26.object({
|
|
2118
|
+
run_id: z26.string().describe("The ID of the improvement run to retrieve")
|
|
1423
2119
|
}),
|
|
1424
2120
|
handler: async (args) => {
|
|
1425
2121
|
const { run_id } = args;
|
|
@@ -1436,11 +2132,11 @@ var getImprovementRunTool = {
|
|
|
1436
2132
|
var listImprovementRunsTool = {
|
|
1437
2133
|
name: "list_improvement_runs",
|
|
1438
2134
|
description: "List recent improvement runs with optional filters. Use to review past runs and their outcomes.",
|
|
1439
|
-
inputSchema:
|
|
1440
|
-
status:
|
|
1441
|
-
trigger_type:
|
|
1442
|
-
limit:
|
|
1443
|
-
offset:
|
|
2135
|
+
inputSchema: z26.object({
|
|
2136
|
+
status: z26.enum(["running", "completed", "failed", "cancelled"]).optional().describe("Filter by run status"),
|
|
2137
|
+
trigger_type: z26.enum(["github_actions", "cron", "manual", "api"]).optional().describe("Filter by trigger type"),
|
|
2138
|
+
limit: z26.number().optional().default(20).describe("Maximum number of results (default: 20)"),
|
|
2139
|
+
offset: z26.number().optional().default(0).describe("Offset for pagination")
|
|
1444
2140
|
}),
|
|
1445
2141
|
handler: async (args) => {
|
|
1446
2142
|
const { status, trigger_type, limit, offset } = args;
|
|
@@ -1461,7 +2157,7 @@ var listImprovementRunsTool = {
|
|
|
1461
2157
|
};
|
|
1462
2158
|
|
|
1463
2159
|
// ../../libraries/mcp-tools/dist/tools/knowledge.js
|
|
1464
|
-
import { z as
|
|
2160
|
+
import { z as z27 } from "zod";
|
|
1465
2161
|
var KNOWLEDGE_CATEGORIES = [
|
|
1466
2162
|
"false_positive",
|
|
1467
2163
|
"intended_behavior",
|
|
@@ -1481,11 +2177,11 @@ var ISSUE_CATEGORIES = [
|
|
|
1481
2177
|
var getSiteKnowledgeTool = {
|
|
1482
2178
|
name: "get_site_knowledge",
|
|
1483
2179
|
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.",
|
|
1484
|
-
inputSchema:
|
|
1485
|
-
category:
|
|
1486
|
-
page_path:
|
|
1487
|
-
issue_category:
|
|
1488
|
-
limit:
|
|
2180
|
+
inputSchema: z27.object({
|
|
2181
|
+
category: z27.enum(KNOWLEDGE_CATEGORIES).optional().describe("Filter by knowledge category"),
|
|
2182
|
+
page_path: z27.string().optional().describe("Filter by page path"),
|
|
2183
|
+
issue_category: z27.enum(ISSUE_CATEGORIES).optional().describe("Filter by issue category"),
|
|
2184
|
+
limit: z27.number().optional().default(50).describe("Maximum number of entries to return (default: 50)")
|
|
1489
2185
|
}),
|
|
1490
2186
|
handler: async (args) => {
|
|
1491
2187
|
const { category, page_path, issue_category, limit } = args;
|
|
@@ -1507,13 +2203,13 @@ var getSiteKnowledgeTool = {
|
|
|
1507
2203
|
var addSiteKnowledgeTool = {
|
|
1508
2204
|
name: "add_site_knowledge",
|
|
1509
2205
|
description: "Store a site-specific learning. Use to record false positives, intended behaviors, successful fix patterns, or important context about the site.",
|
|
1510
|
-
inputSchema:
|
|
1511
|
-
category:
|
|
1512
|
-
subject:
|
|
1513
|
-
content:
|
|
1514
|
-
page_path:
|
|
1515
|
-
element_selector:
|
|
1516
|
-
issue_category:
|
|
2206
|
+
inputSchema: z27.object({
|
|
2207
|
+
category: z27.enum(KNOWLEDGE_CATEGORIES).describe("Category of knowledge: false_positive, intended_behavior, fix_pattern, or context"),
|
|
2208
|
+
subject: z27.string().describe("Unique identifier/title for this knowledge entry"),
|
|
2209
|
+
content: z27.string().describe("Detailed description of the learning"),
|
|
2210
|
+
page_path: z27.string().optional().describe("Associated page path (if applicable)"),
|
|
2211
|
+
element_selector: z27.string().optional().describe("Associated element selector (if applicable)"),
|
|
2212
|
+
issue_category: z27.enum(ISSUE_CATEGORIES).optional().describe("Associated issue category (if applicable)")
|
|
1517
2213
|
}),
|
|
1518
2214
|
handler: async (args) => {
|
|
1519
2215
|
const { category, subject, content, page_path, element_selector, issue_category } = args;
|
|
@@ -1538,8 +2234,8 @@ var addSiteKnowledgeTool = {
|
|
|
1538
2234
|
var deleteSiteKnowledgeTool = {
|
|
1539
2235
|
name: "delete_site_knowledge",
|
|
1540
2236
|
description: "Delete a site knowledge entry. Use when a learning is no longer valid or was created in error.",
|
|
1541
|
-
inputSchema:
|
|
1542
|
-
knowledge_id:
|
|
2237
|
+
inputSchema: z27.object({
|
|
2238
|
+
knowledge_id: z27.string().describe("The ID of the knowledge entry to delete")
|
|
1543
2239
|
}),
|
|
1544
2240
|
handler: async (args) => {
|
|
1545
2241
|
const { knowledge_id } = args;
|
|
@@ -1555,13 +2251,13 @@ var deleteSiteKnowledgeTool = {
|
|
|
1555
2251
|
};
|
|
1556
2252
|
|
|
1557
2253
|
// ../../libraries/mcp-tools/dist/tools/listPages.js
|
|
1558
|
-
import { z as
|
|
2254
|
+
import { z as z28 } from "zod";
|
|
1559
2255
|
var listPagesTool = {
|
|
1560
2256
|
name: "list_pages",
|
|
1561
2257
|
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.",
|
|
1562
|
-
inputSchema:
|
|
1563
|
-
days:
|
|
1564
|
-
limit:
|
|
2258
|
+
inputSchema: z28.object({
|
|
2259
|
+
days: z28.number().optional().default(7).describe("Number of days to look back (default 7)"),
|
|
2260
|
+
limit: z28.number().optional().default(50).describe("Maximum number of pages to return (default 50)")
|
|
1565
2261
|
}),
|
|
1566
2262
|
handler: async (args) => {
|
|
1567
2263
|
const { days, limit } = args;
|
|
@@ -1580,16 +2276,16 @@ var listPagesTool = {
|
|
|
1580
2276
|
};
|
|
1581
2277
|
|
|
1582
2278
|
// ../../libraries/mcp-tools/dist/tools/listSessions.js
|
|
1583
|
-
import { z as
|
|
2279
|
+
import { z as z29 } from "zod";
|
|
1584
2280
|
var listSessionsTool = {
|
|
1585
2281
|
name: "list_sessions",
|
|
1586
2282
|
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.",
|
|
1587
|
-
inputSchema:
|
|
1588
|
-
domain:
|
|
1589
|
-
status:
|
|
1590
|
-
device:
|
|
1591
|
-
days:
|
|
1592
|
-
limit:
|
|
2283
|
+
inputSchema: z29.object({
|
|
2284
|
+
domain: z29.string().optional().describe("Filter by domain name (partial match supported)"),
|
|
2285
|
+
status: z29.enum(["active", "terminated", "all"]).optional().default("all").describe("Filter by session status"),
|
|
2286
|
+
device: z29.enum(["desktop", "mobile", "tablet"]).optional().describe("Filter by device type"),
|
|
2287
|
+
days: z29.number().optional().default(7).describe("Number of days to look back (default: 7)"),
|
|
2288
|
+
limit: z29.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 200)")
|
|
1593
2289
|
}),
|
|
1594
2290
|
handler: async (args) => {
|
|
1595
2291
|
const { domain, status, device, days, limit } = args;
|
|
@@ -1611,16 +2307,16 @@ var listSessionsTool = {
|
|
|
1611
2307
|
};
|
|
1612
2308
|
|
|
1613
2309
|
// ../../libraries/mcp-tools/dist/tools/predictOutcomes.js
|
|
1614
|
-
import { z as
|
|
2310
|
+
import { z as z30 } from "zod";
|
|
1615
2311
|
var predictOutcomesTool = {
|
|
1616
2312
|
name: "predict_outcomes",
|
|
1617
2313
|
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.",
|
|
1618
|
-
inputSchema:
|
|
1619
|
-
session_id:
|
|
1620
|
-
behavioral_features:
|
|
1621
|
-
frustration:
|
|
1622
|
-
confusion:
|
|
1623
|
-
confidence:
|
|
2314
|
+
inputSchema: z30.object({
|
|
2315
|
+
session_id: z30.string().optional().describe("Session ID to analyze and predict outcome for"),
|
|
2316
|
+
behavioral_features: z30.object({
|
|
2317
|
+
frustration: z30.number().describe("Frustration score (0-1)"),
|
|
2318
|
+
confusion: z30.number().describe("Confusion score (0-1)"),
|
|
2319
|
+
confidence: z30.number().describe("Confidence score (0-1)")
|
|
1624
2320
|
}).optional().describe("Behavioral features to use for prediction")
|
|
1625
2321
|
}),
|
|
1626
2322
|
handler: async (args) => {
|
|
@@ -1643,13 +2339,13 @@ var predictOutcomesTool = {
|
|
|
1643
2339
|
};
|
|
1644
2340
|
|
|
1645
2341
|
// ../../libraries/mcp-tools/dist/tools/remediation.js
|
|
1646
|
-
import { z as
|
|
2342
|
+
import { z as z31 } from "zod";
|
|
1647
2343
|
var proposeFixTool = {
|
|
1648
2344
|
name: "propose_fix",
|
|
1649
2345
|
description: "Propose a fix for a UX issue. REQUIRED: page_path, diagnosis, proposed_fix, confidence. Creates a remediation record with baseline metrics for later evaluation. Returns remediation_id for tracking.",
|
|
1650
|
-
inputSchema:
|
|
1651
|
-
page_path:
|
|
1652
|
-
category:
|
|
2346
|
+
inputSchema: z31.object({
|
|
2347
|
+
page_path: z31.string().describe("REQUIRED. The page path where the issue occurs"),
|
|
2348
|
+
category: z31.enum([
|
|
1653
2349
|
"code_error",
|
|
1654
2350
|
"dead_click",
|
|
1655
2351
|
"rage_click",
|
|
@@ -1659,26 +2355,32 @@ var proposeFixTool = {
|
|
|
1659
2355
|
"behavioral_anomaly",
|
|
1660
2356
|
"structural_issue"
|
|
1661
2357
|
]).optional().describe("The issue category"),
|
|
1662
|
-
severity:
|
|
1663
|
-
diagnosis:
|
|
1664
|
-
proposed_fix:
|
|
1665
|
-
code_snippet:
|
|
1666
|
-
affected_files:
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
2358
|
+
severity: z31.enum(["critical", "high", "medium", "low", "info"]).optional().describe("The issue severity"),
|
|
2359
|
+
diagnosis: z31.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
|
|
2360
|
+
proposed_fix: z31.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
|
|
2361
|
+
code_snippet: z31.string().optional().describe("Suggested code changes (if applicable)"),
|
|
2362
|
+
affected_files: z31.array(z31.string()).optional().describe("List of files that need to be modified"),
|
|
2363
|
+
code_changes: z31.array(z31.object({
|
|
2364
|
+
file: z31.string().describe("File path relative to repository root"),
|
|
2365
|
+
content: z31.string().optional().describe("New content for the file"),
|
|
2366
|
+
diff: z31.string().optional().describe("Unified diff format"),
|
|
2367
|
+
description: z31.string().optional().describe("Description of the change")
|
|
2368
|
+
})).optional().describe("Detailed code changes with file contents for programmatic PR creation"),
|
|
2369
|
+
confidence: z31.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
|
|
2370
|
+
title: z31.string().optional().describe("User-friendly title describing the issue from the user's perspective"),
|
|
2371
|
+
execution_metadata: z31.object({
|
|
2372
|
+
difficulty_class: z31.enum(["trivial", "light", "heavy"]),
|
|
2373
|
+
difficulty_reasoning: z31.string(),
|
|
2374
|
+
model_used: z31.string(),
|
|
2375
|
+
was_escalated: z31.boolean().optional(),
|
|
2376
|
+
escalation_reason: z31.string().optional(),
|
|
2377
|
+
iterations: z31.number().optional(),
|
|
2378
|
+
credits_used: z31.number().optional(),
|
|
2379
|
+
duration_ms: z31.number().optional()
|
|
1678
2380
|
}).optional().describe("AI execution metadata for tracking model selection and performance")
|
|
1679
2381
|
}),
|
|
1680
2382
|
handler: async (args) => {
|
|
1681
|
-
const { page_path, category, severity, diagnosis, proposed_fix, code_snippet, affected_files, confidence, title, execution_metadata } = args;
|
|
2383
|
+
const { page_path, category, severity, diagnosis, proposed_fix, code_snippet, affected_files, code_changes, confidence, title, execution_metadata } = args;
|
|
1682
2384
|
if (!isApiConfigured()) {
|
|
1683
2385
|
return apiNotConfiguredResult();
|
|
1684
2386
|
}
|
|
@@ -1690,6 +2392,7 @@ var proposeFixTool = {
|
|
|
1690
2392
|
proposed_fix,
|
|
1691
2393
|
code_snippet,
|
|
1692
2394
|
affected_files,
|
|
2395
|
+
code_changes,
|
|
1693
2396
|
confidence,
|
|
1694
2397
|
title,
|
|
1695
2398
|
execution_metadata
|
|
@@ -1703,7 +2406,7 @@ var proposeFixTool = {
|
|
|
1703
2406
|
var listPendingFixesTool = {
|
|
1704
2407
|
name: "list_pending_fixes",
|
|
1705
2408
|
description: "List all proposed fixes awaiting deployment. Use to show the user what fixes are ready to be deployed.",
|
|
1706
|
-
inputSchema:
|
|
2409
|
+
inputSchema: z31.object({}),
|
|
1707
2410
|
handler: async () => {
|
|
1708
2411
|
if (!isApiConfigured()) {
|
|
1709
2412
|
return apiNotConfiguredResult();
|
|
@@ -1718,20 +2421,19 @@ var listPendingFixesTool = {
|
|
|
1718
2421
|
var listRemediationsTool = {
|
|
1719
2422
|
name: "list_remediations",
|
|
1720
2423
|
description: "List remediation records with optional filters. Use to review past and current fix attempts.",
|
|
1721
|
-
inputSchema:
|
|
1722
|
-
status:
|
|
1723
|
-
"
|
|
1724
|
-
"waiting",
|
|
1725
|
-
"dismissed",
|
|
2424
|
+
inputSchema: z31.object({
|
|
2425
|
+
status: z31.enum([
|
|
2426
|
+
"pending",
|
|
1726
2427
|
"deployed",
|
|
1727
2428
|
"evaluating",
|
|
1728
2429
|
"succeeded",
|
|
1729
2430
|
"failed",
|
|
1730
|
-
"
|
|
1731
|
-
"deferred"
|
|
2431
|
+
"dismissed",
|
|
2432
|
+
"deferred",
|
|
2433
|
+
"reverted"
|
|
1732
2434
|
]).optional().describe("Filter by remediation status"),
|
|
1733
|
-
page_path:
|
|
1734
|
-
category:
|
|
2435
|
+
page_path: z31.string().optional().describe("Filter by page path"),
|
|
2436
|
+
category: z31.enum([
|
|
1735
2437
|
"code_error",
|
|
1736
2438
|
"dead_click",
|
|
1737
2439
|
"rage_click",
|
|
@@ -1741,10 +2443,11 @@ var listRemediationsTool = {
|
|
|
1741
2443
|
"behavioral_anomaly",
|
|
1742
2444
|
"structural_issue"
|
|
1743
2445
|
]).optional().describe("Filter by issue category"),
|
|
1744
|
-
|
|
2446
|
+
kind: z31.enum(["fix", "proposal"]).optional().describe("Filter by kind: 'fix' for code fixes, 'proposal' for audit suggestions"),
|
|
2447
|
+
limit: z31.number().optional().default(20).describe("Maximum number of results (default: 20)")
|
|
1745
2448
|
}),
|
|
1746
2449
|
handler: async (args) => {
|
|
1747
|
-
const { status, page_path, category, limit } = args;
|
|
2450
|
+
const { status, page_path, category, kind, limit } = args;
|
|
1748
2451
|
if (!isApiConfigured()) {
|
|
1749
2452
|
return apiNotConfiguredResult();
|
|
1750
2453
|
}
|
|
@@ -1752,6 +2455,7 @@ var listRemediationsTool = {
|
|
|
1752
2455
|
status,
|
|
1753
2456
|
page_path,
|
|
1754
2457
|
category,
|
|
2458
|
+
kind,
|
|
1755
2459
|
limit: limit ?? 20
|
|
1756
2460
|
});
|
|
1757
2461
|
if (error) {
|
|
@@ -1763,8 +2467,8 @@ var listRemediationsTool = {
|
|
|
1763
2467
|
var confirmDeploymentTool = {
|
|
1764
2468
|
name: "confirm_deployment",
|
|
1765
2469
|
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.",
|
|
1766
|
-
inputSchema:
|
|
1767
|
-
remediation_id:
|
|
2470
|
+
inputSchema: z31.object({
|
|
2471
|
+
remediation_id: z31.string().describe("The ID of the remediation to mark as deployed")
|
|
1768
2472
|
}),
|
|
1769
2473
|
handler: async (args) => {
|
|
1770
2474
|
const { remediation_id } = args;
|
|
@@ -1781,10 +2485,10 @@ var confirmDeploymentTool = {
|
|
|
1781
2485
|
var evaluateFixTool = {
|
|
1782
2486
|
name: "evaluate_fix",
|
|
1783
2487
|
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.",
|
|
1784
|
-
inputSchema:
|
|
1785
|
-
remediation_id:
|
|
1786
|
-
min_hours:
|
|
1787
|
-
skip_status_update:
|
|
2488
|
+
inputSchema: z31.object({
|
|
2489
|
+
remediation_id: z31.string().describe("The ID of the remediation to evaluate"),
|
|
2490
|
+
min_hours: z31.number().optional().default(24).describe("Minimum hours since deployment required (default: 24)"),
|
|
2491
|
+
skip_status_update: z31.boolean().optional().default(false).describe("When true, skips writing remediation status (used in phased mode where applyEvaluateUpdates is authoritative)")
|
|
1788
2492
|
}),
|
|
1789
2493
|
handler: async (args) => {
|
|
1790
2494
|
const { remediation_id, min_hours, skip_status_update } = args;
|
|
@@ -1803,14 +2507,14 @@ var evaluateFixTool = {
|
|
|
1803
2507
|
};
|
|
1804
2508
|
var updateRemediationStatusTool = {
|
|
1805
2509
|
name: "update_remediation_status",
|
|
1806
|
-
description: "Update remediation status based on PR events. REQUIRED: remediation_id, status. Status values: '
|
|
1807
|
-
inputSchema:
|
|
1808
|
-
remediation_id:
|
|
1809
|
-
status:
|
|
1810
|
-
pr_url:
|
|
1811
|
-
pr_number:
|
|
1812
|
-
pr_merged_at:
|
|
1813
|
-
pr_closed_at:
|
|
2510
|
+
description: "Update remediation status based on PR events. REQUIRED: remediation_id, status. Status values: 'deployed' (PR merged), 'dismissed' (PR closed without merge). Note: Remediations with open PRs stay in 'pending' status - use PR fields to track PR state.",
|
|
2511
|
+
inputSchema: z31.object({
|
|
2512
|
+
remediation_id: z31.string().describe("REQUIRED. The ID of the remediation to update"),
|
|
2513
|
+
status: z31.enum(["deployed", "dismissed"]).describe("REQUIRED. One of: deployed, dismissed"),
|
|
2514
|
+
pr_url: z31.string().optional().describe("URL of the pull request"),
|
|
2515
|
+
pr_number: z31.number().optional().describe("PR number"),
|
|
2516
|
+
pr_merged_at: z31.string().optional().describe("ISO timestamp when PR was merged"),
|
|
2517
|
+
pr_closed_at: z31.string().optional().describe("ISO timestamp when PR was closed")
|
|
1814
2518
|
}),
|
|
1815
2519
|
handler: async (args) => {
|
|
1816
2520
|
const { remediation_id, status, pr_url, pr_number, pr_merged_at, pr_closed_at } = args;
|
|
@@ -1832,28 +2536,29 @@ var updateRemediationStatusTool = {
|
|
|
1832
2536
|
};
|
|
1833
2537
|
var listRemediationsByStatusTool = {
|
|
1834
2538
|
name: "list_remediations_by_status",
|
|
1835
|
-
description: "List remediations filtered by one or more statuses. Use to find
|
|
1836
|
-
inputSchema:
|
|
1837
|
-
statuses:
|
|
1838
|
-
"
|
|
1839
|
-
"waiting",
|
|
1840
|
-
"dismissed",
|
|
2539
|
+
description: "List remediations filtered by one or more statuses. Use to find 'pending' fixes with open PRs or 'deployed' fixes ready for evaluation. To find remediations awaiting PR merge, query for 'pending' status and check prUrl field. Set include_execution to get difficulty classification for model selection.",
|
|
2540
|
+
inputSchema: z31.object({
|
|
2541
|
+
statuses: z31.array(z31.enum([
|
|
2542
|
+
"pending",
|
|
1841
2543
|
"deployed",
|
|
1842
2544
|
"evaluating",
|
|
1843
2545
|
"succeeded",
|
|
1844
2546
|
"failed",
|
|
1845
|
-
"
|
|
1846
|
-
"deferred"
|
|
2547
|
+
"dismissed",
|
|
2548
|
+
"deferred",
|
|
2549
|
+
"reverted"
|
|
1847
2550
|
])).describe("List of statuses to filter by"),
|
|
1848
|
-
|
|
2551
|
+
kind: z31.enum(["fix", "proposal"]).optional().describe("Filter by kind: 'fix' for code fixes, 'proposal' for audit suggestions"),
|
|
2552
|
+
include_execution: z31.boolean().optional().describe("Include execution metadata (difficulty class, model used) for each remediation")
|
|
1849
2553
|
}),
|
|
1850
2554
|
handler: async (args) => {
|
|
1851
|
-
const { statuses, include_execution } = args;
|
|
2555
|
+
const { statuses, kind, include_execution } = args;
|
|
1852
2556
|
if (!isApiConfigured()) {
|
|
1853
2557
|
return apiNotConfiguredResult();
|
|
1854
2558
|
}
|
|
1855
2559
|
const { data, error } = await apiGet(`/remediations/by-status`, {
|
|
1856
2560
|
statuses: statuses.join(","),
|
|
2561
|
+
kind,
|
|
1857
2562
|
include_execution: include_execution ? "true" : void 0
|
|
1858
2563
|
});
|
|
1859
2564
|
if (error) {
|
|
@@ -1865,8 +2570,8 @@ var listRemediationsByStatusTool = {
|
|
|
1865
2570
|
var getRemediationByPrTool = {
|
|
1866
2571
|
name: "get_remediation_by_pr",
|
|
1867
2572
|
description: "Get a remediation by its PR number. Use to look up fix details when processing PR events.",
|
|
1868
|
-
inputSchema:
|
|
1869
|
-
pr_number:
|
|
2573
|
+
inputSchema: z31.object({
|
|
2574
|
+
pr_number: z31.number().describe("The PR number to look up")
|
|
1870
2575
|
}),
|
|
1871
2576
|
handler: async (args) => {
|
|
1872
2577
|
const { pr_number } = args;
|
|
@@ -1880,15 +2585,51 @@ var getRemediationByPrTool = {
|
|
|
1880
2585
|
return successResult(data);
|
|
1881
2586
|
}
|
|
1882
2587
|
};
|
|
2588
|
+
var updateRemediationTool = {
|
|
2589
|
+
name: "update_remediation",
|
|
2590
|
+
description: "Update an existing remediation with new information. Use to add code changes, update the proposed fix, or modify other fields. REQUIRED: remediation_id. At least one other field must be provided.",
|
|
2591
|
+
inputSchema: z31.object({
|
|
2592
|
+
remediation_id: z31.string().describe("REQUIRED. The ID of the remediation to update"),
|
|
2593
|
+
proposed_fix: z31.string().optional().describe("Updated description of the proposed fix"),
|
|
2594
|
+
code_snippet: z31.string().optional().describe("Updated code snippet (if applicable)"),
|
|
2595
|
+
affected_files: z31.array(z31.string()).optional().describe("Updated list of files that need to be modified"),
|
|
2596
|
+
code_changes: z31.array(z31.object({
|
|
2597
|
+
file: z31.string().describe("File path relative to repository root"),
|
|
2598
|
+
content: z31.string().optional().describe("New content for the file"),
|
|
2599
|
+
diff: z31.string().optional().describe("Unified diff format"),
|
|
2600
|
+
description: z31.string().optional().describe("Description of the change")
|
|
2601
|
+
})).optional().describe("Detailed code changes with file contents"),
|
|
2602
|
+
confidence: z31.number().min(0).max(1).optional().describe("Updated confidence level in the fix (0-1)"),
|
|
2603
|
+
title: z31.string().optional().describe("Updated user-friendly title")
|
|
2604
|
+
}),
|
|
2605
|
+
handler: async (args) => {
|
|
2606
|
+
const { remediation_id, proposed_fix, code_snippet, affected_files, code_changes, confidence, title } = args;
|
|
2607
|
+
if (!isApiConfigured()) {
|
|
2608
|
+
return apiNotConfiguredResult();
|
|
2609
|
+
}
|
|
2610
|
+
const { data, error } = await apiPatch(`/remediations/${remediation_id}`, {
|
|
2611
|
+
proposed_fix,
|
|
2612
|
+
code_snippet,
|
|
2613
|
+
affected_files,
|
|
2614
|
+
code_changes,
|
|
2615
|
+
confidence,
|
|
2616
|
+
title
|
|
2617
|
+
});
|
|
2618
|
+
if (error) {
|
|
2619
|
+
return errorResult(error);
|
|
2620
|
+
}
|
|
2621
|
+
return successResult(data);
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
1883
2624
|
|
|
1884
2625
|
// ../../libraries/mcp-tools/dist/tools/scanSite.js
|
|
1885
|
-
import { z as
|
|
2626
|
+
import { z as z32 } from "zod";
|
|
1886
2627
|
var scanSiteTool = {
|
|
1887
2628
|
name: "scan_site",
|
|
1888
2629
|
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.",
|
|
1889
|
-
inputSchema:
|
|
1890
|
-
top_n:
|
|
1891
|
-
offset:
|
|
2630
|
+
inputSchema: z32.object({
|
|
2631
|
+
top_n: z32.number().optional().default(10).describe("Number of top pages to analyze (default 10)"),
|
|
2632
|
+
offset: z32.number().optional().default(0).describe("Skip first N pages for pagination (default 0)")
|
|
1892
2633
|
}),
|
|
1893
2634
|
handler: async (args) => {
|
|
1894
2635
|
const { top_n, offset } = args;
|
|
@@ -1907,14 +2648,14 @@ var scanSiteTool = {
|
|
|
1907
2648
|
};
|
|
1908
2649
|
|
|
1909
2650
|
// ../../libraries/mcp-tools/dist/tools/searchSessions.js
|
|
1910
|
-
import { z as
|
|
2651
|
+
import { z as z33 } from "zod";
|
|
1911
2652
|
var searchSessionsTool = {
|
|
1912
2653
|
name: "search_sessions",
|
|
1913
2654
|
description: 'Search sessions by natural language query. Examples: "frustrated users on checkout", "rage clicks on pricing page", "confused users who abandoned cart".',
|
|
1914
|
-
inputSchema:
|
|
1915
|
-
query:
|
|
1916
|
-
page_path:
|
|
1917
|
-
limit:
|
|
2655
|
+
inputSchema: z33.object({
|
|
2656
|
+
query: z33.string().describe("Natural language search query describing the sessions you want to find"),
|
|
2657
|
+
page_path: z33.string().optional().describe("Optional: filter to a specific page path"),
|
|
2658
|
+
limit: z33.number().optional().default(10).describe("Maximum number of sessions to return (default: 10)")
|
|
1918
2659
|
}),
|
|
1919
2660
|
handler: async (args) => {
|
|
1920
2661
|
const { query, page_path, limit } = args;
|
|
@@ -1934,7 +2675,7 @@ var searchSessionsTool = {
|
|
|
1934
2675
|
};
|
|
1935
2676
|
|
|
1936
2677
|
// ../../libraries/mcp-tools/dist/tools/triageSessions.js
|
|
1937
|
-
import { z as
|
|
2678
|
+
import { z as z34 } from "zod";
|
|
1938
2679
|
function formatDuration(seconds) {
|
|
1939
2680
|
if (!seconds)
|
|
1940
2681
|
return "unknown";
|
|
@@ -2057,939 +2798,264 @@ function formatTriageOutput(data) {
|
|
|
2057
2798
|
var triageSessionsTool = {
|
|
2058
2799
|
name: "triage_sessions",
|
|
2059
2800
|
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.",
|
|
2060
|
-
inputSchema:
|
|
2061
|
-
session_ids:
|
|
2801
|
+
inputSchema: z34.object({
|
|
2802
|
+
session_ids: z34.preprocess((val) => {
|
|
2062
2803
|
if (typeof val === "string") {
|
|
2063
2804
|
try {
|
|
2064
2805
|
return JSON.parse(val);
|
|
2065
2806
|
} catch {
|
|
2066
2807
|
return val;
|
|
2067
|
-
}
|
|
2068
|
-
}
|
|
2069
|
-
return val;
|
|
2070
|
-
},
|
|
2071
|
-
days:
|
|
2072
|
-
page_path:
|
|
2073
|
-
min_severity:
|
|
2074
|
-
limit:
|
|
2075
|
-
}),
|
|
2076
|
-
handler: async (args) => {
|
|
2077
|
-
const { session_ids, days, page_path, min_severity, limit } = args;
|
|
2078
|
-
if (!isApiConfigured()) {
|
|
2079
|
-
return apiNotConfiguredResult();
|
|
2080
|
-
}
|
|
2081
|
-
const { data, error } = await apiGet("/sessions/triage", {
|
|
2082
|
-
session_ids: session_ids?.join(","),
|
|
2083
|
-
days: session_ids?.length ? void 0 : days ?? 7,
|
|
2084
|
-
page_path: session_ids?.length ? void 0 : page_path,
|
|
2085
|
-
min_severity,
|
|
2086
|
-
limit: session_ids?.length ? session_ids.length : limit ?? 20
|
|
2087
|
-
});
|
|
2088
|
-
if (error) {
|
|
2089
|
-
return errorResult(error);
|
|
2090
|
-
}
|
|
2091
|
-
const formattedOutput = formatTriageOutput(data);
|
|
2092
|
-
return { content: [{ type: "text", text: formattedOutput }] };
|
|
2093
|
-
}
|
|
2094
|
-
};
|
|
2095
|
-
|
|
2096
|
-
// ../../libraries/mcp-tools/dist/tools/proposal.js
|
|
2097
|
-
import { z as z34 } from "zod";
|
|
2098
|
-
var ISSUE_CATEGORIES2 = [
|
|
2099
|
-
"code_error",
|
|
2100
|
-
"dead_click",
|
|
2101
|
-
"rage_click",
|
|
2102
|
-
"ux_friction",
|
|
2103
|
-
"performance",
|
|
2104
|
-
"form_issue",
|
|
2105
|
-
"behavioral_anomaly",
|
|
2106
|
-
"structural_issue"
|
|
2107
|
-
];
|
|
2108
|
-
var ISSUE_SEVERITIES = ["critical", "high", "medium", "low", "info"];
|
|
2109
|
-
var PROPOSAL_STATUSES = [
|
|
2110
|
-
"pending",
|
|
2111
|
-
"resolved",
|
|
2112
|
-
"dismissed",
|
|
2113
|
-
"evaluating",
|
|
2114
|
-
"succeeded",
|
|
2115
|
-
"failed"
|
|
2116
|
-
];
|
|
2117
|
-
var createProposalTool = {
|
|
2118
|
-
name: "create_proposal",
|
|
2119
|
-
description: "AUDIT MODE ONLY. Create a fix proposal. REQUIRED: title, diagnosis, proposed_fix, confidence, page_path, category, severity. Proposals are suggestions that developers review and implement manually. Do NOT use in fix mode - use propose_fix instead.",
|
|
2120
|
-
inputSchema: z34.object({
|
|
2121
|
-
issue_id: z34.string().optional().describe("The ID of the related issue (if any)"),
|
|
2122
|
-
improvement_run_id: z34.string().optional().describe("The ID of the improvement run creating this proposal"),
|
|
2123
|
-
title: z34.string().describe("REQUIRED. Short user-friendly title describing the issue from the user's perspective"),
|
|
2124
|
-
diagnosis: z34.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
|
|
2125
|
-
proposed_fix: z34.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
|
|
2126
|
-
affected_files: z34.array(z34.string()).optional().describe("List of files that likely need to be modified"),
|
|
2127
|
-
confidence: z34.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
|
|
2128
|
-
page_path: z34.string().describe("REQUIRED. The page path where the issue occurs"),
|
|
2129
|
-
element_selector: z34.string().optional().describe("CSS selector of the affected element (if applicable)"),
|
|
2130
|
-
category: z34.enum(ISSUE_CATEGORIES2).describe("REQUIRED. Category: code_error, dead_click, rage_click, ux_friction, performance, form_issue, behavioral_anomaly, structural_issue"),
|
|
2131
|
-
severity: z34.enum(ISSUE_SEVERITIES).describe("REQUIRED. Severity: critical, high, medium, low, info"),
|
|
2132
|
-
execution_metadata: z34.object({
|
|
2133
|
-
difficulty_class: z34.enum(["trivial", "light", "heavy"]),
|
|
2134
|
-
difficulty_reasoning: z34.string(),
|
|
2135
|
-
model_used: z34.string(),
|
|
2136
|
-
was_escalated: z34.boolean().optional(),
|
|
2137
|
-
escalation_reason: z34.string().optional(),
|
|
2138
|
-
iterations: z34.number().optional(),
|
|
2139
|
-
credits_used: z34.number().optional(),
|
|
2140
|
-
duration_ms: z34.number().optional()
|
|
2141
|
-
}).optional().describe("AI execution metadata for tracking model selection and performance")
|
|
2142
|
-
}),
|
|
2143
|
-
handler: async (args) => {
|
|
2144
|
-
const { issue_id, improvement_run_id, title, diagnosis, proposed_fix, affected_files, confidence, page_path, element_selector, category, severity, execution_metadata } = args;
|
|
2145
|
-
if (!isApiConfigured()) {
|
|
2146
|
-
return apiNotConfiguredResult();
|
|
2147
|
-
}
|
|
2148
|
-
const { data, error } = await apiPost("/proposals", {
|
|
2149
|
-
issue_id,
|
|
2150
|
-
improvement_run_id,
|
|
2151
|
-
title,
|
|
2152
|
-
diagnosis,
|
|
2153
|
-
proposed_fix,
|
|
2154
|
-
affected_files,
|
|
2155
|
-
confidence,
|
|
2156
|
-
page_path,
|
|
2157
|
-
element_selector,
|
|
2158
|
-
category,
|
|
2159
|
-
severity,
|
|
2160
|
-
execution_metadata
|
|
2161
|
-
});
|
|
2162
|
-
if (error) {
|
|
2163
|
-
return errorResult(error);
|
|
2164
|
-
}
|
|
2165
|
-
return successResult(data);
|
|
2166
|
-
}
|
|
2167
|
-
};
|
|
2168
|
-
var listProposalsTool = {
|
|
2169
|
-
name: "list_proposals",
|
|
2170
|
-
description: "List proposals with optional filters. Use to review pending proposals, check resolved proposals, or filter by page/category/severity.",
|
|
2171
|
-
inputSchema: z34.object({
|
|
2172
|
-
status: z34.enum(PROPOSAL_STATUSES).optional().describe("Filter by single status"),
|
|
2173
|
-
statuses: z34.array(z34.enum(PROPOSAL_STATUSES)).optional().describe("Filter by multiple statuses"),
|
|
2174
|
-
issue_id: z34.string().optional().describe("Filter by related issue ID"),
|
|
2175
|
-
page_path: z34.string().optional().describe("Filter by page path"),
|
|
2176
|
-
category: z34.enum(ISSUE_CATEGORIES2).optional().describe("Filter by category"),
|
|
2177
|
-
severity: z34.enum(ISSUE_SEVERITIES).optional().describe("Filter by severity"),
|
|
2178
|
-
limit: z34.number().optional().default(20).describe("Maximum number of results (default: 20)")
|
|
2179
|
-
}),
|
|
2180
|
-
handler: async (args) => {
|
|
2181
|
-
const { status, statuses, issue_id, page_path, category, severity, limit } = args;
|
|
2182
|
-
if (!isApiConfigured()) {
|
|
2183
|
-
return apiNotConfiguredResult();
|
|
2184
|
-
}
|
|
2185
|
-
const params = {
|
|
2186
|
-
limit: limit ?? 20
|
|
2187
|
-
};
|
|
2188
|
-
if (status)
|
|
2189
|
-
params.status = status;
|
|
2190
|
-
if (statuses && statuses.length > 0)
|
|
2191
|
-
params.statuses = statuses.join(",");
|
|
2192
|
-
if (issue_id)
|
|
2193
|
-
params.issue_id = issue_id;
|
|
2194
|
-
if (page_path)
|
|
2195
|
-
params.page_path = page_path;
|
|
2196
|
-
if (category)
|
|
2197
|
-
params.category = category;
|
|
2198
|
-
if (severity)
|
|
2199
|
-
params.severity = severity;
|
|
2200
|
-
const { data, error } = await apiGet("/proposals", params);
|
|
2201
|
-
if (error) {
|
|
2202
|
-
return errorResult(error);
|
|
2203
|
-
}
|
|
2204
|
-
return successResult(data);
|
|
2205
|
-
}
|
|
2206
|
-
};
|
|
2207
|
-
var getProposalTool = {
|
|
2208
|
-
name: "get_proposal",
|
|
2209
|
-
description: "Get details of a specific proposal by ID. Use to review the full diagnosis and proposed fix before implementing.",
|
|
2210
|
-
inputSchema: z34.object({
|
|
2211
|
-
proposal_id: z34.string().describe("The ID of the proposal to retrieve")
|
|
2212
|
-
}),
|
|
2213
|
-
handler: async (args) => {
|
|
2214
|
-
const { proposal_id } = args;
|
|
2215
|
-
if (!isApiConfigured()) {
|
|
2216
|
-
return apiNotConfiguredResult();
|
|
2217
|
-
}
|
|
2218
|
-
const { data, error } = await apiGet(`/proposals/${proposal_id}`);
|
|
2219
|
-
if (error) {
|
|
2220
|
-
return errorResult(error);
|
|
2221
|
-
}
|
|
2222
|
-
return successResult(data);
|
|
2223
|
-
}
|
|
2224
|
-
};
|
|
2225
|
-
var resolveProposalTool = {
|
|
2226
|
-
name: "resolve_proposal",
|
|
2227
|
-
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.",
|
|
2228
|
-
inputSchema: z34.object({
|
|
2229
|
-
proposal_id: z34.string().describe("The ID of the proposal to mark as resolved"),
|
|
2230
|
-
member_id: z34.string().optional().describe("The ID of the member who resolved it (optional)")
|
|
2231
|
-
}),
|
|
2232
|
-
handler: async (args) => {
|
|
2233
|
-
const { proposal_id, member_id } = args;
|
|
2234
|
-
if (!isApiConfigured()) {
|
|
2235
|
-
return apiNotConfiguredResult();
|
|
2236
|
-
}
|
|
2237
|
-
const { data, error } = await apiPost(`/proposals/${proposal_id}/resolve`, { member_id });
|
|
2238
|
-
if (error) {
|
|
2239
|
-
return errorResult(error);
|
|
2240
|
-
}
|
|
2241
|
-
return successResult(data);
|
|
2242
|
-
}
|
|
2243
|
-
};
|
|
2244
|
-
var dismissProposalTool = {
|
|
2245
|
-
name: "dismiss_proposal",
|
|
2246
|
-
description: "Dismiss a proposal that won't be implemented. REQUIRED: proposal_id, reason. Use when the proposal is not applicable, the issue is intended behavior, or a different approach is preferred.",
|
|
2247
|
-
inputSchema: z34.object({
|
|
2248
|
-
proposal_id: z34.string().describe("REQUIRED. The ID of the proposal to dismiss"),
|
|
2249
|
-
reason: z34.string().describe("REQUIRED. Explanation of why the proposal is being dismissed"),
|
|
2250
|
-
member_id: z34.string().optional().describe("The ID of the member who dismissed it")
|
|
2251
|
-
}),
|
|
2252
|
-
handler: async (args) => {
|
|
2253
|
-
const { proposal_id, reason, member_id } = args;
|
|
2254
|
-
if (!isApiConfigured()) {
|
|
2255
|
-
return apiNotConfiguredResult();
|
|
2256
|
-
}
|
|
2257
|
-
const { data, error } = await apiPost(`/proposals/${proposal_id}/dismiss`, { reason, member_id });
|
|
2258
|
-
if (error) {
|
|
2259
|
-
return errorResult(error);
|
|
2260
|
-
}
|
|
2261
|
-
return successResult(data);
|
|
2262
|
-
}
|
|
2263
|
-
};
|
|
2264
|
-
var evaluateProposalTool = {
|
|
2265
|
-
name: "evaluate_proposal",
|
|
2266
|
-
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.",
|
|
2267
|
-
inputSchema: z34.object({
|
|
2268
|
-
proposal_id: z34.string().describe("The ID of the proposal to evaluate"),
|
|
2269
|
-
min_hours: z34.number().optional().default(24).describe("Minimum hours since resolution required (default: 24)")
|
|
2270
|
-
}),
|
|
2271
|
-
handler: async (args) => {
|
|
2272
|
-
const { proposal_id, min_hours } = args;
|
|
2273
|
-
if (!isApiConfigured()) {
|
|
2274
|
-
return apiNotConfiguredResult();
|
|
2275
|
-
}
|
|
2276
|
-
const minHoursParam = min_hours ?? 24;
|
|
2277
|
-
const { data, error } = await apiPost(`/proposals/${proposal_id}/evaluate?min_hours=${minHoursParam}`, {});
|
|
2278
|
-
if (error) {
|
|
2279
|
-
return errorResult(error);
|
|
2280
|
-
}
|
|
2281
|
-
return successResult(data);
|
|
2282
|
-
}
|
|
2283
|
-
};
|
|
2284
|
-
var listPendingProposalsTool = {
|
|
2285
|
-
name: "list_pending_proposals",
|
|
2286
|
-
description: "List all pending proposals awaiting review. Use to see what fix suggestions are available for implementation.",
|
|
2287
|
-
inputSchema: z34.object({}),
|
|
2288
|
-
handler: async () => {
|
|
2289
|
-
if (!isApiConfigured()) {
|
|
2290
|
-
return apiNotConfiguredResult();
|
|
2291
|
-
}
|
|
2292
|
-
const { data, error } = await apiGet("/proposals/pending");
|
|
2293
|
-
if (error) {
|
|
2294
|
-
return errorResult(error);
|
|
2295
|
-
}
|
|
2296
|
-
return successResult(data);
|
|
2297
|
-
}
|
|
2298
|
-
};
|
|
2299
|
-
var listProposalsForEvaluationTool = {
|
|
2300
|
-
name: "list_proposals_for_evaluation",
|
|
2301
|
-
description: "List resolved proposals that are ready for evaluation (24+ hours since resolution). Use during audit runs to check outcomes of previously resolved proposals.",
|
|
2302
|
-
inputSchema: z34.object({
|
|
2303
|
-
min_hours: z34.number().optional().default(24).describe("Minimum hours since resolution (default: 24)")
|
|
2808
|
+
}
|
|
2809
|
+
}
|
|
2810
|
+
return val;
|
|
2811
|
+
}, z34.array(z34.string()).optional()).describe("Triage specific sessions by ID. If provided, other filters are ignored."),
|
|
2812
|
+
days: z34.number().optional().default(7).describe("Lookback period in days (default: 7). Ignored if session_ids is provided."),
|
|
2813
|
+
page_path: z34.string().optional().describe("Filter to sessions that visited a specific page"),
|
|
2814
|
+
min_severity: z34.enum(["critical", "high", "medium", "low"]).optional().describe("Minimum severity to include in results"),
|
|
2815
|
+
limit: z34.number().optional().default(20).describe("Maximum sessions to return (default: 20, max: 100)")
|
|
2304
2816
|
}),
|
|
2305
2817
|
handler: async (args) => {
|
|
2306
|
-
const {
|
|
2818
|
+
const { session_ids, days, page_path, min_severity, limit } = args;
|
|
2307
2819
|
if (!isApiConfigured()) {
|
|
2308
2820
|
return apiNotConfiguredResult();
|
|
2309
2821
|
}
|
|
2310
|
-
const { data, error } = await apiGet("/
|
|
2822
|
+
const { data, error } = await apiGet("/sessions/triage", {
|
|
2823
|
+
session_ids: session_ids?.join(","),
|
|
2824
|
+
days: session_ids?.length ? void 0 : days ?? 7,
|
|
2825
|
+
page_path: session_ids?.length ? void 0 : page_path,
|
|
2826
|
+
min_severity,
|
|
2827
|
+
limit: session_ids?.length ? session_ids.length : limit ?? 20
|
|
2828
|
+
});
|
|
2311
2829
|
if (error) {
|
|
2312
2830
|
return errorResult(error);
|
|
2313
2831
|
}
|
|
2314
|
-
|
|
2832
|
+
const formattedOutput = formatTriageOutput(data);
|
|
2833
|
+
return { content: [{ type: "text", text: formattedOutput }] };
|
|
2315
2834
|
}
|
|
2316
2835
|
};
|
|
2317
2836
|
|
|
2318
|
-
// ../../libraries/mcp-tools/dist/tools/
|
|
2837
|
+
// ../../libraries/mcp-tools/dist/tools/proposal.js
|
|
2319
2838
|
import { z as z35 } from "zod";
|
|
2320
|
-
var
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
branch: branchName,
|
|
2388
|
-
from: fromBranch,
|
|
2389
|
-
provider: "github"
|
|
2390
|
-
});
|
|
2391
|
-
} else {
|
|
2392
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2393
|
-
const createRes = await gitlabRequest(`/projects/${encodedPath}/repository/branches`, {
|
|
2394
|
-
method: "POST",
|
|
2395
|
-
body: JSON.stringify({
|
|
2396
|
-
branch: branchName,
|
|
2397
|
-
ref: fromBranch
|
|
2398
|
-
})
|
|
2399
|
-
});
|
|
2400
|
-
if (!createRes.ok) {
|
|
2401
|
-
const err = await createRes.json();
|
|
2402
|
-
return errorResult(err.message || "Failed to create branch");
|
|
2403
|
-
}
|
|
2404
|
-
return successResult({
|
|
2405
|
-
success: true,
|
|
2406
|
-
branch: branchName,
|
|
2407
|
-
from: fromBranch,
|
|
2408
|
-
provider: "gitlab"
|
|
2409
|
-
});
|
|
2410
|
-
}
|
|
2411
|
-
} catch (error) {
|
|
2412
|
-
return errorResult(error instanceof Error ? error.message : "Failed to create branch");
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
};
|
|
2416
|
-
var getFileContentTool = {
|
|
2417
|
-
name: "get_file_content",
|
|
2418
|
-
description: "Get the content of a file from the repository. Works with both GitHub and GitLab.",
|
|
2419
|
-
inputSchema: z35.object({
|
|
2420
|
-
path: z35.string().describe("Path to the file in the repository"),
|
|
2421
|
-
branch: z35.string().optional().describe("Branch to read from (default: main)")
|
|
2422
|
-
}),
|
|
2423
|
-
handler: async (args) => {
|
|
2424
|
-
try {
|
|
2425
|
-
const ctx = getGitContext();
|
|
2426
|
-
const filePath = args.path;
|
|
2427
|
-
const branch = args.branch || "main";
|
|
2428
|
-
if (ctx.provider === "github") {
|
|
2429
|
-
const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
|
|
2430
|
-
if (!res.ok) {
|
|
2431
|
-
if (res.status === 404) {
|
|
2432
|
-
return errorResult(`File not found: ${filePath}`);
|
|
2433
|
-
}
|
|
2434
|
-
return errorResult(`Failed to get file: ${res.statusText}`);
|
|
2435
|
-
}
|
|
2436
|
-
const data = await res.json();
|
|
2437
|
-
const content = Buffer.from(data.content, "base64").toString("utf-8");
|
|
2438
|
-
return successResult({
|
|
2439
|
-
path: filePath,
|
|
2440
|
-
content,
|
|
2441
|
-
sha: data.sha,
|
|
2442
|
-
provider: "github"
|
|
2443
|
-
});
|
|
2444
|
-
} else {
|
|
2445
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2446
|
-
const encodedFilePath = encodeURIComponent(filePath);
|
|
2447
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
|
|
2448
|
-
if (!res.ok) {
|
|
2449
|
-
if (res.status === 404) {
|
|
2450
|
-
return errorResult(`File not found: ${filePath}`);
|
|
2451
|
-
}
|
|
2452
|
-
return errorResult(`Failed to get file: ${res.statusText}`);
|
|
2453
|
-
}
|
|
2454
|
-
const data = await res.json();
|
|
2455
|
-
const content = Buffer.from(data.content, "base64").toString("utf-8");
|
|
2456
|
-
return successResult({
|
|
2457
|
-
path: filePath,
|
|
2458
|
-
content,
|
|
2459
|
-
sha: data.content_sha256,
|
|
2460
|
-
provider: "gitlab"
|
|
2461
|
-
});
|
|
2462
|
-
}
|
|
2463
|
-
} catch (error) {
|
|
2464
|
-
return errorResult(error instanceof Error ? error.message : "Failed to get file content");
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
};
|
|
2468
|
-
var updateFileTool = {
|
|
2469
|
-
name: "update_file",
|
|
2470
|
-
description: "Update or create a file in the repository. Works with both GitHub and GitLab. SHA is auto-fetched for existing files.",
|
|
2471
|
-
inputSchema: z35.object({
|
|
2472
|
-
path: z35.string().describe("Path to the file in the repository"),
|
|
2473
|
-
content: z35.string().describe("New content for the file"),
|
|
2474
|
-
message: z35.string().describe("Commit message"),
|
|
2475
|
-
branch: z35.string().describe("Branch to commit to"),
|
|
2476
|
-
sha: z35.string().optional().describe("SHA of the file being replaced (auto-fetched if not provided)")
|
|
2477
|
-
}),
|
|
2478
|
-
handler: async (args) => {
|
|
2479
|
-
try {
|
|
2480
|
-
const ctx = getGitContext();
|
|
2481
|
-
const filePath = args.path;
|
|
2482
|
-
const content = args.content;
|
|
2483
|
-
const message = args.message;
|
|
2484
|
-
const branch = args.branch;
|
|
2485
|
-
let sha = args.sha;
|
|
2486
|
-
if (ctx.provider === "github") {
|
|
2487
|
-
if (!sha) {
|
|
2488
|
-
const checkRes = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}?ref=${branch}`);
|
|
2489
|
-
if (checkRes.ok) {
|
|
2490
|
-
const existingFile = await checkRes.json();
|
|
2491
|
-
sha = existingFile.sha;
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
|
-
const body = {
|
|
2495
|
-
message,
|
|
2496
|
-
content: Buffer.from(content).toString("base64"),
|
|
2497
|
-
branch
|
|
2498
|
-
};
|
|
2499
|
-
if (sha) {
|
|
2500
|
-
body.sha = sha;
|
|
2501
|
-
}
|
|
2502
|
-
const res = await githubRequest(`/repos/${ctx.repoFullName}/contents/${filePath}`, {
|
|
2503
|
-
method: "PUT",
|
|
2504
|
-
body: JSON.stringify(body)
|
|
2505
|
-
});
|
|
2506
|
-
if (!res.ok) {
|
|
2507
|
-
const err = await res.json();
|
|
2508
|
-
return errorResult(err.message || "Failed to update file");
|
|
2509
|
-
}
|
|
2510
|
-
const data = await res.json();
|
|
2511
|
-
return successResult({
|
|
2512
|
-
success: true,
|
|
2513
|
-
path: filePath,
|
|
2514
|
-
sha: data.content.sha,
|
|
2515
|
-
commit: data.commit.sha,
|
|
2516
|
-
provider: "github"
|
|
2517
|
-
});
|
|
2518
|
-
} else {
|
|
2519
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2520
|
-
const encodedFilePath = encodeURIComponent(filePath);
|
|
2521
|
-
let method = "POST";
|
|
2522
|
-
if (!sha) {
|
|
2523
|
-
const checkRes = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}?ref=${branch}`);
|
|
2524
|
-
if (checkRes.ok) {
|
|
2525
|
-
method = "PUT";
|
|
2526
|
-
}
|
|
2527
|
-
} else {
|
|
2528
|
-
method = "PUT";
|
|
2529
|
-
}
|
|
2530
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/repository/files/${encodedFilePath}`, {
|
|
2531
|
-
method,
|
|
2532
|
-
body: JSON.stringify({
|
|
2533
|
-
branch,
|
|
2534
|
-
content,
|
|
2535
|
-
commit_message: message,
|
|
2536
|
-
encoding: "text"
|
|
2537
|
-
})
|
|
2538
|
-
});
|
|
2539
|
-
if (!res.ok) {
|
|
2540
|
-
const err = await res.json();
|
|
2541
|
-
return errorResult(err.message || "Failed to update file");
|
|
2542
|
-
}
|
|
2543
|
-
const data = await res.json();
|
|
2544
|
-
return successResult({
|
|
2545
|
-
success: true,
|
|
2546
|
-
path: filePath,
|
|
2547
|
-
sha: data.content_sha256,
|
|
2548
|
-
provider: "gitlab"
|
|
2549
|
-
});
|
|
2550
|
-
}
|
|
2551
|
-
} catch (error) {
|
|
2552
|
-
return errorResult(error instanceof Error ? error.message : "Failed to update file");
|
|
2839
|
+
var ISSUE_CATEGORIES2 = [
|
|
2840
|
+
"code_error",
|
|
2841
|
+
"dead_click",
|
|
2842
|
+
"rage_click",
|
|
2843
|
+
"ux_friction",
|
|
2844
|
+
"performance",
|
|
2845
|
+
"form_issue",
|
|
2846
|
+
"behavioral_anomaly",
|
|
2847
|
+
"structural_issue"
|
|
2848
|
+
];
|
|
2849
|
+
var ISSUE_SEVERITIES = ["critical", "high", "medium", "low", "info"];
|
|
2850
|
+
var PROPOSAL_STATUSES = [
|
|
2851
|
+
"pending",
|
|
2852
|
+
"deployed",
|
|
2853
|
+
"dismissed",
|
|
2854
|
+
"evaluating",
|
|
2855
|
+
"succeeded",
|
|
2856
|
+
"failed",
|
|
2857
|
+
"deferred"
|
|
2858
|
+
];
|
|
2859
|
+
var createProposalTool = {
|
|
2860
|
+
name: "create_proposal",
|
|
2861
|
+
description: "AUDIT MODE ONLY. Create a fix proposal. REQUIRED: title, diagnosis, proposed_fix, confidence, page_path, category, severity. Proposals are suggestions that developers review and implement manually. Do NOT use in fix mode - use propose_fix instead.",
|
|
2862
|
+
inputSchema: z35.object({
|
|
2863
|
+
issue_id: z35.string().optional().describe("The ID of the related issue (if any)"),
|
|
2864
|
+
improvement_run_id: z35.string().optional().describe("The ID of the improvement run creating this proposal"),
|
|
2865
|
+
title: z35.string().describe("REQUIRED. Short user-friendly title describing the issue from the user's perspective"),
|
|
2866
|
+
diagnosis: z35.string().describe("REQUIRED. Detailed analysis of the root cause of the issue"),
|
|
2867
|
+
proposed_fix: z35.string().describe("REQUIRED. Description of the proposed fix and how it addresses the root cause"),
|
|
2868
|
+
affected_files: z35.array(z35.string()).optional().describe("List of files that likely need to be modified"),
|
|
2869
|
+
confidence: z35.number().min(0).max(1).describe("REQUIRED. Confidence level in the fix (0-1). Use <0.7 when uncertain."),
|
|
2870
|
+
page_path: z35.string().describe("REQUIRED. The page path where the issue occurs"),
|
|
2871
|
+
element_selector: z35.string().optional().describe("CSS selector of the affected element (if applicable)"),
|
|
2872
|
+
category: z35.enum(ISSUE_CATEGORIES2).describe("REQUIRED. Category: code_error, dead_click, rage_click, ux_friction, performance, form_issue, behavioral_anomaly, structural_issue"),
|
|
2873
|
+
severity: z35.enum(ISSUE_SEVERITIES).describe("REQUIRED. Severity: critical, high, medium, low, info"),
|
|
2874
|
+
execution_metadata: z35.object({
|
|
2875
|
+
difficulty_class: z35.enum(["trivial", "light", "heavy"]),
|
|
2876
|
+
difficulty_reasoning: z35.string(),
|
|
2877
|
+
model_used: z35.string(),
|
|
2878
|
+
was_escalated: z35.boolean().optional(),
|
|
2879
|
+
escalation_reason: z35.string().optional(),
|
|
2880
|
+
iterations: z35.number().optional(),
|
|
2881
|
+
credits_used: z35.number().optional(),
|
|
2882
|
+
duration_ms: z35.number().optional()
|
|
2883
|
+
}).optional().describe("AI execution metadata for tracking model selection and performance")
|
|
2884
|
+
}),
|
|
2885
|
+
handler: async (args) => {
|
|
2886
|
+
const { issue_id, improvement_run_id, title, diagnosis, proposed_fix, affected_files, confidence, page_path, element_selector, category, severity, execution_metadata } = args;
|
|
2887
|
+
if (!isApiConfigured()) {
|
|
2888
|
+
return apiNotConfiguredResult();
|
|
2889
|
+
}
|
|
2890
|
+
const { data, error } = await apiPost("/proposals", {
|
|
2891
|
+
issue_id,
|
|
2892
|
+
improvement_run_id,
|
|
2893
|
+
title,
|
|
2894
|
+
diagnosis,
|
|
2895
|
+
proposed_fix,
|
|
2896
|
+
affected_files,
|
|
2897
|
+
confidence,
|
|
2898
|
+
page_path,
|
|
2899
|
+
element_selector,
|
|
2900
|
+
category,
|
|
2901
|
+
severity,
|
|
2902
|
+
execution_metadata
|
|
2903
|
+
});
|
|
2904
|
+
if (error) {
|
|
2905
|
+
return errorResult(error);
|
|
2553
2906
|
}
|
|
2907
|
+
return successResult(data);
|
|
2554
2908
|
}
|
|
2555
2909
|
};
|
|
2556
|
-
var
|
|
2557
|
-
name: "
|
|
2558
|
-
description: "
|
|
2910
|
+
var listProposalsTool = {
|
|
2911
|
+
name: "list_proposals",
|
|
2912
|
+
description: "List proposals with optional filters. Use to review pending proposals, check deployed proposals, or filter by page/category/severity.",
|
|
2559
2913
|
inputSchema: z35.object({
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2914
|
+
status: z35.enum(PROPOSAL_STATUSES).optional().describe("Filter by single status"),
|
|
2915
|
+
statuses: z35.array(z35.enum(PROPOSAL_STATUSES)).optional().describe("Filter by multiple statuses"),
|
|
2916
|
+
issue_id: z35.string().optional().describe("Filter by related issue ID"),
|
|
2917
|
+
page_path: z35.string().optional().describe("Filter by page path"),
|
|
2918
|
+
category: z35.enum(ISSUE_CATEGORIES2).optional().describe("Filter by category"),
|
|
2919
|
+
severity: z35.enum(ISSUE_SEVERITIES).optional().describe("Filter by severity"),
|
|
2920
|
+
limit: z35.number().optional().default(20).describe("Maximum number of results (default: 20)")
|
|
2564
2921
|
}),
|
|
2565
2922
|
handler: async (args) => {
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
success: true,
|
|
2589
|
-
mr_number: data.number,
|
|
2590
|
-
url: data.html_url,
|
|
2591
|
-
state: data.state,
|
|
2592
|
-
provider: "github",
|
|
2593
|
-
type: "pull_request"
|
|
2594
|
-
});
|
|
2595
|
-
} else {
|
|
2596
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2597
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests`, {
|
|
2598
|
-
method: "POST",
|
|
2599
|
-
body: JSON.stringify({
|
|
2600
|
-
title,
|
|
2601
|
-
description,
|
|
2602
|
-
source_branch: sourceBranch,
|
|
2603
|
-
target_branch: targetBranch
|
|
2604
|
-
})
|
|
2605
|
-
});
|
|
2606
|
-
if (!res.ok) {
|
|
2607
|
-
const err = await res.json();
|
|
2608
|
-
return errorResult(err.message || "Failed to create merge request");
|
|
2609
|
-
}
|
|
2610
|
-
const data = await res.json();
|
|
2611
|
-
return successResult({
|
|
2612
|
-
success: true,
|
|
2613
|
-
mr_number: data.iid,
|
|
2614
|
-
url: data.web_url,
|
|
2615
|
-
state: data.state,
|
|
2616
|
-
provider: "gitlab",
|
|
2617
|
-
type: "merge_request"
|
|
2618
|
-
});
|
|
2619
|
-
}
|
|
2620
|
-
} catch (error) {
|
|
2621
|
-
return errorResult(error instanceof Error ? error.message : "Failed to create merge request");
|
|
2923
|
+
const { status, statuses, issue_id, page_path, category, severity, limit } = args;
|
|
2924
|
+
if (!isApiConfigured()) {
|
|
2925
|
+
return apiNotConfiguredResult();
|
|
2926
|
+
}
|
|
2927
|
+
const params = {
|
|
2928
|
+
limit: limit ?? 20
|
|
2929
|
+
};
|
|
2930
|
+
if (status)
|
|
2931
|
+
params.status = status;
|
|
2932
|
+
if (statuses && statuses.length > 0)
|
|
2933
|
+
params.statuses = statuses.join(",");
|
|
2934
|
+
if (issue_id)
|
|
2935
|
+
params.issue_id = issue_id;
|
|
2936
|
+
if (page_path)
|
|
2937
|
+
params.page_path = page_path;
|
|
2938
|
+
if (category)
|
|
2939
|
+
params.category = category;
|
|
2940
|
+
if (severity)
|
|
2941
|
+
params.severity = severity;
|
|
2942
|
+
const { data, error } = await apiGet("/proposals", params);
|
|
2943
|
+
if (error) {
|
|
2944
|
+
return errorResult(error);
|
|
2622
2945
|
}
|
|
2946
|
+
return successResult(data);
|
|
2623
2947
|
}
|
|
2624
2948
|
};
|
|
2625
|
-
var
|
|
2626
|
-
name: "
|
|
2627
|
-
description: "
|
|
2949
|
+
var getProposalTool = {
|
|
2950
|
+
name: "get_proposal",
|
|
2951
|
+
description: "Get details of a specific proposal by ID. Use to review the full diagnosis and proposed fix before implementing.",
|
|
2628
2952
|
inputSchema: z35.object({
|
|
2629
|
-
|
|
2953
|
+
proposal_id: z35.string().describe("The ID of the proposal to retrieve")
|
|
2630
2954
|
}),
|
|
2631
2955
|
handler: async (args) => {
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
return errorResult(`Pull request #${mrNumber} not found`);
|
|
2640
|
-
}
|
|
2641
|
-
return errorResult(`Failed to get pull request: ${res.statusText}`);
|
|
2642
|
-
}
|
|
2643
|
-
const data = await res.json();
|
|
2644
|
-
let status;
|
|
2645
|
-
if (data.merged) {
|
|
2646
|
-
status = "merged";
|
|
2647
|
-
} else if (data.state === "closed") {
|
|
2648
|
-
status = "closed";
|
|
2649
|
-
} else {
|
|
2650
|
-
status = "open";
|
|
2651
|
-
}
|
|
2652
|
-
return successResult({
|
|
2653
|
-
mr_number: data.number,
|
|
2654
|
-
title: data.title,
|
|
2655
|
-
status,
|
|
2656
|
-
url: data.html_url,
|
|
2657
|
-
source_branch: data.head.ref,
|
|
2658
|
-
target_branch: data.base.ref,
|
|
2659
|
-
created_at: data.created_at,
|
|
2660
|
-
merged_at: data.merged_at,
|
|
2661
|
-
closed_at: data.closed_at,
|
|
2662
|
-
provider: "github"
|
|
2663
|
-
});
|
|
2664
|
-
} else {
|
|
2665
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2666
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests/${mrNumber}`);
|
|
2667
|
-
if (!res.ok) {
|
|
2668
|
-
if (res.status === 404) {
|
|
2669
|
-
return errorResult(`Merge request !${mrNumber} not found`);
|
|
2670
|
-
}
|
|
2671
|
-
return errorResult(`Failed to get merge request: ${res.statusText}`);
|
|
2672
|
-
}
|
|
2673
|
-
const data = await res.json();
|
|
2674
|
-
let status;
|
|
2675
|
-
if (data.state === "merged") {
|
|
2676
|
-
status = "merged";
|
|
2677
|
-
} else if (data.state === "closed") {
|
|
2678
|
-
status = "closed";
|
|
2679
|
-
} else {
|
|
2680
|
-
status = "open";
|
|
2681
|
-
}
|
|
2682
|
-
return successResult({
|
|
2683
|
-
mr_number: data.iid,
|
|
2684
|
-
title: data.title,
|
|
2685
|
-
status,
|
|
2686
|
-
url: data.web_url,
|
|
2687
|
-
source_branch: data.source_branch,
|
|
2688
|
-
target_branch: data.target_branch,
|
|
2689
|
-
created_at: data.created_at,
|
|
2690
|
-
merged_at: data.merged_at,
|
|
2691
|
-
closed_at: data.closed_at,
|
|
2692
|
-
provider: "gitlab"
|
|
2693
|
-
});
|
|
2694
|
-
}
|
|
2695
|
-
} catch (error) {
|
|
2696
|
-
return errorResult(error instanceof Error ? error.message : "Failed to check MR status");
|
|
2956
|
+
const { proposal_id } = args;
|
|
2957
|
+
if (!isApiConfigured()) {
|
|
2958
|
+
return apiNotConfiguredResult();
|
|
2959
|
+
}
|
|
2960
|
+
const { data, error } = await apiGet(`/proposals/${proposal_id}`);
|
|
2961
|
+
if (error) {
|
|
2962
|
+
return errorResult(error);
|
|
2697
2963
|
}
|
|
2964
|
+
return successResult(data);
|
|
2698
2965
|
}
|
|
2699
2966
|
};
|
|
2700
|
-
var
|
|
2701
|
-
name: "
|
|
2702
|
-
description: "
|
|
2967
|
+
var resolveProposalTool = {
|
|
2968
|
+
name: "resolve_proposal",
|
|
2969
|
+
description: "Mark a proposal as deployed after implementing the fix. This captures baseline metrics for later evaluation. Call this after you have implemented the proposed fix in code.",
|
|
2703
2970
|
inputSchema: z35.object({
|
|
2704
|
-
|
|
2971
|
+
proposal_id: z35.string().describe("The ID of the proposal to mark as deployed"),
|
|
2972
|
+
member_id: z35.string().optional().describe("The ID of the member who deployed it (optional)")
|
|
2705
2973
|
}),
|
|
2706
2974
|
handler: async (args) => {
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
}
|
|
2715
|
-
const data = await res.json();
|
|
2716
|
-
const mrs = data.map((pr) => ({
|
|
2717
|
-
mr_number: pr.number,
|
|
2718
|
-
title: pr.title,
|
|
2719
|
-
url: pr.html_url,
|
|
2720
|
-
source_branch: pr.head.ref,
|
|
2721
|
-
target_branch: pr.base.ref,
|
|
2722
|
-
created_at: pr.created_at
|
|
2723
|
-
}));
|
|
2724
|
-
return successResult({
|
|
2725
|
-
mrs,
|
|
2726
|
-
count: mrs.length,
|
|
2727
|
-
provider: "github"
|
|
2728
|
-
});
|
|
2729
|
-
} else {
|
|
2730
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2731
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/merge_requests?state=opened&per_page=${limit}`);
|
|
2732
|
-
if (!res.ok) {
|
|
2733
|
-
return errorResult(`Failed to list merge requests: ${res.statusText}`);
|
|
2734
|
-
}
|
|
2735
|
-
const data = await res.json();
|
|
2736
|
-
const mrs = data.map((mr) => ({
|
|
2737
|
-
mr_number: mr.iid,
|
|
2738
|
-
title: mr.title,
|
|
2739
|
-
url: mr.web_url,
|
|
2740
|
-
source_branch: mr.source_branch,
|
|
2741
|
-
target_branch: mr.target_branch,
|
|
2742
|
-
created_at: mr.created_at
|
|
2743
|
-
}));
|
|
2744
|
-
return successResult({
|
|
2745
|
-
mrs,
|
|
2746
|
-
count: mrs.length,
|
|
2747
|
-
provider: "gitlab"
|
|
2748
|
-
});
|
|
2749
|
-
}
|
|
2750
|
-
} catch (error) {
|
|
2751
|
-
return errorResult(error instanceof Error ? error.message : "Failed to list open MRs");
|
|
2975
|
+
const { proposal_id, member_id } = args;
|
|
2976
|
+
if (!isApiConfigured()) {
|
|
2977
|
+
return apiNotConfiguredResult();
|
|
2978
|
+
}
|
|
2979
|
+
const { data, error } = await apiPost(`/proposals/${proposal_id}/resolve`, { member_id });
|
|
2980
|
+
if (error) {
|
|
2981
|
+
return errorResult(error);
|
|
2752
2982
|
}
|
|
2983
|
+
return successResult(data);
|
|
2753
2984
|
}
|
|
2754
|
-
};
|
|
2755
|
-
var
|
|
2756
|
-
name: "
|
|
2757
|
-
description: "
|
|
2985
|
+
};
|
|
2986
|
+
var dismissProposalTool = {
|
|
2987
|
+
name: "dismiss_proposal",
|
|
2988
|
+
description: "Dismiss a proposal that won't be implemented. REQUIRED: proposal_id, reason. Use when the proposal is not applicable, the issue is intended behavior, or a different approach is preferred.",
|
|
2758
2989
|
inputSchema: z35.object({
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2990
|
+
proposal_id: z35.string().describe("REQUIRED. The ID of the proposal to dismiss"),
|
|
2991
|
+
reason: z35.string().describe("REQUIRED. Explanation of why the proposal is being dismissed"),
|
|
2992
|
+
member_id: z35.string().optional().describe("The ID of the member who dismissed it")
|
|
2762
2993
|
}),
|
|
2763
2994
|
handler: async (args) => {
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
|
|
2772
|
-
if (!res.ok) {
|
|
2773
|
-
return errorResult(`Failed to list repository: ${res.statusText}`);
|
|
2774
|
-
}
|
|
2775
|
-
const data = await res.json();
|
|
2776
|
-
let items = data.tree;
|
|
2777
|
-
if (dirPath) {
|
|
2778
|
-
const prefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
|
|
2779
|
-
items = items.filter((item) => item.path.startsWith(prefix));
|
|
2780
|
-
}
|
|
2781
|
-
const files = items.filter((item) => item.type === "blob").map((item) => ({
|
|
2782
|
-
path: item.path,
|
|
2783
|
-
type: "file",
|
|
2784
|
-
size: item.size
|
|
2785
|
-
}));
|
|
2786
|
-
const dirs = items.filter((item) => item.type === "tree").map((item) => ({
|
|
2787
|
-
path: item.path,
|
|
2788
|
-
type: "directory"
|
|
2789
|
-
}));
|
|
2790
|
-
return successResult({
|
|
2791
|
-
path: dirPath || "/",
|
|
2792
|
-
files: files.slice(0, 200),
|
|
2793
|
-
directories: dirs.slice(0, 100),
|
|
2794
|
-
total_files: files.length,
|
|
2795
|
-
total_directories: dirs.length,
|
|
2796
|
-
truncated: data.truncated || files.length > 200,
|
|
2797
|
-
provider: "github"
|
|
2798
|
-
});
|
|
2799
|
-
} else {
|
|
2800
|
-
const endpoint = dirPath ? `/repos/${ctx.repoFullName}/contents/${dirPath}?ref=${branch}` : `/repos/${ctx.repoFullName}/contents?ref=${branch}`;
|
|
2801
|
-
const res = await githubRequest(endpoint);
|
|
2802
|
-
if (!res.ok) {
|
|
2803
|
-
if (res.status === 404) {
|
|
2804
|
-
return errorResult(`Directory not found: ${dirPath || "/"}`);
|
|
2805
|
-
}
|
|
2806
|
-
return errorResult(`Failed to list directory: ${res.statusText}`);
|
|
2807
|
-
}
|
|
2808
|
-
const data = await res.json();
|
|
2809
|
-
const files = data.filter((item) => item.type === "file").map((item) => ({
|
|
2810
|
-
name: item.name,
|
|
2811
|
-
path: item.path,
|
|
2812
|
-
type: "file",
|
|
2813
|
-
size: item.size
|
|
2814
|
-
}));
|
|
2815
|
-
const directories = data.filter((item) => item.type === "dir").map((item) => ({
|
|
2816
|
-
name: item.name,
|
|
2817
|
-
path: item.path,
|
|
2818
|
-
type: "directory"
|
|
2819
|
-
}));
|
|
2820
|
-
return successResult({
|
|
2821
|
-
path: dirPath || "/",
|
|
2822
|
-
files,
|
|
2823
|
-
directories,
|
|
2824
|
-
provider: "github"
|
|
2825
|
-
});
|
|
2826
|
-
}
|
|
2827
|
-
} else {
|
|
2828
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2829
|
-
const pathParam = dirPath ? `&path=${encodeURIComponent(dirPath)}` : "";
|
|
2830
|
-
const recursiveParam = recursive ? "&recursive=true" : "";
|
|
2831
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}${pathParam}${recursiveParam}&per_page=100`);
|
|
2832
|
-
if (!res.ok) {
|
|
2833
|
-
if (res.status === 404) {
|
|
2834
|
-
return errorResult(`Directory not found: ${dirPath || "/"}`);
|
|
2835
|
-
}
|
|
2836
|
-
return errorResult(`Failed to list directory: ${res.statusText}`);
|
|
2837
|
-
}
|
|
2838
|
-
const data = await res.json();
|
|
2839
|
-
const files = data.filter((item) => item.type === "blob").map((item) => ({
|
|
2840
|
-
name: item.name,
|
|
2841
|
-
path: item.path,
|
|
2842
|
-
type: "file"
|
|
2843
|
-
}));
|
|
2844
|
-
const directories = data.filter((item) => item.type === "tree").map((item) => ({
|
|
2845
|
-
name: item.name,
|
|
2846
|
-
path: item.path,
|
|
2847
|
-
type: "directory"
|
|
2848
|
-
}));
|
|
2849
|
-
return successResult({
|
|
2850
|
-
path: dirPath || "/",
|
|
2851
|
-
files,
|
|
2852
|
-
directories,
|
|
2853
|
-
provider: "gitlab"
|
|
2854
|
-
});
|
|
2855
|
-
}
|
|
2856
|
-
} catch (error) {
|
|
2857
|
-
return errorResult(error instanceof Error ? error.message : "Failed to list repository files");
|
|
2995
|
+
const { proposal_id, reason, member_id } = args;
|
|
2996
|
+
if (!isApiConfigured()) {
|
|
2997
|
+
return apiNotConfiguredResult();
|
|
2998
|
+
}
|
|
2999
|
+
const { data, error } = await apiPost(`/proposals/${proposal_id}/dismiss`, { reason, member_id });
|
|
3000
|
+
if (error) {
|
|
3001
|
+
return errorResult(error);
|
|
2858
3002
|
}
|
|
3003
|
+
return successResult(data);
|
|
2859
3004
|
}
|
|
2860
3005
|
};
|
|
2861
|
-
var
|
|
2862
|
-
name: "
|
|
2863
|
-
description: "
|
|
3006
|
+
var evaluateProposalTool = {
|
|
3007
|
+
name: "evaluate_proposal",
|
|
3008
|
+
description: "Evaluate if a deployed proposal improved metrics. Compares post-deploy metrics to baseline. Returns success/partial/failed outcome with detailed verdict. Wait at least 24 hours after deployment for reliable results.",
|
|
2864
3009
|
inputSchema: z35.object({
|
|
2865
|
-
|
|
2866
|
-
|
|
3010
|
+
proposal_id: z35.string().describe("The ID of the proposal to evaluate"),
|
|
3011
|
+
min_hours: z35.number().optional().default(24).describe("Minimum hours since deployment required (default: 24)")
|
|
2867
3012
|
}),
|
|
2868
3013
|
handler: async (args) => {
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
const branch = args.branch || "main";
|
|
2873
|
-
if (ctx.provider === "github") {
|
|
2874
|
-
const res = await githubRequest(`/repos/${ctx.repoFullName}/git/trees/${branch}?recursive=1`);
|
|
2875
|
-
if (!res.ok) {
|
|
2876
|
-
return errorResult(`Failed to get repo tree: ${res.statusText}`);
|
|
2877
|
-
}
|
|
2878
|
-
const data = await res.json();
|
|
2879
|
-
const dirs = data.tree.filter((item) => item.type === "tree").map((item) => item.path);
|
|
2880
|
-
const tree = buildNestedTree(dirs, maxDepth);
|
|
2881
|
-
const totalDirs = dirs.length;
|
|
2882
|
-
return successResult({
|
|
2883
|
-
tree,
|
|
2884
|
-
total_directories: totalDirs,
|
|
2885
|
-
depth: maxDepth,
|
|
2886
|
-
provider: "github"
|
|
2887
|
-
});
|
|
2888
|
-
} else {
|
|
2889
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2890
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/repository/tree?ref=${branch}&recursive=true&per_page=100`);
|
|
2891
|
-
if (!res.ok) {
|
|
2892
|
-
return errorResult(`Failed to get repo tree: ${res.statusText}`);
|
|
2893
|
-
}
|
|
2894
|
-
const data = await res.json();
|
|
2895
|
-
const dirs = data.filter((item) => item.type === "tree").map((item) => item.path);
|
|
2896
|
-
const tree = buildNestedTree(dirs, maxDepth);
|
|
2897
|
-
const totalDirs = dirs.length;
|
|
2898
|
-
return successResult({
|
|
2899
|
-
tree,
|
|
2900
|
-
total_directories: totalDirs,
|
|
2901
|
-
depth: maxDepth,
|
|
2902
|
-
provider: "gitlab"
|
|
2903
|
-
});
|
|
2904
|
-
}
|
|
2905
|
-
} catch (error) {
|
|
2906
|
-
return errorResult(error instanceof Error ? error.message : "Failed to get repo tree");
|
|
3014
|
+
const { proposal_id, min_hours } = args;
|
|
3015
|
+
if (!isApiConfigured()) {
|
|
3016
|
+
return apiNotConfiguredResult();
|
|
2907
3017
|
}
|
|
3018
|
+
const minHoursParam = min_hours ?? 24;
|
|
3019
|
+
const { data, error } = await apiPost(`/proposals/${proposal_id}/evaluate?min_hours=${minHoursParam}`, {});
|
|
3020
|
+
if (error) {
|
|
3021
|
+
return errorResult(error);
|
|
3022
|
+
}
|
|
3023
|
+
return successResult(data);
|
|
2908
3024
|
}
|
|
2909
3025
|
};
|
|
2910
|
-
var
|
|
2911
|
-
name: "
|
|
2912
|
-
description: "
|
|
3026
|
+
var listPendingProposalsTool = {
|
|
3027
|
+
name: "list_pending_proposals",
|
|
3028
|
+
description: "List all pending proposals awaiting review. Use to see what fix suggestions are available for implementation.",
|
|
3029
|
+
inputSchema: z35.object({}),
|
|
3030
|
+
handler: async () => {
|
|
3031
|
+
if (!isApiConfigured()) {
|
|
3032
|
+
return apiNotConfiguredResult();
|
|
3033
|
+
}
|
|
3034
|
+
const { data, error } = await apiGet("/proposals/pending");
|
|
3035
|
+
if (error) {
|
|
3036
|
+
return errorResult(error);
|
|
3037
|
+
}
|
|
3038
|
+
return successResult(data);
|
|
3039
|
+
}
|
|
3040
|
+
};
|
|
3041
|
+
var listProposalsForEvaluationTool = {
|
|
3042
|
+
name: "list_proposals_for_evaluation",
|
|
3043
|
+
description: "List deployed proposals that are ready for evaluation (24+ hours since deployment). Use during audit runs to check outcomes of previously deployed proposals.",
|
|
2913
3044
|
inputSchema: z35.object({
|
|
2914
|
-
|
|
2915
|
-
path_filter: z35.string().optional().describe('Restrict search to a directory (e.g., "client/src/components")'),
|
|
2916
|
-
extension: z35.string().optional().describe('Filter by file extension (e.g., "tsx", "ts", "css")')
|
|
3045
|
+
min_hours: z35.number().optional().default(24).describe("Minimum hours since deployment (default: 24)")
|
|
2917
3046
|
}),
|
|
2918
3047
|
handler: async (args) => {
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
const pathFilter = args.path_filter;
|
|
2923
|
-
const extension = args.extension;
|
|
2924
|
-
if (ctx.provider === "github") {
|
|
2925
|
-
let q = `${query} repo:${ctx.repoFullName}`;
|
|
2926
|
-
if (pathFilter)
|
|
2927
|
-
q += ` path:${pathFilter}`;
|
|
2928
|
-
if (extension)
|
|
2929
|
-
q += ` extension:${extension}`;
|
|
2930
|
-
const res = await githubRequest(`/search/code?q=${encodeURIComponent(q)}&per_page=10`, {
|
|
2931
|
-
headers: {
|
|
2932
|
-
Accept: "application/vnd.github.text-match+json"
|
|
2933
|
-
}
|
|
2934
|
-
});
|
|
2935
|
-
if (!res.ok) {
|
|
2936
|
-
const err = await res.json();
|
|
2937
|
-
return errorResult(err.message || `Code search failed: ${res.status}`);
|
|
2938
|
-
}
|
|
2939
|
-
const data = await res.json();
|
|
2940
|
-
const results = data.items.map((item) => ({
|
|
2941
|
-
path: item.path,
|
|
2942
|
-
score: item.score,
|
|
2943
|
-
matched_lines: (item.text_matches ?? []).map((m) => m.fragment),
|
|
2944
|
-
url: item.html_url
|
|
2945
|
-
}));
|
|
2946
|
-
return successResult({
|
|
2947
|
-
total_count: data.total_count,
|
|
2948
|
-
results,
|
|
2949
|
-
provider: "github"
|
|
2950
|
-
});
|
|
2951
|
-
} else {
|
|
2952
|
-
const encodedPath = encodeURIComponent(ctx.repoFullName);
|
|
2953
|
-
const searchQuery = pathFilter ? `${query} filename:${pathFilter}` : query;
|
|
2954
|
-
const res = await gitlabRequest(`/projects/${encodedPath}/search?scope=blobs&search=${encodeURIComponent(searchQuery)}&per_page=10`);
|
|
2955
|
-
if (!res.ok) {
|
|
2956
|
-
const err = await res.json();
|
|
2957
|
-
return errorResult(err.message || `Code search failed: ${res.status}`);
|
|
2958
|
-
}
|
|
2959
|
-
const data = await res.json();
|
|
2960
|
-
const results = data.map((item) => ({
|
|
2961
|
-
path: item.path,
|
|
2962
|
-
matched_lines: [item.data],
|
|
2963
|
-
startline: item.startline
|
|
2964
|
-
}));
|
|
2965
|
-
return successResult({
|
|
2966
|
-
total_count: results.length,
|
|
2967
|
-
results,
|
|
2968
|
-
provider: "gitlab"
|
|
2969
|
-
});
|
|
2970
|
-
}
|
|
2971
|
-
} catch (error) {
|
|
2972
|
-
return errorResult(error instanceof Error ? error.message : "Code search failed");
|
|
3048
|
+
const { min_hours } = args;
|
|
3049
|
+
if (!isApiConfigured()) {
|
|
3050
|
+
return apiNotConfiguredResult();
|
|
2973
3051
|
}
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
const tree = {};
|
|
2978
|
-
for (const dir of dirs) {
|
|
2979
|
-
const parts = dir.split("/");
|
|
2980
|
-
if (parts.length > maxDepth)
|
|
2981
|
-
continue;
|
|
2982
|
-
let current = tree;
|
|
2983
|
-
for (const part of parts) {
|
|
2984
|
-
const key = `${part}/`;
|
|
2985
|
-
if (!current[key]) {
|
|
2986
|
-
current[key] = {};
|
|
2987
|
-
}
|
|
2988
|
-
current = current[key];
|
|
3052
|
+
const { data, error } = await apiGet("/proposals/ready-for-evaluation", { min_hours: min_hours ?? 24 });
|
|
3053
|
+
if (error) {
|
|
3054
|
+
return errorResult(error);
|
|
2989
3055
|
}
|
|
3056
|
+
return successResult(data);
|
|
2990
3057
|
}
|
|
2991
|
-
|
|
2992
|
-
}
|
|
3058
|
+
};
|
|
2993
3059
|
|
|
2994
3060
|
// ../../libraries/mcp-tools/dist/tools/index.js
|
|
2995
3061
|
var allTools = [
|
|
@@ -3050,6 +3116,7 @@ var allTools = [
|
|
|
3050
3116
|
updateRemediationStatusTool,
|
|
3051
3117
|
listRemediationsByStatusTool,
|
|
3052
3118
|
getRemediationByPrTool,
|
|
3119
|
+
updateRemediationTool,
|
|
3053
3120
|
// Scan
|
|
3054
3121
|
scanSiteTool,
|
|
3055
3122
|
// Search
|