@mushi-mushi/mcp 0.20.0 → 0.20.1
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/README.md +3 -3
- package/dist/catalog.js +1 -1
- package/dist/{chunk-OZJEY3QR.js → chunk-TP2QQWDJ.js} +22 -9
- package/dist/{chunk-VBXXUK5G.js → chunk-ZSVRLSW5.js} +4 -2
- package/dist/clients.d.ts +1 -1
- package/dist/clients.js +2 -2
- package/dist/feature-groups.js +1 -1
- package/dist/index.js +402 -195
- package/dist/server.js +378 -186
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -375,7 +375,7 @@ var TDD_TOOL_CATALOG = [
|
|
|
375
375
|
{
|
|
376
376
|
name: "list_byok_keys",
|
|
377
377
|
title: "List your API key pool",
|
|
378
|
-
description: "List the project's BYOK API keys grouped by provider (anthropic | openai | firecrawl | browserbase | cursor). Returns { keys: [{ id,
|
|
378
|
+
description: "List the project's BYOK API keys grouped by provider (anthropic | openai | firecrawl | browserbase | cursor). Returns pooled { keys: [{ id, provider_slug, label, priority, status, cooldown_until, test_status, key_hint, base_url, last_tested_at, last_used_at }] } plus read-only { legacyKeys } metadata for credentials saved before the pooled lifecycle \u2014 never the raw secret. Read-only. Use to see which keys are validated, active, pending, legacy, or rate-limited; use add_byok_key to add one.",
|
|
379
379
|
scope: "mcp:read",
|
|
380
380
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
381
381
|
useCase: "Which API keys are active and which are rate-limited?"
|
|
@@ -383,11 +383,27 @@ var TDD_TOOL_CATALOG = [
|
|
|
383
383
|
{
|
|
384
384
|
name: "add_byok_key",
|
|
385
385
|
title: "Add an API key",
|
|
386
|
-
description: "Add a BYOK API key
|
|
386
|
+
description: "Add and immediately validate a BYOK API key for anthropic | openai | firecrawl | browserbase | cursor. The raw key is stored encrypted in Supabase Vault and never returned; failed probes remain quarantined. Optional baseUrl is accepted only for allow-listed OpenAI-compatible HTTPS providers. Write; NOT idempotent.",
|
|
387
387
|
scope: "mcp:write",
|
|
388
388
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
389
389
|
useCase: "Add a backup Anthropic key to the pool."
|
|
390
390
|
},
|
|
391
|
+
{
|
|
392
|
+
name: "test_byok_key",
|
|
393
|
+
title: "Test an API key",
|
|
394
|
+
description: "Re-test one pooled BYOK credential by key id. A successful provider probe activates the key; auth, quota, and network failures keep it out of the runtime pool. Returns the sanitized validation result and updated key metadata. Write; idempotent.",
|
|
395
|
+
scope: "mcp:write",
|
|
396
|
+
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
397
|
+
useCase: "Re-test a quarantined OpenAI key after fixing its account quota."
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: "remove_byok_key",
|
|
401
|
+
title: "Remove an API key",
|
|
402
|
+
description: "Permanently remove one pooled BYOK credential by key id from the authenticated project, including its Vault secret. Write; destructive; idempotent.",
|
|
403
|
+
scope: "mcp:write",
|
|
404
|
+
hints: { readOnly: false, destructive: true, idempotent: true, openWorld: true },
|
|
405
|
+
useCase: "Remove a rotated or compromised provider key from the runtime pool."
|
|
406
|
+
},
|
|
391
407
|
{
|
|
392
408
|
name: "list_pending_review_stories",
|
|
393
409
|
title: "List QA stories pending review",
|
|
@@ -561,7 +577,9 @@ var USE_MUSHI_INTENTS = {
|
|
|
561
577
|
"activation_status",
|
|
562
578
|
"get_backend_health",
|
|
563
579
|
"list_byok_keys",
|
|
564
|
-
"add_byok_key"
|
|
580
|
+
"add_byok_key",
|
|
581
|
+
"test_byok_key",
|
|
582
|
+
"remove_byok_key"
|
|
565
583
|
],
|
|
566
584
|
hint: "Call mushi_setup first \u2014 it diagnoses setup gaps and returns the next command to run."
|
|
567
585
|
},
|
|
@@ -591,12 +609,7 @@ var USE_MUSHI_INTENTS = {
|
|
|
591
609
|
},
|
|
592
610
|
audit: {
|
|
593
611
|
label: "Audit / health check",
|
|
594
|
-
tools: [
|
|
595
|
-
"run_fullstack_audit",
|
|
596
|
-
"get_backend_health",
|
|
597
|
-
"get_dashboard",
|
|
598
|
-
"get_usage"
|
|
599
|
-
],
|
|
612
|
+
tools: ["run_fullstack_audit", "get_backend_health", "get_dashboard", "get_usage"],
|
|
600
613
|
hint: "Call run_fullstack_audit for a full-stack health scorecard."
|
|
601
614
|
}
|
|
602
615
|
};
|
|
@@ -759,8 +772,10 @@ var TOOL_FEATURE_MAP = {
|
|
|
759
772
|
start_skill_pipeline: "skills",
|
|
760
773
|
get_pipeline_run: "skills",
|
|
761
774
|
checkin_pipeline_step: "skills",
|
|
762
|
-
list_byok_keys: "
|
|
763
|
-
add_byok_key: "
|
|
775
|
+
list_byok_keys: "setup",
|
|
776
|
+
add_byok_key: "setup",
|
|
777
|
+
test_byok_key: "setup",
|
|
778
|
+
remove_byok_key: "setup",
|
|
764
779
|
// audit
|
|
765
780
|
run_fullstack_audit: "audit",
|
|
766
781
|
get_backend_health: "audit",
|
|
@@ -1004,7 +1019,7 @@ function createMushiServer(config) {
|
|
|
1004
1019
|
// Authorization is accepted by `adminOrApiKey` only as a JWT, but we
|
|
1005
1020
|
// still send it for endpoints still behind plain `jwtAuth` (legacy)
|
|
1006
1021
|
// and for transparent proxies that strip X-Mushi-* headers.
|
|
1007
|
-
|
|
1022
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1008
1023
|
"X-Mushi-Api-Key": apiKey,
|
|
1009
1024
|
"X-Request-Id": requestId,
|
|
1010
1025
|
...projectId ? { "X-Mushi-Project-Id": projectId } : {},
|
|
@@ -1055,9 +1070,7 @@ function createMushiServer(config) {
|
|
|
1055
1070
|
}
|
|
1056
1071
|
if (projectId) return projectId;
|
|
1057
1072
|
try {
|
|
1058
|
-
const data = await apiCall(
|
|
1059
|
-
"/v1/admin/mcp/account-overview"
|
|
1060
|
-
);
|
|
1073
|
+
const data = await apiCall("/v1/admin/mcp/account-overview");
|
|
1061
1074
|
if (data.total === 1 && data.projects[0]) {
|
|
1062
1075
|
return data.projects[0].id;
|
|
1063
1076
|
}
|
|
@@ -1168,9 +1181,12 @@ function createMushiServer(config) {
|
|
|
1168
1181
|
params.set("limit", String(Math.min(args.limit ?? 20, 100)));
|
|
1169
1182
|
const pid = await resolveProjectId(args.project_id);
|
|
1170
1183
|
const extraHeaders = pid !== projectId ? { "X-Mushi-Project-Id": pid } : {};
|
|
1171
|
-
const data = await apiCall(
|
|
1172
|
-
|
|
1173
|
-
|
|
1184
|
+
const data = await apiCall(
|
|
1185
|
+
`/v1/admin/reports?${params}`,
|
|
1186
|
+
{
|
|
1187
|
+
headers: extraHeaders
|
|
1188
|
+
}
|
|
1189
|
+
);
|
|
1174
1190
|
return jsonResult(data);
|
|
1175
1191
|
}
|
|
1176
1192
|
);
|
|
@@ -1193,7 +1209,10 @@ function createMushiServer(config) {
|
|
|
1193
1209
|
return {
|
|
1194
1210
|
...res,
|
|
1195
1211
|
resource_links: [
|
|
1196
|
-
{
|
|
1212
|
+
{
|
|
1213
|
+
uri: `project://reports/${args.reportId}`,
|
|
1214
|
+
title: `Report ${args.reportId.slice(0, 8)}\u2026`
|
|
1215
|
+
}
|
|
1197
1216
|
]
|
|
1198
1217
|
};
|
|
1199
1218
|
}
|
|
@@ -1297,17 +1316,20 @@ function createMushiServer(config) {
|
|
|
1297
1316
|
const report = await apiCall(`/v1/admin/reports/${args.reportId}`, {
|
|
1298
1317
|
headers
|
|
1299
1318
|
});
|
|
1300
|
-
return wrappedJsonResult(
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1319
|
+
return wrappedJsonResult(
|
|
1320
|
+
{
|
|
1321
|
+
report,
|
|
1322
|
+
// Paste-ready fix prompt composed server-side by composeFixPacket()
|
|
1323
|
+
// (diagnosis + repro + suggested fix + relevant code + blast radius).
|
|
1324
|
+
// Hand this straight to the editing agent — no second LLM key needed.
|
|
1325
|
+
fixPrompt: report.fix_packet ?? null,
|
|
1326
|
+
reproductionSteps: report.reproduction_steps ?? [],
|
|
1327
|
+
component: report.component,
|
|
1328
|
+
rootCause: report.stage2_analysis?.rootCause,
|
|
1329
|
+
bugOntologyTags: report.bug_ontology_tags
|
|
1330
|
+
},
|
|
1331
|
+
"fix context"
|
|
1332
|
+
);
|
|
1311
1333
|
}
|
|
1312
1334
|
);
|
|
1313
1335
|
server.registerTool(
|
|
@@ -1356,7 +1378,9 @@ function createMushiServer(config) {
|
|
|
1356
1378
|
seed: args.seed,
|
|
1357
1379
|
depth: String(Math.min(args.depth ?? 2, 4))
|
|
1358
1380
|
});
|
|
1359
|
-
const data = await apiCall(
|
|
1381
|
+
const data = await apiCall(
|
|
1382
|
+
`/v1/admin/graph/traverse?${params}`
|
|
1383
|
+
);
|
|
1360
1384
|
return jsonResult(data);
|
|
1361
1385
|
}
|
|
1362
1386
|
);
|
|
@@ -1367,7 +1391,9 @@ function createMushiServer(config) {
|
|
|
1367
1391
|
description: descOf("get_graph_neighborhood"),
|
|
1368
1392
|
annotations: annotationsFor("get_graph_neighborhood"),
|
|
1369
1393
|
inputSchema: {
|
|
1370
|
-
seed: z.string().describe(
|
|
1394
|
+
seed: z.string().describe(
|
|
1395
|
+
"Starting graph node id or label to expand around (e.g. an inventory Action id or component name)"
|
|
1396
|
+
),
|
|
1371
1397
|
depth: z.number().int().min(1).max(4).optional().describe("BFS hops to traverse outward. Default 2; clamped to a max of 4.")
|
|
1372
1398
|
},
|
|
1373
1399
|
outputSchema: {
|
|
@@ -1380,7 +1406,9 @@ function createMushiServer(config) {
|
|
|
1380
1406
|
seed: args.seed,
|
|
1381
1407
|
depth: String(Math.min(args.depth ?? 2, 4))
|
|
1382
1408
|
});
|
|
1383
|
-
const data = await apiCall(
|
|
1409
|
+
const data = await apiCall(
|
|
1410
|
+
`/v1/admin/graph/traverse?${params}`
|
|
1411
|
+
);
|
|
1384
1412
|
return jsonResult(data);
|
|
1385
1413
|
}
|
|
1386
1414
|
);
|
|
@@ -1390,7 +1418,9 @@ function createMushiServer(config) {
|
|
|
1390
1418
|
title: titleOf("get_graph_node"),
|
|
1391
1419
|
description: descOf("get_graph_node"),
|
|
1392
1420
|
annotations: annotationsFor("get_graph_node"),
|
|
1393
|
-
inputSchema: {
|
|
1421
|
+
inputSchema: {
|
|
1422
|
+
nodeId: z.string().describe("The graph_nodes.id of the node to fetch (UUID).")
|
|
1423
|
+
},
|
|
1394
1424
|
outputSchema: {
|
|
1395
1425
|
node: z.unknown().describe("Single graph_nodes row including metadata (Action nodes carry v2 status)")
|
|
1396
1426
|
}
|
|
@@ -1412,7 +1442,8 @@ function createMushiServer(config) {
|
|
|
1412
1442
|
},
|
|
1413
1443
|
async (args) => {
|
|
1414
1444
|
const pid = args.projectId ?? projectId;
|
|
1415
|
-
if (!pid)
|
|
1445
|
+
if (!pid)
|
|
1446
|
+
throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for get_inventory");
|
|
1416
1447
|
return jsonText(await apiCall(`/v1/admin/inventory/${pid}`));
|
|
1417
1448
|
}
|
|
1418
1449
|
);
|
|
@@ -1430,7 +1461,8 @@ function createMushiServer(config) {
|
|
|
1430
1461
|
},
|
|
1431
1462
|
async (args) => {
|
|
1432
1463
|
const pid = args.projectId ?? projectId;
|
|
1433
|
-
if (!pid)
|
|
1464
|
+
if (!pid)
|
|
1465
|
+
throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for diff_inventory");
|
|
1434
1466
|
const q = new URLSearchParams({ from: args.fromSha, to: args.toSha });
|
|
1435
1467
|
return jsonText(await apiCall(`/v1/admin/inventory/${pid}/diff?${q}`));
|
|
1436
1468
|
}
|
|
@@ -1453,12 +1485,19 @@ function createMushiServer(config) {
|
|
|
1453
1485
|
},
|
|
1454
1486
|
async (args) => {
|
|
1455
1487
|
const pid = args.projectId ?? projectId;
|
|
1456
|
-
if (!pid)
|
|
1488
|
+
if (!pid)
|
|
1489
|
+
throw new MushiApiError(
|
|
1490
|
+
400,
|
|
1491
|
+
"MISSING_PROJECT",
|
|
1492
|
+
"projectId is required for list_gate_findings"
|
|
1493
|
+
);
|
|
1457
1494
|
const q = new URLSearchParams();
|
|
1458
1495
|
if (args.gate) q.set("gate", args.gate);
|
|
1459
1496
|
if (args.severity) q.set("severity", args.severity);
|
|
1460
1497
|
const suffix = q.toString() ? `?${q}` : "";
|
|
1461
|
-
const data = await apiCall(
|
|
1498
|
+
const data = await apiCall(
|
|
1499
|
+
`/v1/admin/inventory/${pid}/findings${suffix}`
|
|
1500
|
+
);
|
|
1462
1501
|
return jsonResult(data);
|
|
1463
1502
|
}
|
|
1464
1503
|
);
|
|
@@ -1468,7 +1507,9 @@ function createMushiServer(config) {
|
|
|
1468
1507
|
title: titleOf("suggest_fix"),
|
|
1469
1508
|
description: descOf("suggest_fix"),
|
|
1470
1509
|
annotations: annotationsFor("suggest_fix"),
|
|
1471
|
-
inputSchema: {
|
|
1510
|
+
inputSchema: {
|
|
1511
|
+
reportId: z.string().describe("Report UUID to read the Stage-2 suggested-fix slice for")
|
|
1512
|
+
},
|
|
1472
1513
|
outputSchema: {
|
|
1473
1514
|
reportId: z.string().describe("The report this slice was read from"),
|
|
1474
1515
|
rootCause: z.unknown().describe("Stage-2 root-cause hint, or null if not yet classified"),
|
|
@@ -1497,7 +1538,11 @@ function createMushiServer(config) {
|
|
|
1497
1538
|
title: titleOf("run_nl_query"),
|
|
1498
1539
|
description: descOf("run_nl_query"),
|
|
1499
1540
|
annotations: annotationsFor("run_nl_query"),
|
|
1500
|
-
inputSchema: {
|
|
1541
|
+
inputSchema: {
|
|
1542
|
+
question: z.string().describe(
|
|
1543
|
+
'Question in plain English, e.g. "Which components had the most critical bugs this week?"'
|
|
1544
|
+
)
|
|
1545
|
+
}
|
|
1501
1546
|
},
|
|
1502
1547
|
async (args) => {
|
|
1503
1548
|
const data = await apiCall("/v1/admin/query", {
|
|
@@ -1514,7 +1559,9 @@ function createMushiServer(config) {
|
|
|
1514
1559
|
description: descOf("diagnose_setup"),
|
|
1515
1560
|
annotations: annotationsFor("diagnose_setup"),
|
|
1516
1561
|
inputSchema: {
|
|
1517
|
-
mode: z.enum(["full", "ingest", "dispatch"]).optional().describe(
|
|
1562
|
+
mode: z.enum(["full", "ingest", "dispatch"]).optional().describe(
|
|
1563
|
+
"full (default) = ingest + dispatch; ingest = SDK pipeline only; dispatch = fix preflight only."
|
|
1564
|
+
),
|
|
1518
1565
|
project_id: z.string().optional().describe("Project UUID for dispatch checks (defaults to configured project)."),
|
|
1519
1566
|
projectId: z.string().optional().describe("Alias for project_id.")
|
|
1520
1567
|
},
|
|
@@ -1552,7 +1599,11 @@ function createMushiServer(config) {
|
|
|
1552
1599
|
}
|
|
1553
1600
|
if (mode === "dispatch") {
|
|
1554
1601
|
if (!resolvedId) {
|
|
1555
|
-
throw new MushiApiError(
|
|
1602
|
+
throw new MushiApiError(
|
|
1603
|
+
400,
|
|
1604
|
+
"MISSING_PROJECT",
|
|
1605
|
+
"project_id is required for dispatch mode"
|
|
1606
|
+
);
|
|
1556
1607
|
}
|
|
1557
1608
|
const data = await apiCall(`/v1/admin/projects/${resolvedId}/preflight`);
|
|
1558
1609
|
const failed = data.checks.filter((c) => !c.ready);
|
|
@@ -1686,7 +1737,12 @@ function createMushiServer(config) {
|
|
|
1686
1737
|
async (args) => {
|
|
1687
1738
|
const query = args.query ?? "";
|
|
1688
1739
|
const hits = searchMushiDocs(query, args.limit ?? 8);
|
|
1689
|
-
const results = hits.map(({ title, path, excerpt, score }) => ({
|
|
1740
|
+
const results = hits.map(({ title, path, excerpt, score }) => ({
|
|
1741
|
+
title,
|
|
1742
|
+
path,
|
|
1743
|
+
excerpt,
|
|
1744
|
+
score
|
|
1745
|
+
}));
|
|
1690
1746
|
return jsonResult({ query, results });
|
|
1691
1747
|
}
|
|
1692
1748
|
);
|
|
@@ -1836,7 +1892,9 @@ function createMushiServer(config) {
|
|
|
1836
1892
|
} : { error: String(reportRes.reason) };
|
|
1837
1893
|
return jsonText({
|
|
1838
1894
|
evidence,
|
|
1839
|
-
reporter_thread: timeline ?? {
|
|
1895
|
+
reporter_thread: timeline ?? {
|
|
1896
|
+
error: String(timelineRes.reason)
|
|
1897
|
+
}
|
|
1840
1898
|
});
|
|
1841
1899
|
}
|
|
1842
1900
|
);
|
|
@@ -1871,15 +1929,23 @@ function createMushiServer(config) {
|
|
|
1871
1929
|
const includeLogs = args.include_logs !== false;
|
|
1872
1930
|
const [reportRes, evidenceRes, similarRes, fixCtxRes, blastRes, logsRes] = await Promise.allSettled([
|
|
1873
1931
|
apiCall(`/v1/admin/reports/${args.report_id}`, { headers }),
|
|
1874
|
-
apiCall(`/v1/admin/reports/${args.report_id}/timeline`, {
|
|
1932
|
+
apiCall(`/v1/admin/reports/${args.report_id}/timeline`, {
|
|
1933
|
+
headers
|
|
1934
|
+
}),
|
|
1875
1935
|
apiCall(`/v1/admin/reports/similarity`, {
|
|
1876
1936
|
method: "POST",
|
|
1877
1937
|
headers,
|
|
1878
1938
|
body: JSON.stringify({ report_id: args.report_id })
|
|
1879
1939
|
}).catch(() => null),
|
|
1880
|
-
apiCall(`/v1/admin/reports/${args.report_id}/fix-context`, { headers }).catch(
|
|
1881
|
-
|
|
1882
|
-
|
|
1940
|
+
apiCall(`/v1/admin/reports/${args.report_id}/fix-context`, { headers }).catch(
|
|
1941
|
+
() => null
|
|
1942
|
+
),
|
|
1943
|
+
apiCall(`/v1/admin/reports/${args.report_id}/blast-radius`, { headers }).catch(
|
|
1944
|
+
() => null
|
|
1945
|
+
),
|
|
1946
|
+
includeLogs ? apiCall(`/v1/admin/mcp/logs/${pid}?limit=20&level=warn`, { headers }).catch(
|
|
1947
|
+
() => null
|
|
1948
|
+
) : Promise.resolve(null)
|
|
1883
1949
|
]);
|
|
1884
1950
|
const report = reportRes.status === "fulfilled" ? reportRes.value : null;
|
|
1885
1951
|
const severity = report ? report.severity : "unknown";
|
|
@@ -1946,7 +2012,10 @@ function createMushiServer(config) {
|
|
|
1946
2012
|
return {
|
|
1947
2013
|
...res,
|
|
1948
2014
|
resource_links: [
|
|
1949
|
-
{
|
|
2015
|
+
{
|
|
2016
|
+
uri: `project://reports/${args.report_id}`,
|
|
2017
|
+
title: `Report ${args.report_id.slice(0, 8)}\u2026`
|
|
2018
|
+
},
|
|
1950
2019
|
...pid ? [{ uri: `project://dashboard`, title: "Project dashboard" }] : []
|
|
1951
2020
|
]
|
|
1952
2021
|
};
|
|
@@ -1965,7 +2034,9 @@ function createMushiServer(config) {
|
|
|
1965
2034
|
filesChanged: z.array(z.string()).describe("Repo-relative paths modified by the fix"),
|
|
1966
2035
|
linesChanged: z.number().describe("Total lines added + removed across filesChanged"),
|
|
1967
2036
|
summary: z.string().describe("One-line human summary of what the fix changed"),
|
|
1968
|
-
idempotencyKey: z.string().uuid().optional().describe(
|
|
2037
|
+
idempotencyKey: z.string().uuid().optional().describe(
|
|
2038
|
+
"Optional UUID \u2014 resend the same key to safely retry without creating duplicate fix rows"
|
|
2039
|
+
)
|
|
1969
2040
|
},
|
|
1970
2041
|
outputSchema: {
|
|
1971
2042
|
ok: z.boolean().describe("True when the fix_attempt row was created and marked completed"),
|
|
@@ -2003,8 +2074,12 @@ function createMushiServer(config) {
|
|
|
2003
2074
|
inputSchema: {
|
|
2004
2075
|
reportId: z.string().uuid().describe("Report UUID to fix"),
|
|
2005
2076
|
agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
|
|
2006
|
-
idempotencyKey: z.string().uuid().optional().describe(
|
|
2007
|
-
|
|
2077
|
+
idempotencyKey: z.string().uuid().optional().describe(
|
|
2078
|
+
"Optional RFC 4122 UUID. Resend the same key to safely retry without dispatching a duplicate fix job (Idempotency-Key IETF draft)."
|
|
2079
|
+
),
|
|
2080
|
+
inventoryActionNodeId: z.string().uuid().optional().describe(
|
|
2081
|
+
"Optional inventory Action node UUID for spec-traceability (\xA72.10). When provided, the fix-worker embeds the expected_outcome contract in the LLM prompt and runs validateAgainstSpec before opening the PR."
|
|
2082
|
+
)
|
|
2008
2083
|
},
|
|
2009
2084
|
outputSchema: {
|
|
2010
2085
|
fixId: z.string(),
|
|
@@ -2077,11 +2152,19 @@ function createMushiServer(config) {
|
|
|
2077
2152
|
},
|
|
2078
2153
|
async (args) => {
|
|
2079
2154
|
const pid = args.projectId ?? projectId;
|
|
2080
|
-
if (!pid)
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2155
|
+
if (!pid)
|
|
2156
|
+
throw new MushiApiError(
|
|
2157
|
+
400,
|
|
2158
|
+
"MISSING_PROJECT",
|
|
2159
|
+
"projectId is required for test_gen_from_report"
|
|
2160
|
+
);
|
|
2161
|
+
const data = await apiCall(
|
|
2162
|
+
`/v1/admin/inventory/${pid}/test-gen/from-report/${args.reportId}`,
|
|
2163
|
+
{
|
|
2164
|
+
method: "POST",
|
|
2165
|
+
body: JSON.stringify({})
|
|
2166
|
+
}
|
|
2167
|
+
);
|
|
2085
2168
|
return jsonText(data);
|
|
2086
2169
|
}
|
|
2087
2170
|
);
|
|
@@ -2160,7 +2243,17 @@ function createMushiServer(config) {
|
|
|
2160
2243
|
annotations: annotationsFor("transition_status"),
|
|
2161
2244
|
inputSchema: {
|
|
2162
2245
|
reportId: z.string().describe("Report UUID"),
|
|
2163
|
-
status: z.enum([
|
|
2246
|
+
status: z.enum([
|
|
2247
|
+
"pending",
|
|
2248
|
+
"classified",
|
|
2249
|
+
"grouped",
|
|
2250
|
+
"fixing",
|
|
2251
|
+
"fixed",
|
|
2252
|
+
"resolved",
|
|
2253
|
+
"verified",
|
|
2254
|
+
"reopened",
|
|
2255
|
+
"dismissed"
|
|
2256
|
+
]).describe("Target status"),
|
|
2164
2257
|
reason: z.string().optional().describe("Reason for the transition (audit trail)")
|
|
2165
2258
|
}
|
|
2166
2259
|
},
|
|
@@ -2184,7 +2277,12 @@ function createMushiServer(config) {
|
|
|
2184
2277
|
},
|
|
2185
2278
|
async (args) => {
|
|
2186
2279
|
const pid = args.projectId ?? projectId;
|
|
2187
|
-
if (!pid)
|
|
2280
|
+
if (!pid)
|
|
2281
|
+
throw new MushiApiError(
|
|
2282
|
+
400,
|
|
2283
|
+
"MISSING_PROJECT",
|
|
2284
|
+
"projectId is required for setup_repo_for_mushi"
|
|
2285
|
+
);
|
|
2188
2286
|
const data = await apiCall(`/v1/admin/projects/${pid}/repo/bootstrap`, {
|
|
2189
2287
|
method: "POST",
|
|
2190
2288
|
body: JSON.stringify({})
|
|
@@ -2249,23 +2347,45 @@ function createMushiServer(config) {
|
|
|
2249
2347
|
"project://stats",
|
|
2250
2348
|
{ description: "Report counts, category breakdown, severity distribution" },
|
|
2251
2349
|
async () => ({
|
|
2252
|
-
contents: [
|
|
2350
|
+
contents: [
|
|
2351
|
+
{
|
|
2352
|
+
uri: "project://stats",
|
|
2353
|
+
mimeType: "application/json",
|
|
2354
|
+
text: JSON.stringify(await apiCall("/v1/admin/stats"), null, 2)
|
|
2355
|
+
}
|
|
2356
|
+
]
|
|
2253
2357
|
})
|
|
2254
2358
|
);
|
|
2255
2359
|
server.resource(
|
|
2256
2360
|
"project_settings",
|
|
2257
2361
|
"project://settings",
|
|
2258
|
-
{
|
|
2362
|
+
{
|
|
2363
|
+
description: "Project configuration \u2014 autofix agent, plugins enabled, ontology, LLM budgets"
|
|
2364
|
+
},
|
|
2259
2365
|
async () => ({
|
|
2260
|
-
contents: [
|
|
2366
|
+
contents: [
|
|
2367
|
+
{
|
|
2368
|
+
uri: "project://settings",
|
|
2369
|
+
mimeType: "application/json",
|
|
2370
|
+
text: JSON.stringify(await apiCall("/v1/admin/settings"), null, 2)
|
|
2371
|
+
}
|
|
2372
|
+
]
|
|
2261
2373
|
})
|
|
2262
2374
|
);
|
|
2263
2375
|
server.resource(
|
|
2264
2376
|
"project_dashboard",
|
|
2265
2377
|
"project://dashboard",
|
|
2266
|
-
{
|
|
2378
|
+
{
|
|
2379
|
+
description: "PDCA health snapshot \u2014 stage counts, bottleneck, recent activity (same payload the admin console polls)"
|
|
2380
|
+
},
|
|
2267
2381
|
async () => ({
|
|
2268
|
-
contents: [
|
|
2382
|
+
contents: [
|
|
2383
|
+
{
|
|
2384
|
+
uri: "project://dashboard",
|
|
2385
|
+
mimeType: "application/json",
|
|
2386
|
+
text: JSON.stringify(await apiCall("/v1/admin/dashboard"), null, 2)
|
|
2387
|
+
}
|
|
2388
|
+
]
|
|
2269
2389
|
})
|
|
2270
2390
|
);
|
|
2271
2391
|
server.registerTool(
|
|
@@ -2280,14 +2400,16 @@ function createMushiServer(config) {
|
|
|
2280
2400
|
}
|
|
2281
2401
|
},
|
|
2282
2402
|
async ({ limit, range }) => ({
|
|
2283
|
-
content: [
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2403
|
+
content: [
|
|
2404
|
+
{
|
|
2405
|
+
type: "text",
|
|
2406
|
+
text: JSON.stringify(
|
|
2407
|
+
await apiCall(`/v1/admin/rewards/leaderboard?range=${range}&limit=${limit}`),
|
|
2408
|
+
null,
|
|
2409
|
+
2
|
|
2410
|
+
)
|
|
2411
|
+
}
|
|
2412
|
+
]
|
|
2291
2413
|
})
|
|
2292
2414
|
);
|
|
2293
2415
|
server.registerTool(
|
|
@@ -2303,18 +2425,20 @@ function createMushiServer(config) {
|
|
|
2303
2425
|
}
|
|
2304
2426
|
},
|
|
2305
2427
|
async ({ external_user_id, points, reason }) => ({
|
|
2306
|
-
content: [
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2428
|
+
content: [
|
|
2429
|
+
{
|
|
2430
|
+
type: "text",
|
|
2431
|
+
text: JSON.stringify(
|
|
2432
|
+
await apiCall("/v1/admin/rewards/bonus-points", {
|
|
2433
|
+
method: "POST",
|
|
2434
|
+
headers: { "Content-Type": "application/json" },
|
|
2435
|
+
body: JSON.stringify({ external_user_id, points, reason })
|
|
2436
|
+
}),
|
|
2437
|
+
null,
|
|
2438
|
+
2
|
|
2439
|
+
)
|
|
2440
|
+
}
|
|
2441
|
+
]
|
|
2318
2442
|
})
|
|
2319
2443
|
);
|
|
2320
2444
|
server.registerTool(
|
|
@@ -2330,18 +2454,20 @@ function createMushiServer(config) {
|
|
|
2330
2454
|
}
|
|
2331
2455
|
},
|
|
2332
2456
|
async ({ external_user_id, tier_slug, reason }) => ({
|
|
2333
|
-
content: [
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2457
|
+
content: [
|
|
2458
|
+
{
|
|
2459
|
+
type: "text",
|
|
2460
|
+
text: JSON.stringify(
|
|
2461
|
+
await apiCall("/v1/admin/rewards/set-tier", {
|
|
2462
|
+
method: "POST",
|
|
2463
|
+
headers: { "Content-Type": "application/json" },
|
|
2464
|
+
body: JSON.stringify({ external_user_id, tier_slug, reason })
|
|
2465
|
+
}),
|
|
2466
|
+
null,
|
|
2467
|
+
2
|
|
2468
|
+
)
|
|
2469
|
+
}
|
|
2470
|
+
]
|
|
2345
2471
|
})
|
|
2346
2472
|
);
|
|
2347
2473
|
server.resource(
|
|
@@ -2351,11 +2477,13 @@ function createMushiServer(config) {
|
|
|
2351
2477
|
description: "Returns the privacy posture for this project: storage region, LLM provider, whether BYOK is configured, data retention window, and last audit timestamp."
|
|
2352
2478
|
},
|
|
2353
2479
|
async () => ({
|
|
2354
|
-
contents: [
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2480
|
+
contents: [
|
|
2481
|
+
{
|
|
2482
|
+
uri: "privacy://status",
|
|
2483
|
+
mimeType: "application/json",
|
|
2484
|
+
text: JSON.stringify(await apiCall("/v1/admin/privacy-status"), null, 2)
|
|
2485
|
+
}
|
|
2486
|
+
]
|
|
2359
2487
|
})
|
|
2360
2488
|
);
|
|
2361
2489
|
server.resource(
|
|
@@ -2365,15 +2493,19 @@ function createMushiServer(config) {
|
|
|
2365
2493
|
description: "Returns the project's last 30 days of judge scores, prompt promotions, fixed-bug count, and lesson inductions. Agents can read this to see whether the loop is converging (rising judge scores, falling recurrence) or stalling."
|
|
2366
2494
|
},
|
|
2367
2495
|
async () => ({
|
|
2368
|
-
contents: [
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2496
|
+
contents: [
|
|
2497
|
+
{
|
|
2498
|
+
uri: "evolution://history",
|
|
2499
|
+
mimeType: "application/json",
|
|
2500
|
+
text: projectId ? JSON.stringify(
|
|
2501
|
+
await apiCall(
|
|
2502
|
+
`/v1/admin/projects/${encodeURIComponent(projectId)}/evolution-history`
|
|
2503
|
+
),
|
|
2504
|
+
null,
|
|
2505
|
+
2
|
|
2506
|
+
) : JSON.stringify({ ok: false, error: "project_id required" })
|
|
2507
|
+
}
|
|
2508
|
+
]
|
|
2377
2509
|
})
|
|
2378
2510
|
);
|
|
2379
2511
|
server.resource(
|
|
@@ -2383,11 +2515,13 @@ function createMushiServer(config) {
|
|
|
2383
2515
|
description: "Live health status of every configured BYOK channel (Sentry, GitHub, LangFuse, PagerDuty, \u2026). Orchestrators should check this before dispatching a fix to fail-fast on broken channels rather than burning LLM budget and discovering the failure mid-run."
|
|
2384
2516
|
},
|
|
2385
2517
|
async () => ({
|
|
2386
|
-
contents: [
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2518
|
+
contents: [
|
|
2519
|
+
{
|
|
2520
|
+
uri: "project://integration-health",
|
|
2521
|
+
mimeType: "application/json",
|
|
2522
|
+
text: JSON.stringify(await apiCall("/v1/admin/integrations/health"), null, 2)
|
|
2523
|
+
}
|
|
2524
|
+
]
|
|
2391
2525
|
})
|
|
2392
2526
|
);
|
|
2393
2527
|
server.resource(
|
|
@@ -2399,11 +2533,13 @@ function createMushiServer(config) {
|
|
|
2399
2533
|
async () => {
|
|
2400
2534
|
const path = projectId ? `/v1/admin/inventory/${projectId}` : "/v1/admin/inventory";
|
|
2401
2535
|
return {
|
|
2402
|
-
contents: [
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2536
|
+
contents: [
|
|
2537
|
+
{
|
|
2538
|
+
uri: "inventory://current",
|
|
2539
|
+
mimeType: "application/json",
|
|
2540
|
+
text: JSON.stringify(await apiCall(path), null, 2)
|
|
2541
|
+
}
|
|
2542
|
+
]
|
|
2407
2543
|
};
|
|
2408
2544
|
}
|
|
2409
2545
|
);
|
|
@@ -2432,11 +2568,13 @@ function createMushiServer(config) {
|
|
|
2432
2568
|
async () => {
|
|
2433
2569
|
const qs = projectId ? `?project_id=${encodeURIComponent(projectId)}` : "";
|
|
2434
2570
|
return {
|
|
2435
|
-
contents: [
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2571
|
+
contents: [
|
|
2572
|
+
{
|
|
2573
|
+
uri: "mushi://activation",
|
|
2574
|
+
mimeType: "application/json",
|
|
2575
|
+
text: JSON.stringify(await apiCall(`/v1/admin/activation${qs}`), null, 2)
|
|
2576
|
+
}
|
|
2577
|
+
]
|
|
2440
2578
|
};
|
|
2441
2579
|
}
|
|
2442
2580
|
);
|
|
@@ -2445,11 +2583,12 @@ function createMushiServer(config) {
|
|
|
2445
2583
|
"Turn a Mushi report into a one-line root cause, smallest file set, repro steps, and blast-radius warnings. Use before asking an agent to write the patch.",
|
|
2446
2584
|
{ reportId: z.string().describe("The report UUID to summarize") },
|
|
2447
2585
|
({ reportId }) => ({
|
|
2448
|
-
messages: [
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2586
|
+
messages: [
|
|
2587
|
+
{
|
|
2588
|
+
role: "user",
|
|
2589
|
+
content: {
|
|
2590
|
+
type: "text",
|
|
2591
|
+
text: `You are a senior engineer preparing a fix plan. Use the Mushi MCP tools to:
|
|
2453
2592
|
1. Call get_fix_context for reportId "${reportId}".
|
|
2454
2593
|
2. Call get_blast_radius if the report has a component node id.
|
|
2455
2594
|
3. Call get_similar_bugs with the component or summary as the query.
|
|
@@ -2462,8 +2601,9 @@ Then produce a markdown fix plan with exactly these sections:
|
|
|
2462
2601
|
- Confidence (low/medium/high) with a one-line justification
|
|
2463
2602
|
|
|
2464
2603
|
Lead with the fix. Skip the preamble.`
|
|
2604
|
+
}
|
|
2465
2605
|
}
|
|
2466
|
-
|
|
2606
|
+
]
|
|
2467
2607
|
})
|
|
2468
2608
|
);
|
|
2469
2609
|
server.prompt(
|
|
@@ -2471,11 +2611,12 @@ Lead with the fix. Skip the preamble.`
|
|
|
2471
2611
|
"Turn raw Sonnet-as-Judge scores into ship / iterate / dismiss guidance. Use after a fix attempt has been judged.",
|
|
2472
2612
|
{ fixId: z.string().describe("The fix_attempt UUID to explain") },
|
|
2473
2613
|
({ fixId }) => ({
|
|
2474
|
-
messages: [
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2614
|
+
messages: [
|
|
2615
|
+
{
|
|
2616
|
+
role: "user",
|
|
2617
|
+
content: {
|
|
2618
|
+
type: "text",
|
|
2619
|
+
text: `You are a release engineer. Use the Mushi MCP tools:
|
|
2479
2620
|
1. Call get_fix_timeline for fixId "${fixId}" to see the full PDCA journey.
|
|
2480
2621
|
|
|
2481
2622
|
Then write a short verdict in this format:
|
|
@@ -2484,8 +2625,9 @@ Then write a short verdict in this format:
|
|
|
2484
2625
|
- **If iterate:** bullet list of the smallest next patch
|
|
2485
2626
|
|
|
2486
2627
|
No preamble, no score-by-score recap \u2014 the numbers are in the tool output.`
|
|
2628
|
+
}
|
|
2487
2629
|
}
|
|
2488
|
-
|
|
2630
|
+
]
|
|
2489
2631
|
})
|
|
2490
2632
|
);
|
|
2491
2633
|
server.prompt(
|
|
@@ -2493,11 +2635,12 @@ No preamble, no score-by-score recap \u2014 the numbers are in the tool output.`
|
|
|
2493
2635
|
'Answer "what should I focus on right now?" \u2014 five-item markdown list drawn from the dashboard + recent classified queue.',
|
|
2494
2636
|
{},
|
|
2495
2637
|
() => ({
|
|
2496
|
-
messages: [
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2638
|
+
messages: [
|
|
2639
|
+
{
|
|
2640
|
+
role: "user",
|
|
2641
|
+
content: {
|
|
2642
|
+
type: "text",
|
|
2643
|
+
text: `You are a tech lead looking at the Mushi cockpit. Use the MCP tools:
|
|
2501
2644
|
1. Read the project://dashboard resource for the PDCA snapshot.
|
|
2502
2645
|
2. Call get_recent_reports with status="classified", limit=10.
|
|
2503
2646
|
|
|
@@ -2505,8 +2648,9 @@ Then output exactly 5 bullets, in priority order, each formatted as:
|
|
|
2505
2648
|
\`**Action** \u2014 why it matters \u2014 suggested tool call\`
|
|
2506
2649
|
|
|
2507
2650
|
Prefer items that are bottlenecks or critical severity. Skip filler.`
|
|
2651
|
+
}
|
|
2508
2652
|
}
|
|
2509
|
-
|
|
2653
|
+
]
|
|
2510
2654
|
})
|
|
2511
2655
|
);
|
|
2512
2656
|
server.prompt(
|
|
@@ -2514,11 +2658,12 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
|
|
|
2514
2658
|
"Diagnose why Mushi setup is stuck and return the single next command or console step to unblock it.",
|
|
2515
2659
|
{},
|
|
2516
2660
|
() => ({
|
|
2517
|
-
messages: [
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2661
|
+
messages: [
|
|
2662
|
+
{
|
|
2663
|
+
role: "user",
|
|
2664
|
+
content: {
|
|
2665
|
+
type: "text",
|
|
2666
|
+
text: `You are a Mushi onboarding copilot. Use the MCP tools:
|
|
2522
2667
|
1. Read mushi://activation for unified setup posture.
|
|
2523
2668
|
2. Read project://integration-health if GitHub or Sentry looks blocked.
|
|
2524
2669
|
3. Call diagnose_setup if the resource is unavailable.
|
|
@@ -2530,8 +2675,9 @@ Then output:
|
|
|
2530
2675
|
- **If still stuck:** one diagnostic command (e.g. mushi doctor --fix)
|
|
2531
2676
|
|
|
2532
2677
|
Be specific. No generic advice.`
|
|
2678
|
+
}
|
|
2533
2679
|
}
|
|
2534
|
-
|
|
2680
|
+
]
|
|
2535
2681
|
})
|
|
2536
2682
|
);
|
|
2537
2683
|
server.registerTool(
|
|
@@ -2552,7 +2698,15 @@ Be specific. No generic advice.`
|
|
|
2552
2698
|
if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2553
2699
|
const data = await apiCall(
|
|
2554
2700
|
`/v1/admin/inventory/${projectId2}/map-from-live`,
|
|
2555
|
-
{
|
|
2701
|
+
{
|
|
2702
|
+
method: "POST",
|
|
2703
|
+
body: JSON.stringify({
|
|
2704
|
+
base_url: baseUrl,
|
|
2705
|
+
max_pages: maxPages,
|
|
2706
|
+
provider,
|
|
2707
|
+
cursor_cloud_refine: cursorCloudRefine
|
|
2708
|
+
})
|
|
2709
|
+
}
|
|
2556
2710
|
);
|
|
2557
2711
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2558
2712
|
}
|
|
@@ -2580,7 +2734,9 @@ Be specific. No generic advice.`
|
|
|
2580
2734
|
annotations: annotationsFor("generate_tdd_from_story", TDD_TOOL_CATALOG),
|
|
2581
2735
|
inputSchema: {
|
|
2582
2736
|
projectId: z.string().describe("Project UUID that owns the inventory story"),
|
|
2583
|
-
storyNodeId: z.string().describe(
|
|
2737
|
+
storyNodeId: z.string().describe(
|
|
2738
|
+
"User story node id/slug from accepted inventory (from map_user_stories or inventory.yaml)"
|
|
2739
|
+
),
|
|
2584
2740
|
automationMode: z.enum(["auto", "review", "approve"]).optional().describe("Approval gate for the generated qa_story (default review \u2192 pending_review)"),
|
|
2585
2741
|
baseUrl: z.string().url().optional().describe("Override the live app URL the Playwright test should target"),
|
|
2586
2742
|
openPr: z.boolean().optional().describe("Open a draft GitHub PR with the generated spec (default true)")
|
|
@@ -2594,10 +2750,14 @@ Be specific. No generic advice.`
|
|
|
2594
2750
|
},
|
|
2595
2751
|
async ({ projectId: projectId2, storyNodeId, automationMode, baseUrl, openPr }) => {
|
|
2596
2752
|
if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
|
|
2597
|
-
const data = await apiCall(
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2753
|
+
const data = await apiCall(`/v1/admin/inventory/${projectId2}/stories/${storyNodeId}/generate-test`, {
|
|
2754
|
+
method: "POST",
|
|
2755
|
+
body: JSON.stringify({
|
|
2756
|
+
automation_mode: automationMode,
|
|
2757
|
+
base_url: baseUrl,
|
|
2758
|
+
open_pr: openPr
|
|
2759
|
+
})
|
|
2760
|
+
});
|
|
2601
2761
|
return jsonResult(data);
|
|
2602
2762
|
}
|
|
2603
2763
|
);
|
|
@@ -2612,10 +2772,10 @@ Be specific. No generic advice.`
|
|
|
2612
2772
|
}
|
|
2613
2773
|
},
|
|
2614
2774
|
async ({ projectId: projectId2 }) => {
|
|
2615
|
-
const data = await apiCall(
|
|
2616
|
-
"
|
|
2617
|
-
|
|
2618
|
-
);
|
|
2775
|
+
const data = await apiCall("/v1/admin/pdca/improve-qa-stories", {
|
|
2776
|
+
method: "POST",
|
|
2777
|
+
body: JSON.stringify({ project_id: projectId2 })
|
|
2778
|
+
});
|
|
2619
2779
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2620
2780
|
}
|
|
2621
2781
|
);
|
|
@@ -2649,7 +2809,9 @@ Be specific. No generic advice.`
|
|
|
2649
2809
|
}
|
|
2650
2810
|
},
|
|
2651
2811
|
async ({ projectId: projectId2 }) => {
|
|
2652
|
-
const data = await apiCall(
|
|
2812
|
+
const data = await apiCall(
|
|
2813
|
+
`/v1/admin/byok/keys?project_id=${encodeURIComponent(projectId2)}`
|
|
2814
|
+
);
|
|
2653
2815
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2654
2816
|
}
|
|
2655
2817
|
);
|
|
@@ -2662,16 +2824,60 @@ Be specific. No generic advice.`
|
|
|
2662
2824
|
inputSchema: {
|
|
2663
2825
|
projectId: z.string().describe("Project id"),
|
|
2664
2826
|
provider: z.enum(["anthropic", "openai", "firecrawl", "browserbase", "cursor"]).describe("Provider slug"),
|
|
2665
|
-
key: z.string().min(
|
|
2827
|
+
key: z.string().min(8).max(4096).describe("The API key value to add"),
|
|
2666
2828
|
label: z.string().optional().describe("Human-readable label for this key"),
|
|
2667
|
-
priority: z.number().int().min(
|
|
2829
|
+
priority: z.number().int().min(0).max(1e4).optional().describe("Priority for ordering (lower = higher priority)"),
|
|
2830
|
+
baseUrl: z.string().url().max(2048).optional().describe("Allow-listed OpenAI-compatible HTTPS base URL")
|
|
2668
2831
|
}
|
|
2669
2832
|
},
|
|
2670
|
-
async ({ projectId: projectId2, provider, key, label, priority }) => {
|
|
2671
|
-
const data = await apiCall(
|
|
2672
|
-
"
|
|
2673
|
-
|
|
2674
|
-
|
|
2833
|
+
async ({ projectId: projectId2, provider, key, label, priority, baseUrl }) => {
|
|
2834
|
+
const data = await apiCall("/v1/admin/byok/keys", {
|
|
2835
|
+
method: "POST",
|
|
2836
|
+
body: JSON.stringify({
|
|
2837
|
+
project_id: projectId2,
|
|
2838
|
+
provider_slug: provider,
|
|
2839
|
+
key,
|
|
2840
|
+
label,
|
|
2841
|
+
priority,
|
|
2842
|
+
base_url: baseUrl
|
|
2843
|
+
})
|
|
2844
|
+
});
|
|
2845
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2846
|
+
}
|
|
2847
|
+
);
|
|
2848
|
+
server.registerTool(
|
|
2849
|
+
"test_byok_key",
|
|
2850
|
+
{
|
|
2851
|
+
title: titleOf("test_byok_key", TDD_TOOL_CATALOG),
|
|
2852
|
+
description: descOf("test_byok_key", TDD_TOOL_CATALOG),
|
|
2853
|
+
annotations: annotationsFor("test_byok_key", TDD_TOOL_CATALOG),
|
|
2854
|
+
inputSchema: {
|
|
2855
|
+
projectId: z.string().describe("Project id"),
|
|
2856
|
+
keyId: z.string().uuid().describe("Pooled BYOK key id")
|
|
2857
|
+
}
|
|
2858
|
+
},
|
|
2859
|
+
async ({ keyId }) => {
|
|
2860
|
+
const data = await apiCall(`/v1/admin/byok/keys/${encodeURIComponent(keyId)}/test`, {
|
|
2861
|
+
method: "POST"
|
|
2862
|
+
});
|
|
2863
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2864
|
+
}
|
|
2865
|
+
);
|
|
2866
|
+
server.registerTool(
|
|
2867
|
+
"remove_byok_key",
|
|
2868
|
+
{
|
|
2869
|
+
title: titleOf("remove_byok_key", TDD_TOOL_CATALOG),
|
|
2870
|
+
description: descOf("remove_byok_key", TDD_TOOL_CATALOG),
|
|
2871
|
+
annotations: annotationsFor("remove_byok_key", TDD_TOOL_CATALOG),
|
|
2872
|
+
inputSchema: {
|
|
2873
|
+
projectId: z.string().describe("Project id"),
|
|
2874
|
+
keyId: z.string().uuid().describe("Pooled BYOK key UUID from list_byok_keys")
|
|
2875
|
+
}
|
|
2876
|
+
},
|
|
2877
|
+
async ({ keyId }) => {
|
|
2878
|
+
const data = await apiCall(`/v1/admin/byok/keys/${encodeURIComponent(keyId)}`, {
|
|
2879
|
+
method: "DELETE"
|
|
2880
|
+
});
|
|
2675
2881
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2676
2882
|
}
|
|
2677
2883
|
);
|
|
@@ -2686,7 +2892,9 @@ Be specific. No generic advice.`
|
|
|
2686
2892
|
}
|
|
2687
2893
|
},
|
|
2688
2894
|
async ({ projectId: projectId2 }) => {
|
|
2689
|
-
const data = await apiCall(
|
|
2895
|
+
const data = await apiCall(
|
|
2896
|
+
`/v1/admin/inventory/${projectId2}/stories/pending-review`
|
|
2897
|
+
);
|
|
2690
2898
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2691
2899
|
}
|
|
2692
2900
|
);
|
|
@@ -2723,13 +2931,10 @@ Be specific. No generic advice.`
|
|
|
2723
2931
|
}
|
|
2724
2932
|
},
|
|
2725
2933
|
async ({ reportId, message, authorName }) => {
|
|
2726
|
-
const data = await apiCall(
|
|
2727
|
-
|
|
2728
|
-
{
|
|
2729
|
-
|
|
2730
|
-
body: JSON.stringify({ message, author_name: authorName })
|
|
2731
|
-
}
|
|
2732
|
-
);
|
|
2934
|
+
const data = await apiCall(`/v1/sync/reports/${reportId}/reply`, {
|
|
2935
|
+
method: "POST",
|
|
2936
|
+
body: JSON.stringify({ message, author_name: authorName })
|
|
2937
|
+
});
|
|
2733
2938
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2734
2939
|
}
|
|
2735
2940
|
);
|
|
@@ -2808,10 +3013,10 @@ Be specific. No generic advice.`
|
|
|
2808
3013
|
async ({ project_id }) => {
|
|
2809
3014
|
const pid = project_id ?? config.projectId;
|
|
2810
3015
|
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "project_id is required");
|
|
2811
|
-
const data = await apiCall(
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
);
|
|
3016
|
+
const data = await apiCall(`/v1/admin/projects/${pid}/audit`, {
|
|
3017
|
+
method: "POST",
|
|
3018
|
+
body: "{}"
|
|
3019
|
+
});
|
|
2815
3020
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
2816
3021
|
}
|
|
2817
3022
|
);
|
|
@@ -2907,17 +3112,19 @@ Be specific. No generic advice.`
|
|
|
2907
3112
|
inputSchema: {
|
|
2908
3113
|
root_skill_slug: z.string().describe('Root skill slug to run, e.g. "workflow-fix-and-ship"'),
|
|
2909
3114
|
report_id: z.string().optional().describe("Report UUID to attach the pipeline to"),
|
|
2910
|
-
mode: z.enum(["handoff", "cloud"]).optional().describe(
|
|
3115
|
+
mode: z.enum(["handoff", "cloud"]).optional().describe(
|
|
3116
|
+
"handoff (default): get context packet for local agent. cloud: auto-dispatch via Cursor Cloud."
|
|
3117
|
+
),
|
|
2911
3118
|
project_id: z.string().optional().describe("Project UUID. Falls back to the configured project.")
|
|
2912
3119
|
}
|
|
2913
3120
|
},
|
|
2914
3121
|
async (args) => {
|
|
2915
3122
|
const resolvedProjectId = args.project_id ?? projectId;
|
|
2916
3123
|
if (!resolvedProjectId) return jsonText({ error: "No project_id provided or configured." });
|
|
2917
|
-
const data = await apiCall(
|
|
2918
|
-
"
|
|
2919
|
-
|
|
2920
|
-
);
|
|
3124
|
+
const data = await apiCall("/v1/admin/skills/pipelines", {
|
|
3125
|
+
method: "POST",
|
|
3126
|
+
body: JSON.stringify({ ...args, project_id: resolvedProjectId })
|
|
3127
|
+
});
|
|
2921
3128
|
return jsonText(data);
|
|
2922
3129
|
}
|
|
2923
3130
|
);
|
|
@@ -2953,10 +3160,10 @@ Be specific. No generic advice.`
|
|
|
2953
3160
|
},
|
|
2954
3161
|
async (args) => {
|
|
2955
3162
|
const { run_id, step_index, ...body } = args;
|
|
2956
|
-
await apiCall(
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
);
|
|
3163
|
+
await apiCall(`/v1/admin/skills/pipelines/${run_id}/steps/${step_index}/checkin`, {
|
|
3164
|
+
method: "POST",
|
|
3165
|
+
body: JSON.stringify(body)
|
|
3166
|
+
});
|
|
2960
3167
|
return jsonText({ ok: true, message: `Step ${step_index} \u2192 ${args.status}` });
|
|
2961
3168
|
}
|
|
2962
3169
|
);
|
|
@@ -3124,7 +3331,11 @@ Be specific. No generic advice.`
|
|
|
3124
3331
|
if (args.compare) qs.set("compare", args.compare);
|
|
3125
3332
|
if (args.fix_id) qs.set("fix_id", args.fix_id);
|
|
3126
3333
|
if (!qs.toString()) {
|
|
3127
|
-
throw new MushiApiError(
|
|
3334
|
+
throw new MushiApiError(
|
|
3335
|
+
400,
|
|
3336
|
+
"BAD_REQUEST",
|
|
3337
|
+
"Provide paths, source=last_push, compare, or fix_id"
|
|
3338
|
+
);
|
|
3128
3339
|
}
|
|
3129
3340
|
const data = await apiCall(`/v1/admin/projects/${pid}/codebase/impact?${qs}`);
|
|
3130
3341
|
return jsonResult(data);
|
|
@@ -3176,7 +3387,7 @@ Be specific. No generic advice.`
|
|
|
3176
3387
|
"### First step",
|
|
3177
3388
|
`Call \`${cluster.tools[0]}\` to get started.`,
|
|
3178
3389
|
"",
|
|
3179
|
-
"> Tip: call any tool by name \u2014 `use_mushi` is a read-only helper that never calls other tools itself. All
|
|
3390
|
+
"> Tip: call any tool by name \u2014 `use_mushi` is a read-only helper that never calls other tools itself. All 71 tools remain available."
|
|
3180
3391
|
].join("\n");
|
|
3181
3392
|
return { content: [{ type: "text", text: orientation }] };
|
|
3182
3393
|
}
|
|
@@ -3185,11 +3396,7 @@ Be specific. No generic advice.`
|
|
|
3185
3396
|
const featureFilter = config.features ?? "all";
|
|
3186
3397
|
if (grantedScopes !== void 0 || featureFilter !== "all") {
|
|
3187
3398
|
const toolRegistry = server._registeredTools;
|
|
3188
|
-
const allSpecs = [
|
|
3189
|
-
...TOOL_CATALOG,
|
|
3190
|
-
...TDD_TOOL_CATALOG,
|
|
3191
|
-
...CODEBASE_TOOL_CATALOG
|
|
3192
|
-
];
|
|
3399
|
+
const allSpecs = [...TOOL_CATALOG, ...TDD_TOOL_CATALOG, ...CODEBASE_TOOL_CATALOG];
|
|
3193
3400
|
for (const spec of allSpecs) {
|
|
3194
3401
|
if (grantedScopes !== void 0 && !grantedScopes.includes(spec.scope)) {
|
|
3195
3402
|
toolRegistry[spec.name]?.remove();
|