@mushi-mushi/mcp 0.10.0 → 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 (3) hide show
  1. package/README.md +10 -2
  2. package/dist/index.js +221 -2
  3. package/package.json +17 -17
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # mushi-mcp
1
+ # @mushi-mushi/mcp
2
2
 
3
3
  > **Sentry sees what code throws. Mushi sees what users feel — and closes the loop with AI.**
4
4
 
@@ -20,7 +20,7 @@ That command reads `~/.mushirc`, writes `.cursor/mcp.json` with the `mushi` serv
20
20
 
21
21
  > **What this is, and what it isn't**
22
22
  >
23
- > - **This package** (`mushi-mcp`) is the MCP **server** — runs locally next to your editor, talks to the Mushi API, and presents bug reports as MCP tools/resources to your coding agent. The npm package name is `mushi-mcp`; the scoped alias `@mushi-mushi/mcp` still works.
23
+ > - **This package** (`@mushi-mushi/mcp`) is the MCP **server** — runs locally next to your editor, talks to the Mushi API, and presents bug reports as MCP tools/resources to your coding agent. Always install it by its scoped name (`npx -y @mushi-mushi/mcp@latest`) the bare `mushi-mcp` name was never published to npm.
24
24
  > - **`@mushi-mushi/agents`** ships the MCP **client adapter** — used by the autofix orchestrator when your project's `autofix_agent = 'mcp'`. See `packages/agents/src/adapters/mcp.ts`.
25
25
  > - The `generic_mcp` adapter shipped before V5.3 was a misnomer (it spoke plain REST). It is now `RestFixWorkerAgent`; the old export is kept as a deprecated alias for one more minor.
26
26
 
@@ -130,6 +130,8 @@ The endpoint accepts JSON-RPC 2.0 over POST (returns `application/json` or `text
130
130
  | `get_fix_timeline` | Ordered timeline of a fix attempt (dispatched → started → branch → commit → PR → CI → completed/failed) |
131
131
  | `get_blast_radius` | Graph traversal showing other components a bug group touches |
132
132
  | `get_knowledge_graph` | Traverse the knowledge graph from a seed component or page |
133
+ | `setup_check` | The 4 **dispatch-readiness** checks (GitHub repo, codebase indexed, Anthropic key, autofix enabled) — run before `dispatch_fix` |
134
+ | `ingest_setup_check` | The 4 **required ingest** checks (project, active API key, SDK heartbeat, first report) + `last_sdk_seen_at` diagnostics — run after wiring env vars to confirm the SDK is reporting |
133
135
 
134
136
  ### Write / agentic
135
137
 
@@ -366,3 +368,9 @@ In Cursor chat, type `/` — you should see the Mushi Mushi slash-prompts (`/sum
366
368
  ## License
367
369
 
368
370
  MIT
371
+
372
+
373
+ <!-- mushi-readme-stats-footer -->
374
+ ---
375
+
376
+ <sub>Monorepo scale (June 2026): 43 edge functions · 234 SQL migrations · 13 outbound plugins · 11 inbound adapters. Canonical counts: <a href="https://github.com/kensaurus/mushi-mushi/blob/master/docs/stats.md">docs/stats.md</a> · <code>pnpm docs-stats</code></sub>
package/dist/index.js CHANGED
@@ -133,10 +133,18 @@ var TOOL_CATALOG = [
133
133
  {
134
134
  name: "setup_check",
135
135
  title: "Dispatch preflight check",
136
- description: "Run the 4 dispatch-readiness checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, Anthropic BYOK key present, autofix enabled). Also returns the target repo URL when GitHub is connected. Use this before calling dispatch_fix to understand why a dispatch might fail \u2014 or to validate that the onboarding wizard is complete.",
136
+ description: "Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, Anthropic BYOK key present, autofix enabled). Also returns the target repo URL when GitHub is connected. Use this before calling dispatch_fix to understand why a dispatch might fail. For SDK ingest health (API key \u2192 heartbeat \u2192 first report), call ingest_setup_check instead.",
137
137
  scope: "mcp:read",
138
138
  hints: { readOnly: true, idempotent: true, openWorld: true },
139
- useCase: "Is this project ready to auto-fix bugs? What is blocking me?"
139
+ useCase: "Is this project ready to auto-fix bugs? What is blocking dispatch?"
140
+ },
141
+ {
142
+ name: "ingest_setup_check",
143
+ title: "Ingest setup check",
144
+ description: "Run the 4 **required ingest** checks for the project tied to this API key: project exists, active API key, SDK heartbeat (or real report), and at least one ingested report. Returns per-step pass/fail plus last_sdk_seen_at and endpoint host diagnostics. Use after wiring env vars or pasting the SDK snippet to confirm the banner will work.",
145
+ scope: "mcp:read",
146
+ hints: { readOnly: true, idempotent: true, openWorld: true },
147
+ useCase: "Is the SDK installed and ingesting reports? Why is my banner still missing?"
140
148
  },
141
149
  // --- Write / agentic ----------------------------------------------------
142
150
  {
@@ -289,6 +297,55 @@ var TDD_TOOL_CATALOG = [
289
297
  scope: "mcp:write",
290
298
  hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
291
299
  useCase: "Approve this auto-generated test."
300
+ },
301
+ {
302
+ name: "reply_to_reporter",
303
+ title: "Reply to a reporter",
304
+ description: "Send a visible message to the end-user who filed a bug report. The reply appears in the in-app Mushi widget as an admin comment and creates an unread notification badge so the reporter sees it immediately. Use this to answer questions, request reproduction steps, or confirm a fix \u2014 without leaving the Cursor IDE.",
305
+ scope: "mcp:write",
306
+ hints: { readOnly: false, destructive: false, idempotent: false, openWorld: false },
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."
292
349
  }
293
350
  ];
294
351
 
@@ -696,6 +753,36 @@ function createMushiServer(config) {
696
753
  });
697
754
  }
