@mushi-mushi/mcp 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +150 -0
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -305,6 +305,47 @@ 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
+ // ── Full-Stack Audit tools (Phase 5) ────────────────────────────────────────
334
+ {
335
+ name: "run_fullstack_audit",
336
+ title: "Run a full-stack health audit",
337
+ 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.",
338
+ scope: "mcp:write",
339
+ hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
340
+ 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."
341
+ },
342
+ {
343
+ name: "get_backend_health",
344
+ title: "Get backend health for a project",
345
+ 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.',
346
+ scope: "mcp:read",
347
+ hints: { readOnly: true, idempotent: true, openWorld: true },
348
+ useCase: "Are there any tables without RLS? Show me recent backend errors for this project."
308
349
  }
309
350
  ];
310
351
 
@@ -1318,6 +1359,115 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
1318
1359
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1319
1360
  }
1320
1361
  );
1362
+ server.registerTool(
1363
+ "list_qa_story_runs",
1364
+ {
1365
+ title: titleOf("list_qa_story_runs", TDD_TOOL_CATALOG),
1366
+ description: descOf("list_qa_story_runs", TDD_TOOL_CATALOG),
1367
+ annotations: annotationsFor("list_qa_story_runs", TDD_TOOL_CATALOG),
1368
+ inputSchema: {
1369
+ storyId: z.string().describe("QA story id (uuid)"),
1370
+ limit: z.number().int().min(1).max(50).optional().default(10).describe("Max runs to return (default 10)")
1371
+ }
1372
+ },
1373
+ async ({ storyId, limit }) => {
1374
+ const data = await apiCall(
1375
+ `/v1/admin/projects/${config.projectId}/qa-stories/${storyId}/runs?limit=${limit ?? 10}`
1376
+ );
1377
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1378
+ }
1379
+ );
1380
+ server.registerTool(
1381
+ "get_qa_story_run",
1382
+ {
1383
+ title: titleOf("get_qa_story_run", TDD_TOOL_CATALOG),
1384
+ description: descOf("get_qa_story_run", TDD_TOOL_CATALOG),
1385
+ annotations: annotationsFor("get_qa_story_run", TDD_TOOL_CATALOG),
1386
+ inputSchema: {
1387
+ storyId: z.string().describe("QA story id (uuid)"),
1388
+ runId: z.string().describe("Run id (uuid) to fetch detail for")
1389
+ }
1390
+ },
1391
+ async ({ storyId, runId }) => {
1392
+ const data = await apiCall(
1393
+ `/v1/admin/projects/${config.projectId}/qa-stories/${storyId}/runs?limit=50`
1394
+ );
1395
+ const run = data?.data?.runs?.find((r) => r.id === runId) ?? null;
1396
+ if (!run) {
1397
+ throw new MushiApiError(
1398
+ 404,
1399
+ "RUN_NOT_FOUND",
1400
+ `Run ${runId} not found in the 50 most recent runs for story ${storyId}`
1401
+ );
1402
+ }
1403
+ return { content: [{ type: "text", text: JSON.stringify(run, null, 2) }] };
1404
+ }
1405
+ );
1406
+ server.registerTool(
1407
+ "test_notification_channel",
1408
+ {
1409
+ title: titleOf("test_notification_channel", TDD_TOOL_CATALOG),
1410
+ description: descOf("test_notification_channel", TDD_TOOL_CATALOG),
1411
+ annotations: annotationsFor("test_notification_channel", TDD_TOOL_CATALOG),
1412
+ inputSchema: {
1413
+ kind: z.enum(["slack", "discord"]).describe("Notification channel kind to test")
1414
+ }
1415
+ },
1416
+ async ({ kind }) => {
1417
+ const data = await apiCall(
1418
+ `/v1/admin/projects/${config.projectId}/integrations/${kind}/test`,
1419
+ { method: "POST" }
1420
+ );
1421
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1422
+ }
1423
+ );
1424
+ server.registerTool(
1425
+ "run_fullstack_audit",
1426
+ {
1427
+ title: titleOf("run_fullstack_audit"),
1428
+ description: descOf("run_fullstack_audit"),
1429
+ annotations: annotationsFor("run_fullstack_audit"),
1430
+ inputSchema: {
1431
+ project_id: z.string().optional().describe("Project ID to audit. Defaults to the configured project.")
1432
+ }
1433
+ },
1434
+ async ({ project_id }) => {
1435
+ const pid = project_id ?? config.projectId;
1436
+ if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "project_id is required");
1437
+ const data = await apiCall(
1438
+ `/v1/admin/projects/${pid}/audit`,
1439
+ { method: "POST", body: "{}" }
1440
+ );
1441
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1442
+ }
1443
+ );
1444
+ server.registerTool(
1445
+ "get_backend_health",
1446
+ {
1447
+ title: titleOf("get_backend_health"),
1448
+ description: descOf("get_backend_health"),
1449
+ annotations: annotationsFor("get_backend_health"),
1450
+ inputSchema: {
1451
+ project_id: z.string().optional().describe("Project ID. Defaults to the configured project."),
1452
+ include_logs: z.boolean().optional().describe("Whether to include recent backend error logs (default: true).")
1453
+ }
1454
+ },
1455
+ async ({ project_id, include_logs = true }) => {
1456
+ const pid = project_id ?? config.projectId;
1457
+ if (!pid) throw new MushiApiError(400, "MISSING_PROJECT_ID", "project_id is required");
1458
+ const [schemaRes, advisorsRes, logsRes] = await Promise.allSettled([
1459
+ apiCall(`/v1/admin/projects/${pid}/backend/schema`),
1460
+ apiCall(`/v1/admin/projects/${pid}/db-advisors`),
1461
+ include_logs ? apiCall(`/v1/admin/projects/${pid}/backend/logs?service=api`) : Promise.resolve(null)
1462
+ ]);
1463
+ const result = {
1464
+ schema: schemaRes.status === "fulfilled" ? schemaRes.value : { error: String(schemaRes.reason) },
1465
+ advisors: advisorsRes.status === "fulfilled" ? advisorsRes.value : { error: String(advisorsRes.reason) },
1466
+ logs: logsRes.status === "fulfilled" ? logsRes.value : include_logs ? { error: String(logsRes.reason) } : null
1467
+ };
1468
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
1469
+ }
1470
+ );
1321
1471
  const grantedScopes = config.scopes;
1322
1472
  if (grantedScopes !== void 0) {
1323
1473
  const toolRegistry = server._registeredTools;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mushi-mushi/mcp",
3
- "version": "0.10.1",
3
+ "version": "0.11.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.7.5",
28
+ "@mushi-mushi/core": "^1.9.0",
29
29
  "zod": "^4.4.2"
30
30
  },
31
31
  "devDependencies": {