@mushi-mushi/mcp 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +292 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -305,6 +305,88 @@ var TDD_TOOL_CATALOG = [
|
|
|
305
305
|
scope: "mcp:write",
|
|
306
306
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: false },
|
|
307
307
|
useCase: "Reply to a reporter asking for more info or confirming a fix."
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: "list_qa_story_runs",
|
|
311
|
+
title: "Recent QA story runs",
|
|
312
|
+
description: "Return the most recent runs for a given QA story, newest first. Each row includes status (passed/failed/error/running), latency_ms, created_at, error_message headline, and assertion_failures (up to 10). Use this to understand whether a story is currently healthy, what the last failure was, and whether a manual run has completed.",
|
|
313
|
+
scope: "mcp:read",
|
|
314
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
315
|
+
useCase: 'Show me the last 10 runs for the "home page loads" story and why they failed.'
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: "get_qa_story_run",
|
|
319
|
+
title: "QA story run detail",
|
|
320
|
+
description: "Fetch the full detail for a single qa_story_run: status, error_message, assertion_failures, latency_ms, provider_session_url (Browserbase replay link when available), and screenshot URLs from qa_story_evidence. Use this to drill into a specific failing run and understand the exact error before deciding whether to improve the story script or fix the app.",
|
|
321
|
+
scope: "mcp:read",
|
|
322
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
323
|
+
useCase: "What exactly happened in this failed QA run? Show me the error and screenshots."
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: "test_notification_channel",
|
|
327
|
+
title: "Send a test notification",
|
|
328
|
+
description: 'Send a test notification to a configured notification channel for the project. Supported channel kinds: "slack" (posts a test Block Kit message to the configured channel), "discord" (posts to the project discord_webhook_url). Returns ok=true if the message was accepted. Use this to verify the integration is working after setup or after changing credentials.',
|
|
329
|
+
scope: "mcp:write",
|
|
330
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
331
|
+
useCase: 'Did "Add to Slack" work? Send a test ping to confirm.'
|
|
332
|
+
},
|
|
333
|
+
// ── Skill Pipeline tools ─────────────────────────────────────────────────────
|
|
334
|
+
{
|
|
335
|
+
name: "list_skills",
|
|
336
|
+
title: "List agent skills",
|
|
337
|
+
description: "List the agent skills available in the catalog, optionally filtered by category. Returns slug, title, description, category, and chain_slugs for each skill. Use before start_skill_pipeline to find the right skill slug for a given type of work.",
|
|
338
|
+
scope: "mcp:read",
|
|
339
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
340
|
+
useCase: "What skills are available for debugging production errors?"
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "get_skill",
|
|
344
|
+
title: "Get skill detail",
|
|
345
|
+
description: "Fetch the full detail for a single agent skill by slug, including the complete SKILL.md body and resolved chain. Use before executing a step to understand what the skill expects you to do.",
|
|
346
|
+
scope: "mcp:read",
|
|
347
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
348
|
+
useCase: "What does workflow-fix-and-ship actually instruct me to do?"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: "start_skill_pipeline",
|
|
352
|
+
title: "Start a skill pipeline",
|
|
353
|
+
description: "Start a new skill pipeline run for a report. Pass root_skill_slug and optionally report_id. Returns run_id, context_packet (full instructions + report context), and step list. Read the context_packet \u2014 it contains skill instructions plus full report context (repro steps, root cause, RAG files). After executing each step, call checkin_pipeline_step. The PM watching the console sees progress live.",
|
|
354
|
+
scope: "mcp:write",
|
|
355
|
+
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
356
|
+
useCase: "Start the workflow-fix-and-ship pipeline for this bug report."
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: "get_pipeline_run",
|
|
360
|
+
title: "Get pipeline run detail",
|
|
361
|
+
description: "Fetch the full detail for a skill pipeline run: status, context_packet, and all step statuses. Call to retrieve the context_packet after a pipeline was started from the console or another agent.",
|
|
362
|
+
scope: "mcp:read",
|
|
363
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
364
|
+
useCase: "Give me the context packet and step status for this pipeline run."
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "checkin_pipeline_step",
|
|
368
|
+
title: "Check in a pipeline step",
|
|
369
|
+
description: "Report the completion status of a pipeline step (passed, failed, running, or skipped). Optionally include notes, a PR URL, or the Cursor agentId. Updates the live React Flow canvas in the Mushi console so PMs see real-time progress.",
|
|
370
|
+
scope: "mcp:write",
|
|
371
|
+
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
372
|
+
useCase: "I just opened the PR \u2014 mark step 2 as passed and link the PR."
|
|
373
|
+
},
|
|
374
|
+
// ── Full-Stack Audit tools (Phase 5) ────────────────────────────────────────
|
|
375
|
+
{
|
|
376
|
+
name: "run_fullstack_audit",
|
|
377
|
+
title: "Run a full-stack health audit",
|
|
378
|
+
description: "Fan out a full-stack health audit for the current project: DB schema + advisors, API contract gate results (Gates 3\u20138), recent backend error logs, and RLS gap detection. Returns a PM-readable scorecard with severity-ranked findings and fix hints. Requires the project to have a Supabase PAT configured (Settings \u2192 API Keys, slug: supabase) and supabase_project_ref set in project settings for backend analysis. The audit completes synchronously in ~10 s. Triggers a background gate run for orphan_endpoint and unknown_call gates if they have not run today.",
|
|
379
|
+
scope: "mcp:write",
|
|
380
|
+
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
381
|
+
useCase: "Is this project healthy? Run a one-click full-stack audit to find API contract mismatches, unlinked backend features, schema drift, and security gaps."
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
name: "get_backend_health",
|
|
385
|
+
title: "Get backend health for a project",
|
|
386
|
+
description: 'Return the current backend health state for a linked Supabase project: table list (with RLS enabled status), recent API and Postgres error logs, and DB advisor findings. Uses the read-only Supabase MCP client via the stored PAT. Returns { tables, logs, advisors, projectRef } when the backend is linked, or { reason: "no_supabase_pat" | "no_project_ref" } when it is not configured. This is the read-only fast path \u2014 use run_fullstack_audit for the full scorecard including gate results.',
|
|
387
|
+
scope: "mcp:read",
|
|
388
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
389
|
+
useCase: "Are there any tables without RLS? Show me recent backend errors for this project."
|
|
308
390
|
}
|
|
309
391
|
];
|
|
310
392
|
|
|
@@ -1318,6 +1400,216 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
|
|
|
1318
1400
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1319
1401
|
}
|
|
1320
1402
|
);
|
|
1403
|
+
server.registerTool(
|
|
1404
|
+
"list_qa_story_runs",
|
|
1405
|
+
{
|
|
1406
|
+
title: titleOf("list_qa_story_runs", TDD_TOOL_CATALOG),
|
|
1407
|
+
description: descOf("list_qa_story_runs", TDD_TOOL_CATALOG),
|
|
1408
|
+
annotations: annotationsFor("list_qa_story_runs", TDD_TOOL_CATALOG),
|
|
1409
|
+
inputSchema: {
|
|
1410
|
+
storyId: z.string().describe("QA story id (uuid)"),
|
|
1411
|
+
limit: z.number().int().min(1).max(50).optional().default(10).describe("Max runs to return (default 10)")
|
|
1412
|
+
}
|
|
1413
|
+
},
|
|
1414
|
+
async ({ storyId, limit }) => {
|
|
1415
|
+
const data = await apiCall(
|
|
1416
|
+
`/v1/admin/projects/${config.projectId}/qa-stories/${storyId}/runs?limit=${limit ?? 10}`
|
|
1417
|
+
);
|
|
1418
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1419
|
+
}
|
|
1420
|
+
);
|
|
1421
|
+
server.registerTool(
|
|
1422
|
+
"get_qa_story_run",
|
|
1423
|
+
{
|
|
1424
|
+
title: titleOf("get_qa_story_run", TDD_TOOL_CATALOG),
|
|
1425
|
+
description: descOf("get_qa_story_run", TDD_TOOL_CATALOG),
|
|
1426
|
+
annotations: annotationsFor("get_qa_story_run", TDD_TOOL_CATALOG),
|
|
1427
|
+
inputSchema: {
|
|
1428
|
+
storyId: z.string().describe("QA story id (uuid)"),
|
|
1429
|
+
runId: z.string().describe("Run id (uuid) to fetch detail for")
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
async ({ storyId, runId }) => {
|
|
1433
|
+
const data = await apiCall(
|
|
1434
|
+
`/v1/admin/projects/${config.projectId}/qa-stories/${storyId}/runs?limit=50`
|
|
1435
|
+
);
|
|
1436
|
+
const run = data?.data?.runs?.find((r) => r.id === runId) ?? null;
|
|
1437
|
+
if (!run) {
|
|
1438
|
+
throw new MushiApiError(
|
|
1439
|
+
404,
|
|
1440
|
+
"RUN_NOT_FOUND",
|
|
1441
|
+
`Run ${runId} not found in the 50 most recent runs for story ${storyId}`
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
return { content: [{ type: "text", text: JSON.stringify(run, null, 2) }] };
|
|
1445
|
+
}
|
|
1446
|
+
);
|
|
1447
|
+
server.registerTool(
|
|
1448
|
+
"test_notification_channel",
|
|
1449
|
+
{
|
|
1450
|
+
title: titleOf("test_notification_channel", TDD_TOOL_CATALOG),
|
|
1451
|
+
description: descOf("test_notification_channel", TDD_TOOL_CATALOG),
|
|
1452
|
+
annotations: annotationsFor("test_notification_channel", TDD_TOOL_CATALOG),
|
|
1453
|
+
inputSchema: {
|
|
1454
|
+
kind: z.enum(["slack", "discord"]).describe("Notification channel kind to test")
|
|
1455
|
+
}
|
|
1456
|
+
},
|
|
1457
|
+
async ({ kind }) => {
|
|
1458
|
+
const data = await apiCall(
|
|
1459
|
+
`/v1/admin/projects/${config.projectId}/integrations/${kind}/test`,
|
|
1460
|
+
{ method: "POST" }
|
|
1461
|
+
);
|
|
1462
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1463
|
+
}
|
|
1464
|
+
);
|
|
1465
|
+
server.registerTool(
|
|
1466
|
+
"run_fullstack_audit",
|
|
1467
|
+
{
|
|
1468
|
+
title: titleOf("run_fullstack_audit"),
|
|
1469
|
+
description: descOf("run_fullstack_audit"),
|
|
1470
|
+
annotations: annotationsFor("run_fullstack_audit"),
|
|
1471
|
+
inputSchema: {
|
|
1472
|
+
project_id: z.string().optional().describe("Project ID to audit. Defaults to the configured project.")
|
|
1473
|
+
}
|
|
1474
|
+
},
|
|
1475
|
+
async ({ project_id }) => {
|
|
1476
|
+
const pid = project_id ?? config.projectId;
|
|
1477
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "project_id is required");
|
|
1478
|
+
const data = await apiCall(
|
|
1479
|
+
`/v1/admin/projects/${pid}/audit`,
|
|
1480
|
+
{ method: "POST", body: "{}" }
|
|
1481
|
+
);
|
|
1482
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
1483
|
+
}
|
|
1484
|
+
);
|
|
1485
|
+
server.registerTool(
|
|
1486
|
+
"get_backend_health",
|
|
1487
|
+
{
|
|
1488
|
+
title: titleOf("get_backend_health"),
|
|
1489
|
+
description: descOf("get_backend_health"),
|
|
1490
|
+
annotations: annotationsFor("get_backend_health"),
|
|
1491
|
+
inputSchema: {
|
|
1492
|
+
project_id: z.string().optional().describe("Project ID. Defaults to the configured project."),
|
|
1493
|
+
include_logs: z.boolean().optional().describe("Whether to include recent backend error logs (default: true).")
|
|
1494
|
+
}
|
|
1495
|
+
},
|
|
1496
|
+
async ({ project_id, include_logs = true }) => {
|
|
1497
|
+
const pid = project_id ?? config.projectId;
|
|
1498
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "project_id is required");
|
|
1499
|
+
const [schemaRes, advisorsRes, logsRes] = await Promise.allSettled([
|
|
1500
|
+
apiCall(`/v1/admin/projects/${pid}/backend/schema`),
|
|
1501
|
+
apiCall(`/v1/admin/projects/${pid}/db-advisors`),
|
|
1502
|
+
include_logs ? apiCall(`/v1/admin/projects/${pid}/backend/logs?service=api`) : Promise.resolve(null)
|
|
1503
|
+
]);
|
|
1504
|
+
const result = {
|
|
1505
|
+
schema: schemaRes.status === "fulfilled" ? schemaRes.value : { error: String(schemaRes.reason) },
|
|
1506
|
+
advisors: advisorsRes.status === "fulfilled" ? advisorsRes.value : { error: String(advisorsRes.reason) },
|
|
1507
|
+
logs: logsRes.status === "fulfilled" ? logsRes.value : include_logs ? { error: String(logsRes.reason) } : null
|
|
1508
|
+
};
|
|
1509
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1510
|
+
}
|
|
1511
|
+
);
|
|
1512
|
+
server.registerTool(
|
|
1513
|
+
"list_skills",
|
|
1514
|
+
{
|
|
1515
|
+
title: titleOf("list_skills"),
|
|
1516
|
+
description: descOf("list_skills"),
|
|
1517
|
+
annotations: annotationsFor("list_skills"),
|
|
1518
|
+
inputSchema: {
|
|
1519
|
+
category: z.string().optional().describe("Filter by category: workflow, debug, test, audit, enhance, \u2026"),
|
|
1520
|
+
search: z.string().optional().describe("Free-text search across slug, title, description"),
|
|
1521
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
1522
|
+
limit: z.number().optional().describe("Max results per page (default 200, max 200)")
|
|
1523
|
+
}
|
|
1524
|
+
},
|
|
1525
|
+
async (args) => {
|
|
1526
|
+
const qs = new URLSearchParams();
|
|
1527
|
+
if (args.category) qs.set("category", args.category);
|
|
1528
|
+
if (args.search) qs.set("q", args.search);
|
|
1529
|
+
if (args.page) qs.set("page", String(args.page));
|
|
1530
|
+
qs.set("limit", String(Math.min(args.limit ?? 200, 200)));
|
|
1531
|
+
const data = await apiCall(`/v1/admin/skills?${qs}`);
|
|
1532
|
+
const skills = Array.isArray(data) ? data : [];
|
|
1533
|
+
return jsonText({ skills, count: skills.length });
|
|
1534
|
+
}
|
|
1535
|
+
);
|
|
1536
|
+
server.registerTool(
|
|
1537
|
+
"get_skill",
|
|
1538
|
+
{
|
|
1539
|
+
title: titleOf("get_skill"),
|
|
1540
|
+
description: descOf("get_skill"),
|
|
1541
|
+
annotations: annotationsFor("get_skill"),
|
|
1542
|
+
inputSchema: {
|
|
1543
|
+
slug: z.string().describe('Skill slug, e.g. "workflow-fix-and-ship"')
|
|
1544
|
+
}
|
|
1545
|
+
},
|
|
1546
|
+
async (args) => {
|
|
1547
|
+
const data = await apiCall(`/v1/admin/skills/${args.slug}`);
|
|
1548
|
+
return jsonText(data);
|
|
1549
|
+
}
|
|
1550
|
+
);
|
|
1551
|
+
server.registerTool(
|
|
1552
|
+
"start_skill_pipeline",
|
|
1553
|
+
{
|
|
1554
|
+
title: titleOf("start_skill_pipeline"),
|
|
1555
|
+
description: descOf("start_skill_pipeline"),
|
|
1556
|
+
annotations: annotationsFor("start_skill_pipeline"),
|
|
1557
|
+
inputSchema: {
|
|
1558
|
+
root_skill_slug: z.string().describe('Root skill slug to run, e.g. "workflow-fix-and-ship"'),
|
|
1559
|
+
report_id: z.string().optional().describe("Report UUID to attach the pipeline to"),
|
|
1560
|
+
mode: z.enum(["handoff", "cloud"]).optional().describe("handoff (default): get context packet for local agent. cloud: auto-dispatch via Cursor Cloud."),
|
|
1561
|
+
project_id: z.string().optional().describe("Project UUID. Falls back to the configured project.")
|
|
1562
|
+
}
|
|
1563
|
+
},
|
|
1564
|
+
async (args) => {
|
|
1565
|
+
const resolvedProjectId = args.project_id ?? projectId;
|
|
1566
|
+
if (!resolvedProjectId) return jsonText({ error: "No project_id provided or configured." });
|
|
1567
|
+
const data = await apiCall(
|
|
1568
|
+
"/v1/admin/skills/pipelines",
|
|
1569
|
+
{ method: "POST", body: JSON.stringify({ ...args, project_id: resolvedProjectId }) }
|
|
1570
|
+
);
|
|
1571
|
+
return jsonText(data);
|
|
1572
|
+
}
|
|
1573
|
+
);
|
|
1574
|
+
server.registerTool(
|
|
1575
|
+
"get_pipeline_run",
|
|
1576
|
+
{
|
|
1577
|
+
title: titleOf("get_pipeline_run"),
|
|
1578
|
+
description: descOf("get_pipeline_run"),
|
|
1579
|
+
annotations: annotationsFor("get_pipeline_run"),
|
|
1580
|
+
inputSchema: {
|
|
1581
|
+
run_id: z.string().describe("Pipeline run UUID")
|
|
1582
|
+
}
|
|
1583
|
+
},
|
|
1584
|
+
async (args) => {
|
|
1585
|
+
const data = await apiCall(`/v1/admin/skills/pipelines/${args.run_id}`);
|
|
1586
|
+
return jsonText(data);
|
|
1587
|
+
}
|
|
1588
|
+
);
|
|
1589
|
+
server.registerTool(
|
|
1590
|
+
"checkin_pipeline_step",
|
|
1591
|
+
{
|
|
1592
|
+
title: titleOf("checkin_pipeline_step"),
|
|
1593
|
+
description: descOf("checkin_pipeline_step"),
|
|
1594
|
+
annotations: annotationsFor("checkin_pipeline_step"),
|
|
1595
|
+
inputSchema: {
|
|
1596
|
+
run_id: z.string().describe("Pipeline run UUID"),
|
|
1597
|
+
step_index: z.number().describe("Step index (0-based)"),
|
|
1598
|
+
status: z.enum(["running", "passed", "failed", "skipped"]).describe("Step status"),
|
|
1599
|
+
notes: z.string().optional().describe("Optional notes or output summary"),
|
|
1600
|
+
pr_url: z.string().optional().describe("PR URL opened during this step"),
|
|
1601
|
+
agent_ref: z.string().optional().describe("Cursor agentId or external agent reference")
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
async (args) => {
|
|
1605
|
+
const { run_id, step_index, ...body } = args;
|
|
1606
|
+
await apiCall(
|
|
1607
|
+
`/v1/admin/skills/pipelines/${run_id}/steps/${step_index}/checkin`,
|
|
1608
|
+
{ method: "POST", body: JSON.stringify(body) }
|
|
1609
|
+
);
|
|
1610
|
+
return jsonText({ ok: true, message: `Step ${step_index} \u2192 ${args.status}` });
|
|
1611
|
+
}
|
|
1612
|
+
);
|
|
1321
1613
|
const grantedScopes = config.scopes;
|
|
1322
1614
|
if (grantedScopes !== void 0) {
|
|
1323
1615
|
const toolRegistry = server._registeredTools;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "MCP server exposing Mushi Mushi reports to coding agents",
|
|
6
6
|
"type": "module",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"@mushi-mushi/core": "^1.
|
|
28
|
+
"@mushi-mushi/core": "^1.9.0",
|
|
29
29
|
"zod": "^4.4.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|