698
755
  );
756
+ server.registerTool(
757
+ "ingest_setup_check",
758
+ {
759
+ title: titleOf("ingest_setup_check"),
760
+ description: descOf("ingest_setup_check"),
761
+ annotations: annotationsFor("ingest_setup_check"),
762
+ inputSchema: {}
763
+ },
764
+ async () => {
765
+ const data = await apiCall("/v1/sync/ingest-setup");
766
+ const failed = data.steps.filter((s) => s.required && !s.complete);
767
+ const summary = data.ready ? `Ingest setup complete (${data.required_complete}/${data.required_total}) for ${data.project_name}.` : `Ingest incomplete (${data.required_complete}/${data.required_total}) \u2014 still need: ${failed.map((s) => s.label).join(", ")}.`;
768
+ return jsonText({
769
+ ready: data.ready,
770
+ projectId: data.project_id,
771
+ projectName: data.project_name,
772
+ requiredComplete: data.required_complete,
773
+ requiredTotal: data.required_total,
774
+ steps: data.steps.map((s) => ({
775
+ id: s.id,
776
+ label: s.label,
777
+ passed: s.complete,
778
+ required: s.required,
779
+ hint: s.hint
780
+ })),
781
+ diagnostic: data.diagnostic ?? null,
782
+ summary
783
+ });
784
+ }
785
+ );
699
786
  server.registerTool(
700
787
  "submit_fix_result",
701
788
  {
@@ -1249,6 +1336,138 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
1249
1336
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1250
1337
  }
1251
1338
  );
1339
+ server.registerTool(
1340
+ "reply_to_reporter",
1341
+ {
1342
+ title: titleOf("reply_to_reporter", TDD_TOOL_CATALOG),
1343
+ description: descOf("reply_to_reporter", TDD_TOOL_CATALOG),
1344
+ annotations: annotationsFor("reply_to_reporter", TDD_TOOL_CATALOG),
1345
+ inputSchema: {
1346
+ reportId: z.string().describe("Report id to reply to"),
1347
+ message: z.string().min(1).max(1e4).describe("Message text to send to the reporter"),
1348
+ authorName: z.string().optional().describe('Display name for the admin sender (default: "Mushi Admin")')
1349
+ }
1350
+ },
1351
+ async ({ reportId, message, authorName }) => {
1352
+ const data = await apiCall(
1353
+ `/v1/sync/reports/${reportId}/reply`,
1354
+ {
1355
+ method: "POST",
1356
+ body: JSON.stringify({ message, author_name: authorName })
1357
+ }
1358
+ );
1359
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1360
+ }
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
+ );
1252
1471
  const grantedScopes = config.scopes;
1253
1472
  if (grantedScopes !== void 0) {
1254
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.0",
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",
@@ -23,30 +23,18 @@
23
23
  "CODE_OF_CONDUCT.md",
24
24
  "SECURITY.md"
25
25
  ],
26
- "scripts": {
27
- "build": "tsup",
28
- "clean:types": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
29
- "dev": "tsup --watch",
30
- "lint": "eslint src/",
31
- "test": "vitest run",
32
- "test:smoke": "node scripts/smoke-stdio.mjs",
33
- "test:localhost": "node scripts/localhost-e2e.mjs",
34
- "demo": "node scripts/demo-terminal-config.mjs",
35
- "inspector": "npx --yes @modelcontextprotocol/inspector@latest node ./dist/index.js",
36
- "typecheck": "tsc --noEmit"
37
- },
38
26
  "dependencies": {
39
27
  "@modelcontextprotocol/sdk": "^1.29.0",
40
- "@mushi-mushi/core": "workspace:^",
28
+ "@mushi-mushi/core": "^1.9.0",
41
29
  "zod": "^4.4.2"
42
30
  },
43
31
  "devDependencies": {
44
- "@mushi-mushi/eslint-config": "workspace:*",
45
32
  "@types/node": "^22.19.17",
46
33
  "eslint": "^10.3.0",
47
34
  "tsup": "^8.5.1",
48
35
  "typescript": "^6.0.3",
49
- "vitest": "^4.1.5"
36
+ "vitest": "^4.1.5",
37
+ "@mushi-mushi/eslint-config": "0.0.0"
50
38
  },
51
39
  "repository": {
52
40
  "type": "git",
@@ -85,5 +73,17 @@
85
73
  ],
86
74
  "engines": {
87
75
  "node": ">=20"
76
+ },
77
+ "scripts": {
78
+ "build": "tsup",
79
+ "clean:types": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
80
+ "dev": "tsup --watch",
81
+ "lint": "eslint src/",
82
+ "test": "vitest run",
83
+ "test:smoke": "node scripts/smoke-stdio.mjs",
84
+ "test:localhost": "node scripts/localhost-e2e.mjs",
85
+ "demo": "node scripts/demo-terminal-config.mjs",
86
+ "inspector": "npx --yes @modelcontextprotocol/inspector@latest node ./dist/index.js",
87
+ "typecheck": "tsc --noEmit"
88
88
  }
89
- }
89
+ }