@mushi-mushi/mcp 0.17.0 → 0.17.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 CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  > **Your AI wrote it. Mushi tells you why it broke.**
4
4
 
5
- [Model Context Protocol](https://spec.modelcontextprotocol.io/) server that wires Mushi into Cursor, Claude Code, and any MCP client. Pull fix context, run triage, and dispatch fixes without a second LLM vendor key.
5
+ [![Add to Cursor](https://img.shields.io/badge/Add%20to-Cursor-0098FF)](https://kensaur.us/mushi-mushi/docs/connect)
6
+ [![Add to VS Code](https://img.shields.io/badge/Add%20to-VS%20Code-007ACC?logo=visualstudiocode&logoColor=white)](https://kensaur.us/mushi-mushi/docs/connect)
7
+ [![Try the demo — no signup](https://img.shields.io/badge/Try%20the%20demo-no%20signup-E34234)](https://kensaur.us/mushi-mushi/docs/connect)
8
+ [![npm](https://img.shields.io/npm/v/@mushi-mushi/mcp?color=cb3837)](https://www.npmjs.com/package/@mushi-mushi/mcp)
9
+
10
+ [Model Context Protocol](https://spec.modelcontextprotocol.io/) server that wires Mushi into Cursor, Claude Code, and any MCP client. Pull fix context, review bugs, and dispatch fixes without a second LLM vendor key.
6
11
 
7
12
  Wire it into Cursor, Claude Code, or any MCP client in one command:
8
13
 
@@ -259,7 +264,7 @@ Named templates the MCP client surfaces in its slash-menu. Each one bakes in the
259
264
  |---|---|---|---|
260
265
  | `MUSHI_API_KEY` | yes | — | Project API key with `mcp:read` or `mcp:write` scope. Mint one in the admin console → **Projects** (the one-time reveal card has a **Copy as `.env.local`** tab). |
261
266
  | `MUSHI_PROJECT_ID` | yes | — | UUID from the admin console URL (`/projects/<uuid>/...`) or the reveal card. |
262
- | `MUSHI_API_ENDPOINT` | no | `https://api.mushimushi.dev` | Override only if you self-host. Localhost value: `http://localhost:54321/functions/v1/api`. |
267
+ | `MUSHI_API_ENDPOINT` | no | `https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api` | Override only if you self-host. Localhost: `http://localhost:54321/functions/v1/api`. |
263
268
 
264
269
  ### Storing the key in `.env.local`
265
270
 
@@ -269,7 +274,7 @@ The MCP binary reads these three vars from `process.env` on spawn — that means
269
274
 
270
275
  ```bash
271
276
  # Mushi MCP — drop into .env.local (gitignored). The MCP binary picks these up on spawn.
272
- MUSHI_API_ENDPOINT=https://api.mushimushi.dev
277
+ MUSHI_API_ENDPOINT=https://dxptnwrhwsqckaftyymj.supabase.co/functions/v1/api
273
278
  MUSHI_PROJECT_ID=<your-uuid>
274
279
  MUSHI_API_KEY=mushi_live_…
275
280
  ```
@@ -459,6 +464,7 @@ Both are complementary — use Sentry for exception noise, Mushi for "the user s
459
464
 
460
465
  ## See also
461
466
 
467
+ - [GTM distribution runbook](../../docs/marketing/GTM-DISTRIBUTION.md) — MCP registry, Glama, Connect, cursor.directory, release checklist.
462
468
  - [V5.3 whitepaper §2.10](../../MushiMushi_Whitepaper_V5.md) — the agentic fix architecture this server feeds into.
463
469
  - [`@mushi-mushi/agents`](../agents/README.md) — orchestrator that consumes MCP-exposed fix workers.
464
470
 
@@ -470,4 +476,4 @@ MIT
470
476
  <!-- mushi-readme-stats-footer -->
471
477
  ---
472
478
 
473
- <sub>Monorepo scale (June 2026): 47 edge functions · 256 SQL migrations · 13 outbound plugins · 11 inbound adapters · 18 pipeline agents. 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>
479
+ <sub>Monorepo scale (June 2026): 51 edge functions · 298 SQL migrations · 13 outbound plugins · 11 inbound adapters · 19 pipeline agents. 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/branding.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  MUSHI_SERVER_METADATA,
6
6
  MUSHI_WEBSITE_URL,
7
7
  mcpIconUrl
8
- } from "./chunk-EHB2BGWX.js";
8
+ } from "./chunk-EANW5YTI.js";
9
9
  export {
10
10
  MUSHI_ICON_PNG_URL,
11
11
  MUSHI_ICON_SVG_URL,
package/dist/catalog.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  RESOURCE_CATALOG,
6
6
  TDD_TOOL_CATALOG,
7
7
  TOOL_CATALOG
8
- } from "./chunk-MM3IMFM4.js";
8
+ } from "./chunk-7UOTJ2LI.js";
9
9
  export {
10
10
  ALL_SCOPES,
11
11
  CODEBASE_TOOL_CATALOG,
@@ -4,10 +4,10 @@ var TOOL_CATALOG = [
4
4
  {
5
5
  name: "get_recent_reports",
6
6
  title: "Recent bug reports",
7
- description: "List recent bug reports with optional filters (status / category / severity). Use this to survey what the triage queue looks like right now.",
7
+ description: "List recent bug reports with optional filters (status / category / severity). Use this to see what is waiting in your reports inbox right now.",
8
8
  scope: "mcp:read",
9
9
  hints: { readOnly: true, idempotent: true, openWorld: true },
10
- useCase: "What landed in my triage queue today?"
10
+ useCase: "What landed in my bug queue today?"
11
11
  },
12
12
  {
13
13
  name: "get_report_detail",
@@ -134,7 +134,7 @@ var TOOL_CATALOG = [
134
134
  {
135
135
  name: "setup_check",
136
136
  title: "Dispatch preflight check",
137
- description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] 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.",
137
+ description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, your Anthropic API key present, autofix enabled). Also returns the target repo URL when GitHub is connected.",
138
138
  scope: "mcp:read",
139
139
  hints: { readOnly: true, idempotent: true, openWorld: true },
140
140
  useCase: "Is this project ready to auto-fix bugs? What is blocking dispatch?"
@@ -184,7 +184,7 @@ var TOOL_CATALOG = [
184
184
  {
185
185
  name: "dispatch_fix",
186
186
  title: "Dispatch Mushi fix agent",
187
- description: 'Dispatch the Mushi agentic fix orchestrator for a classified report. Set agent="cursor_cloud" to dispatch a Cursor Cloud Agent that opens a signed draft PR. Returns a fix_attempt id; poll get_fix_timeline for progress.',
187
+ description: 'Start the Mushi fix agent for a classified report. Set agent="cursor_cloud" to dispatch a Cursor Cloud Agent that opens a signed draft PR. Returns a fix_attempt id; poll get_fix_timeline for progress.',
188
188
  scope: "mcp:write",
189
189
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
190
190
  useCase: "Let the in-repo agent attempt this fix for me (or: dispatch a Cursor Cloud Agent)."
@@ -200,7 +200,7 @@ var TOOL_CATALOG = [
200
200
  {
201
201
  name: "test_gen_from_report",
202
202
  title: "Generate Playwright test from report",
203
- description: "POST to inventory test-gen: uses the project BYOK LLM to author a Playwright spec from a classified report and opens a draft PR (internal service orchestration). Requires inventory_v2 + GitHub + LLM keys.",
203
+ description: "Generate a Playwright spec from a classified report using your project LLM key and open a draft PR. Requires inventory_v2 + GitHub + LLM keys.",
204
204
  scope: "mcp:write",
205
205
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
206
206
  useCase: "Turn this regression report into an E2E test PR."
@@ -232,7 +232,7 @@ var TOOL_CATALOG = [
232
232
  {
233
233
  name: "reopen_report",
234
234
  title: "Reopen report (operator)",
235
- description: "Move a report to reopened for regression triage.",
235
+ description: "Move a report to reopened for regression review.",
236
236
  scope: "mcp:write",
237
237
  hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
238
238
  useCase: "Reopen a regression the reporter flagged as not fixed."
@@ -290,10 +290,10 @@ var TOOL_CATALOG = [
290
290
  {
291
291
  name: "get_project_context",
292
292
  title: "Project context snapshot",
293
- description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness, BYOK LLM config, plan tier, and active integration health. Equivalent to a merged preflight + activation + settings read. Agents should call this at the start of a triage session to orient themselves.",
293
+ description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness, your LLM key config, plan tier, and active integration health. Equivalent to a merged preflight + activation + settings read. Agents should call this at the start of a review session to orient themselves.",
294
294
  scope: "mcp:read",
295
295
  hints: { readOnly: true, idempotent: true, openWorld: true },
296
- useCase: "Give me a status overview of this project before I start triaging."
296
+ useCase: "Give me a status overview of this project before I start reviewing bugs."
297
297
  },
298
298
  {
299
299
  name: "get_pipeline_logs",
@@ -314,7 +314,7 @@ var TOOL_CATALOG = [
314
314
  {
315
315
  name: "triage_issue",
316
316
  title: "Triage issue end-to-end",
317
- description: 'Read-only orchestration tool: combines report detail, evidence, similar bugs, fix context, blast radius, and recent pipeline logs for a report into a single structured triage packet. Returns a prioritised list of recommended next actions (investigate, dispatch_fix, group_with, dismiss). Equivalent to a Sentry "Analyze with Seer" flow grounded in user-felt reports. Pass report_id to kick off triage. Call this before dispatch_fix.',
317
+ description: 'Read-only combined tool: merges report detail, evidence, similar bugs, fix context, blast radius, and recent pipeline logs for a report into a single structured review packet. Returns a prioritised list of recommended next actions (investigate, dispatch_fix, group_with, dismiss). Equivalent to a Sentry "Analyze with Seer" flow grounded in user-felt reports. Pass report_id to kick off review. Call this before dispatch_fix.',
318
318
  scope: "mcp:read",
319
319
  hints: { readOnly: true, idempotent: true, openWorld: true },
320
320
  useCase: "Analyze this bug report end-to-end and tell me what to do."
@@ -341,8 +341,8 @@ var RESOURCE_CATALOG = [
341
341
  {
342
342
  name: "project_dashboard",
343
343
  uri: "project://dashboard",
344
- title: "PDCA dashboard snapshot",
345
- description: "PDCA health snapshot \u2014 stage counts, bottleneck, recent activity (same payload the admin console polls).",
344
+ title: "Loop dashboard snapshot",
345
+ description: "Loop health snapshot \u2014 stage counts, bottleneck, recent activity (same payload the admin console polls).",
346
346
  scope: "mcp:read"
347
347
  },
348
348
  {
@@ -363,14 +363,14 @@ var RESOURCE_CATALOG = [
363
363
  name: "privacy_status",
364
364
  uri: "privacy://status",
365
365
  title: "Privacy posture",
366
- description: "Returns the privacy posture for this project: storage region, LLM provider, whether BYOK is configured, data retention window, and last audit timestamp. Agents should read this before dispatching a fix to confirm that client data stays within the expected boundary. Reads as project data does not leave the project's own LLM account when byok_configured=true.",
366
+ description: "Returns the privacy posture for this project: storage region, LLM provider, whether your own LLM key is configured, data retention window, and last audit timestamp. Agents should read this before dispatching a fix to confirm that client data stays within the expected boundary. Reads as project data does not leave the project's own LLM account when byok_configured=true.",
367
367
  scope: "mcp:read"
368
368
  },
369
369
  {
370
370
  name: "evolution_history",
371
371
  uri: "evolution://history",
372
372
  title: "Evolution history",
373
- 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 (flat scores, same bugs re-appearing). Use before triage to understand which bug classes the loop has already learned to handle, and which still need human attention.",
373
+ 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 (flat scores, same bugs re-appearing). Use before review to understand which bug classes the loop has already learned to handle, and which still need human attention.",
374
374
  scope: "mcp:read"
375
375
  },
376
376
  {
@@ -384,7 +384,7 @@ var RESOURCE_CATALOG = [
384
384
  name: "project_integration_health",
385
385
  uri: "project://integration-health",
386
386
  title: "Integration health",
387
- 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.",
387
+ description: "Live health status of every configured integration channel (Sentry, GitHub, LangFuse, PagerDuty, \u2026). Check this before dispatching a fix to fail-fast on broken channels rather than burning LLM budget and discovering the failure mid-run.",
388
388
  scope: "mcp:read"
389
389
  },
390
390
  {
@@ -448,8 +448,8 @@ var TDD_TOOL_CATALOG = [
448
448
  },
449
449
  {
450
450
  name: "improve_qa_story",
451
- title: "PDCA auto-improve a failing QA story",
452
- description: "Trigger the PDCA improver for a specific project. Finds recently failed qa_story_runs and uses Claude to write improved test scripts. New tests are created with source=pdca and approval gated by the original story's automation_mode.",
451
+ title: "Auto-improve a failing QA story",
452
+ description: "Find recently failed qa_story_runs and use Claude to write improved test scripts. New tests are created with source=pdca and approval gated by the original story's automation_mode.",
453
453
  scope: "mcp:write",
454
454
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
455
455
  useCase: "Fix my failing QA tests automatically."
@@ -464,16 +464,16 @@ var TDD_TOOL_CATALOG = [
464
464
  },
465
465
  {
466
466
  name: "list_byok_keys",
467
- title: "List BYOK API key pool",
468
- description: "List all BYOK API keys for the project, grouped by provider. Shows label, priority, status, and cooldown. Never returns the raw key value \u2014 only metadata. Use this to see which keys are active or exhausted.",
467
+ title: "List your API key pool",
468
+ description: "List all project API keys, grouped by provider. Shows label, priority, status, and cooldown. Never returns the raw key value \u2014 only metadata. Use this to see which keys are active or exhausted.",
469
469
  scope: "mcp:read",
470
470
  hints: { readOnly: true, idempotent: true, openWorld: true },
471
471
  useCase: "Which API keys are active and which are rate-limited?"
472
472
  },
473
473
  {
474
474
  name: "add_byok_key",
475
- title: "Add a BYOK API key",
476
- description: "Add a new API key to the project's BYOK pool for a given provider (anthropic, openai, firecrawl, browserbase, cursor). Specify label and priority for ordering. The key is stored encrypted in Supabase Vault.",
475
+ title: "Add an API key",
476
+ description: "Add a new API key to the project pool for a given provider (anthropic, openai, firecrawl, browserbase, cursor). Specify label and priority for ordering. The key is stored encrypted in Supabase Vault.",
477
477
  scope: "mcp:write",
478
478
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
479
479
  useCase: "Add a backup Anthropic key to the pool."
@@ -622,7 +622,7 @@ var CODEBASE_TOOL_CATALOG = [
622
622
  {
623
623
  name: "ask_codebase",
624
624
  title: "Ask about the indexed codebase",
625
- description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and an Anthropic or OpenAI BYOK key.",
625
+ description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and your Anthropic or OpenAI API key.",
626
626
  scope: "mcp:write",
627
627
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
628
628
  useCase: "How does authentication work in this repo?"
@@ -3,7 +3,9 @@ var MUSHI_WEBSITE_URL = "https://kensaur.us/mushi-mushi";
3
3
  var MUSHI_ICON_PNG_URL = `${MUSHI_WEBSITE_URL}/integrations/mushi-mark-512.png`;
4
4
  var MUSHI_ICON_SVG_URL = `${MUSHI_WEBSITE_URL}/favicon.svg`;
5
5
  function mcpIconUrl(mcpBaseUrl) {
6
- const base = mcpBaseUrl.replace(/\/+$/, "");
6
+ let end = mcpBaseUrl.length;
7
+ while (end > 0 && mcpBaseUrl.charCodeAt(end - 1) === 47) end--;
8
+ const base = mcpBaseUrl.slice(0, end);
7
9
  return `${base}?icon=1`;
8
10
  }
9
11
  var MUSHI_SERVER_ICONS = [
@@ -11,6 +11,7 @@ var FEATURE_GROUPS = [
11
11
  "audit",
12
12
  "docs",
13
13
  "usage",
14
+ "usage",
14
15
  /** Deprecated aliases kept for one release — hidden unless `legacy` or `all`. */
15
16
  "legacy"
16
17
  ];
package/dist/clients.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { FeatureGroup } from './feature-groups.js';
2
+
1
3
  /**
2
4
  * FILE: packages/mcp/src/clients.ts
3
5
  * PURPOSE: Shared, pure (React-free) client registry + install-config builders for
@@ -37,6 +39,7 @@
37
39
  * - Windsurf uses `serverUrl` for remote HTTP (NOT `url`) — encoding both would break it.
38
40
  * - The `cliIde` field drives `mushi setup --ide <cliIde>` in the CLI lane.
39
41
  */
42
+
40
43
  interface StdioConfig {
41
44
  command: string;
42
45
  args: string[];
@@ -61,6 +64,19 @@ interface McpBuildInput {
61
64
  endpoint: string;
62
65
  /** Hosted Streamable HTTP MCP URL (`RESOLVED_MCP_HTTP_URL`). */
63
66
  mcpHttpUrl: string;
67
+ /**
68
+ * Restrict the exposed tool groups. Omitted → each client's existing default
69
+ * (the lean `DEFAULT_FEATURE_GROUPS` for stdio deeplinks; all tools for the
70
+ * config-json clients that historically set no `MUSHI_FEATURES`). The public
71
+ * keyless demo passes an explicit read-only subset here.
72
+ */
73
+ features?: readonly FeatureGroup[];
74
+ /**
75
+ * Hosted HTTP transport only: append `?read_only=1` so the server hides and
76
+ * blocks write tools regardless of key scope. Used by the public demo on top
77
+ * of an `mcp:read` key for defence in depth. No-op for stdio configs.
78
+ */
79
+ readOnly?: boolean;
64
80
  }
65
81
  type McpBuildResult = {
66
82
  kind: 'deeplink';
@@ -91,7 +107,7 @@ interface McpClientDef {
91
107
  /** Build a stable, unique, human-readable MCP server slug for a project. */
92
108
  declare function projectServerName(projectId: string, projectName: string): string;
93
109
  declare function buildStdioConfigObj(projectId: string | undefined, apiKey: string, apiEndpoint: string, features?: readonly string[]): StdioConfig;
94
- declare function buildHttpConfigObj(projectId: string | undefined, apiKey: string, mcpHttpUrl: string, features?: readonly string[]): HttpConfig;
110
+ declare function buildHttpConfigObj(projectId: string | undefined, apiKey: string, mcpHttpUrl: string, features?: readonly string[], readOnly?: boolean): HttpConfig;
95
111
  declare const MCP_CLIENTS: McpClientDef[];
96
112
  /** Look up a client by id — throws if not found. */
97
113
  declare function getMcpClient(id: McpClientId): McpClientDef;
package/dist/clients.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  MUSHI_ICON_PNG_URL
3
- } from "./chunk-EHB2BGWX.js";
3
+ } from "./chunk-EANW5YTI.js";
4
4
  import {
5
5
  DEFAULT_FEATURE_GROUPS,
6
6
  appendFeaturesToUrl,
7
7
  featuresQueryString
8
- } from "./chunk-ZX3XIJ7S.js";
8
+ } from "./chunk-PW4AK6SV.js";
9
9
 
10
10
  // src/clients.ts
11
11
  function projectServerName(projectId, projectName) {
@@ -27,8 +27,17 @@ function buildStdioConfigObj(projectId, apiKey, apiEndpoint, features = DEFAULT_
27
27
  icon: MUSHI_ICON_PNG_URL
28
28
  };
29
29
  }
30
- function buildHttpConfigObj(projectId, apiKey, mcpHttpUrl, features = DEFAULT_FEATURE_GROUPS) {
31
- const url = appendFeaturesToUrl(mcpHttpUrl, features);
30
+ function withReadOnly(baseUrl, readOnly) {
31
+ if (!readOnly) return baseUrl;
32
+ const url = new URL(baseUrl);
33
+ url.searchParams.set("read_only", "1");
34
+ return url.toString();
35
+ }
36
+ function buildHttpConfigObj(projectId, apiKey, mcpHttpUrl, features = DEFAULT_FEATURE_GROUPS, readOnly = false) {
37
+ const url = withReadOnly(
38
+ appendFeaturesToUrl(mcpHttpUrl, features),
39
+ readOnly
40
+ );
32
41
  const headers = {
33
42
  Authorization: `Bearer ${apiKey}`,
34
43
  "X-Mushi-Api-Key": apiKey
@@ -57,9 +66,9 @@ var MCP_CLIENTS = [
57
66
  method: "deeplink",
58
67
  transport: "stdio",
59
68
  cliIde: "cursor",
60
- build({ projectId, projectName, apiKey, endpoint }) {
69
+ build({ projectId, projectName, apiKey, endpoint, features }) {
61
70
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
62
- const config = buildStdioConfigObj(projectId, apiKey, endpoint);
71
+ const config = buildStdioConfigObj(projectId, apiKey, endpoint, features);
63
72
  return { kind: "deeplink", url: encodeCursorDeeplink(name, config) };
64
73
  }
65
74
  },
@@ -70,9 +79,9 @@ var MCP_CLIENTS = [
70
79
  description: "One-click install via VS Code MCP extension",
71
80
  method: "deeplink",
72
81
  transport: "stdio",
73
- build({ projectId, projectName, apiKey, endpoint }) {
82
+ build({ projectId, projectName, apiKey, endpoint, features }) {
74
83
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
75
- const config = buildStdioConfigObj(projectId, apiKey, endpoint);
84
+ const config = buildStdioConfigObj(projectId, apiKey, endpoint, features);
76
85
  return { kind: "deeplink", url: encodeVsCodeDeeplink("vscode", name, config) };
77
86
  }
78
87
  },
@@ -83,9 +92,9 @@ var MCP_CLIENTS = [
83
92
  description: "One-click install via VS Code Insiders MCP extension",
84
93
  method: "deeplink",
85
94
  transport: "stdio",
86
- build({ projectId, projectName, apiKey, endpoint }) {
95
+ build({ projectId, projectName, apiKey, endpoint, features }) {
87
96
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
88
- const config = buildStdioConfigObj(projectId, apiKey, endpoint);
97
+ const config = buildStdioConfigObj(projectId, apiKey, endpoint, features);
89
98
  return { kind: "deeplink", url: encodeVsCodeDeeplink("vscode-insiders", name, config) };
90
99
  }
91
100
  },
@@ -96,9 +105,12 @@ var MCP_CLIENTS = [
96
105
  description: "Copy config into ~/.codeium/windsurf/mcp_config.json",
97
106
  method: "config-json",
98
107
  transport: "http",
99
- build({ projectId, projectName, apiKey, mcpHttpUrl }) {
108
+ build({ projectId, projectName, apiKey, mcpHttpUrl, features, readOnly }) {
100
109
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
101
- const url = appendFeaturesToUrl(mcpHttpUrl, DEFAULT_FEATURE_GROUPS);
110
+ const url = withReadOnly(
111
+ appendFeaturesToUrl(mcpHttpUrl, features ?? DEFAULT_FEATURE_GROUPS),
112
+ readOnly
113
+ );
102
114
  const headers = {
103
115
  Authorization: `Bearer ${apiKey}`,
104
116
  "X-Mushi-Api-Key": apiKey
@@ -124,12 +136,13 @@ var MCP_CLIENTS = [
124
136
  description: "Copy config into cline_mcp_settings.json",
125
137
  method: "config-json",
126
138
  transport: "stdio",
127
- build({ projectId, projectName, apiKey, endpoint }) {
139
+ build({ projectId, projectName, apiKey, endpoint, features }) {
128
140
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
129
141
  const env = {
130
142
  MUSHI_API_ENDPOINT: endpoint,
131
143
  MUSHI_API_KEY: apiKey
132
144
  };
145
+ if (features) env.MUSHI_FEATURES = featuresQueryString(features);
133
146
  if (projectId) env.MUSHI_PROJECT_ID = projectId;
134
147
  const clineConfig = {
135
148
  mcpServers: {
@@ -152,9 +165,12 @@ var MCP_CLIENTS = [
152
165
  method: "cli-command",
153
166
  transport: "http",
154
167
  cliIde: "claude",
155
- build({ projectId, projectName, apiKey, mcpHttpUrl }) {
168
+ build({ projectId, projectName, apiKey, mcpHttpUrl, features, readOnly }) {
156
169
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
157
- const url = appendFeaturesToUrl(mcpHttpUrl, DEFAULT_FEATURE_GROUPS);
170
+ const url = withReadOnly(
171
+ appendFeaturesToUrl(mcpHttpUrl, features ?? DEFAULT_FEATURE_GROUPS),
172
+ readOnly
173
+ );
158
174
  const cmd = `claude mcp add --transport http ${name} "${url}" --header "Authorization: Bearer ${apiKey}"`;
159
175
  return { kind: "command", text: cmd };
160
176
  }
@@ -166,12 +182,13 @@ var MCP_CLIENTS = [
166
182
  description: "Copy config into claude_desktop_config.json",
167
183
  method: "config-json",
168
184
  transport: "stdio",
169
- build({ projectId, projectName, apiKey, endpoint }) {
185
+ build({ projectId, projectName, apiKey, endpoint, features }) {
170
186
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
171
187
  const env = {
172
188
  MUSHI_API_ENDPOINT: endpoint,
173
189
  MUSHI_API_KEY: apiKey
174
190
  };
191
+ if (features) env.MUSHI_FEATURES = featuresQueryString(features);
175
192
  if (projectId) env.MUSHI_PROJECT_ID = projectId;
176
193
  const desktopConfig = {
177
194
  mcpServers: {
@@ -193,12 +210,13 @@ var MCP_CLIENTS = [
193
210
  method: "config-json",
194
211
  transport: "stdio",
195
212
  cliIde: "zed",
196
- build({ projectId, projectName, apiKey, endpoint }) {
213
+ build({ projectId, projectName, apiKey, endpoint, features }) {
197
214
  const name = projectId ? projectServerName(projectId, projectName) : `mushi-${projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24)}`;
198
215
  const env = {
199
216
  MUSHI_API_ENDPOINT: endpoint,
200
217
  MUSHI_API_KEY: apiKey
201
218
  };
219
+ if (features) env.MUSHI_FEATURES = featuresQueryString(features);
202
220
  if (projectId) env.MUSHI_PROJECT_ID = projectId;
203
221
  const zedConfig = {
204
222
  context_servers: {
@@ -226,8 +244,11 @@ var MCP_CLIENTS = [
226
244
  description: "Streamable HTTP URL \u2014 works with OpenClaw, Hermes, Gemini CLI, ChatGPT, and more",
227
245
  method: "remote-url",
228
246
  transport: "http",
229
- build({ projectId, apiKey, mcpHttpUrl }) {
230
- const url = appendFeaturesToUrl(mcpHttpUrl, DEFAULT_FEATURE_GROUPS);
247
+ build({ projectId, apiKey, mcpHttpUrl, features, readOnly }) {
248
+ const url = withReadOnly(
249
+ appendFeaturesToUrl(mcpHttpUrl, features ?? DEFAULT_FEATURE_GROUPS),
250
+ readOnly
251
+ );
231
252
  const headerSnippet = [
232
253
  `Authorization: Bearer ${apiKey}`,
233
254
  projectId ? `X-Mushi-Project-Id: ${projectId}` : ""
@@ -4,7 +4,7 @@
4
4
  * When `features` is omitted → all tools (backward compatible).
5
5
  * New installs default to DEFAULT_FEATURE_GROUPS via deeplink / docs.
6
6
  */
7
- declare const FEATURE_GROUPS: readonly ["triage", "fixes", "inventory", "setup", "qa", "skills", "rewards", "admin", "audit", "docs", "usage", "legacy"];
7
+ declare const FEATURE_GROUPS: readonly ["triage", "fixes", "inventory", "setup", "qa", "skills", "rewards", "admin", "audit", "docs", "usage", "usage", "legacy"];
8
8
  type FeatureGroup = (typeof FEATURE_GROUPS)[number];
9
9
  /** Lean default for new Cursor installs (~45 tools vs 70 full). */
10
10
  declare const DEFAULT_FEATURE_GROUPS: readonly FeatureGroup[];
@@ -9,7 +9,7 @@ import {
9
9
  parseFeaturesCsv,
10
10
  parseFeaturesParam,
11
11
  toolMatchesFeatures
12
- } from "./chunk-ZX3XIJ7S.js";
12
+ } from "./chunk-PW4AK6SV.js";
13
13
  export {
14
14
  ALL_FEATURE_GROUPS,
15
15
  DEFAULT_FEATURE_GROUPS,
package/dist/index.js CHANGED
@@ -11,10 +11,10 @@ var TOOL_CATALOG = [
11
11
  {
12
12
  name: "get_recent_reports",
13
13
  title: "Recent bug reports",
14
- description: "List recent bug reports with optional filters (status / category / severity). Use this to survey what the triage queue looks like right now.",
14
+ description: "List recent bug reports with optional filters (status / category / severity). Use this to see what is waiting in your reports inbox right now.",
15
15
  scope: "mcp:read",
16
16
  hints: { readOnly: true, idempotent: true, openWorld: true },
17
- useCase: "What landed in my triage queue today?"
17
+ useCase: "What landed in my bug queue today?"
18
18
  },
19
19
  {
20
20
  name: "get_report_detail",
@@ -141,7 +141,7 @@ var TOOL_CATALOG = [
141
141
  {
142
142
  name: "setup_check",
143
143
  title: "Dispatch preflight check",
144
- description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] 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.",
144
+ description: "[Deprecated \u2014 use diagnose_setup with mode=dispatch or diagnose_connection.] Run the 4 **dispatch-readiness** checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, your Anthropic API key present, autofix enabled). Also returns the target repo URL when GitHub is connected.",
145
145
  scope: "mcp:read",
146
146
  hints: { readOnly: true, idempotent: true, openWorld: true },
147
147
  useCase: "Is this project ready to auto-fix bugs? What is blocking dispatch?"
@@ -191,7 +191,7 @@ var TOOL_CATALOG = [
191
191
  {
192
192
  name: "dispatch_fix",
193
193
  title: "Dispatch Mushi fix agent",
194
- description: 'Dispatch the Mushi agentic fix orchestrator for a classified report. Set agent="cursor_cloud" to dispatch a Cursor Cloud Agent that opens a signed draft PR. Returns a fix_attempt id; poll get_fix_timeline for progress.',
194
+ description: 'Start the Mushi fix agent for a classified report. Set agent="cursor_cloud" to dispatch a Cursor Cloud Agent that opens a signed draft PR. Returns a fix_attempt id; poll get_fix_timeline for progress.',
195
195
  scope: "mcp:write",
196
196
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
197
197
  useCase: "Let the in-repo agent attempt this fix for me (or: dispatch a Cursor Cloud Agent)."
@@ -207,7 +207,7 @@ var TOOL_CATALOG = [
207
207
  {
208
208
  name: "test_gen_from_report",
209
209
  title: "Generate Playwright test from report",
210
- description: "POST to inventory test-gen: uses the project BYOK LLM to author a Playwright spec from a classified report and opens a draft PR (internal service orchestration). Requires inventory_v2 + GitHub + LLM keys.",
210
+ description: "Generate a Playwright spec from a classified report using your project LLM key and open a draft PR. Requires inventory_v2 + GitHub + LLM keys.",
211
211
  scope: "mcp:write",
212
212
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
213
213
  useCase: "Turn this regression report into an E2E test PR."
@@ -239,7 +239,7 @@ var TOOL_CATALOG = [
239
239
  {
240
240
  name: "reopen_report",
241
241
  title: "Reopen report (operator)",
242
- description: "Move a report to reopened for regression triage.",
242
+ description: "Move a report to reopened for regression review.",
243
243
  scope: "mcp:write",
244
244
  hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
245
245
  useCase: "Reopen a regression the reporter flagged as not fixed."
@@ -297,10 +297,10 @@ var TOOL_CATALOG = [
297
297
  {
298
298
  name: "get_project_context",
299
299
  title: "Project context snapshot",
300
- description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness, BYOK LLM config, plan tier, and active integration health. Equivalent to a merged preflight + activation + settings read. Agents should call this at the start of a triage session to orient themselves.",
300
+ description: "Return a rich context snapshot for a project: name, repo URL, SDK heartbeat, ingest status, open report count, autofix readiness, your LLM key config, plan tier, and active integration health. Equivalent to a merged preflight + activation + settings read. Agents should call this at the start of a review session to orient themselves.",
301
301
  scope: "mcp:read",
302
302
  hints: { readOnly: true, idempotent: true, openWorld: true },
303
- useCase: "Give me a status overview of this project before I start triaging."
303
+ useCase: "Give me a status overview of this project before I start reviewing bugs."
304
304
  },
305
305
  {
306
306
  name: "get_pipeline_logs",
@@ -321,7 +321,7 @@ var TOOL_CATALOG = [
321
321
  {
322
322
  name: "triage_issue",
323
323
  title: "Triage issue end-to-end",
324
- description: 'Read-only orchestration tool: combines report detail, evidence, similar bugs, fix context, blast radius, and recent pipeline logs for a report into a single structured triage packet. Returns a prioritised list of recommended next actions (investigate, dispatch_fix, group_with, dismiss). Equivalent to a Sentry "Analyze with Seer" flow grounded in user-felt reports. Pass report_id to kick off triage. Call this before dispatch_fix.',
324
+ description: 'Read-only combined tool: merges report detail, evidence, similar bugs, fix context, blast radius, and recent pipeline logs for a report into a single structured review packet. Returns a prioritised list of recommended next actions (investigate, dispatch_fix, group_with, dismiss). Equivalent to a Sentry "Analyze with Seer" flow grounded in user-felt reports. Pass report_id to kick off review. Call this before dispatch_fix.',
325
325
  scope: "mcp:read",
326
326
  hints: { readOnly: true, idempotent: true, openWorld: true },
327
327
  useCase: "Analyze this bug report end-to-end and tell me what to do."
@@ -371,8 +371,8 @@ var TDD_TOOL_CATALOG = [
371
371
  },
372
372
  {
373
373
  name: "improve_qa_story",
374
- title: "PDCA auto-improve a failing QA story",
375
- description: "Trigger the PDCA improver for a specific project. Finds recently failed qa_story_runs and uses Claude to write improved test scripts. New tests are created with source=pdca and approval gated by the original story's automation_mode.",
374
+ title: "Auto-improve a failing QA story",
375
+ description: "Find recently failed qa_story_runs and use Claude to write improved test scripts. New tests are created with source=pdca and approval gated by the original story's automation_mode.",
376
376
  scope: "mcp:write",
377
377
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
378
378
  useCase: "Fix my failing QA tests automatically."
@@ -387,16 +387,16 @@ var TDD_TOOL_CATALOG = [
387
387
  },
388
388
  {
389
389
  name: "list_byok_keys",
390
- title: "List BYOK API key pool",
391
- description: "List all BYOK API keys for the project, grouped by provider. Shows label, priority, status, and cooldown. Never returns the raw key value \u2014 only metadata. Use this to see which keys are active or exhausted.",
390
+ title: "List your API key pool",
391
+ description: "List all project API keys, grouped by provider. Shows label, priority, status, and cooldown. Never returns the raw key value \u2014 only metadata. Use this to see which keys are active or exhausted.",
392
392
  scope: "mcp:read",
393
393
  hints: { readOnly: true, idempotent: true, openWorld: true },
394
394
  useCase: "Which API keys are active and which are rate-limited?"
395
395
  },
396
396
  {
397
397
  name: "add_byok_key",
398
- title: "Add a BYOK API key",
399
- description: "Add a new API key to the project's BYOK pool for a given provider (anthropic, openai, firecrawl, browserbase, cursor). Specify label and priority for ordering. The key is stored encrypted in Supabase Vault.",
398
+ title: "Add an API key",
399
+ description: "Add a new API key to the project pool for a given provider (anthropic, openai, firecrawl, browserbase, cursor). Specify label and priority for ordering. The key is stored encrypted in Supabase Vault.",
400
400
  scope: "mcp:write",
401
401
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
402
402
  useCase: "Add a backup Anthropic key to the pool."
@@ -545,7 +545,7 @@ var CODEBASE_TOOL_CATALOG = [
545
545
  {
546
546
  name: "ask_codebase",
547
547
  title: "Ask about the indexed codebase",
548
- description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and an Anthropic or OpenAI BYOK key.",
548
+ description: "Ask a plain-English question about the connected repo. Grounds on pgvector retrieval over project_codebase_files and returns an answer with file:line citations. Requires codebase indexing enabled and your Anthropic or OpenAI API key.",
549
549
  scope: "mcp:write",
550
550
  hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
551
551
  useCase: "How does authentication work in this repo?"
@@ -613,6 +613,7 @@ var FEATURE_GROUPS = [
613
613
  "audit",
614
614
  "docs",
615
615
  "usage",
616
+ "usage",
616
617
  /** Deprecated aliases kept for one release — hidden unless `legacy` or `all`. */
617
618
  "legacy"
618
619
  ];
@@ -857,6 +858,7 @@ function searchMushiDocs(query, limit = 8) {
857
858
  }
858
859
 
859
860
  // src/server.ts
861
+ var NO_ARG_INPUT = {};
860
862
  var MushiApiError = class extends Error {
861
863
  constructor(status, code, message) {
862
864
  super(`[${code}] ${message}`);
@@ -1093,7 +1095,7 @@ function createMushiServer(config) {
1093
1095
  title: titleOf("get_two_way_comms_health"),
1094
1096
  description: descOf("get_two_way_comms_health"),
1095
1097
  annotations: annotationsFor("get_two_way_comms_health"),
1096
- inputSchema: {}
1098
+ inputSchema: NO_ARG_INPUT
1097
1099
  },
1098
1100
  async () => jsonText(await apiCall("/v1/sync/two-way-health"))
1099
1101
  );
@@ -1400,7 +1402,7 @@ function createMushiServer(config) {
1400
1402
  title: titleOf("ingest_setup_check"),
1401
1403
  description: descOf("ingest_setup_check"),
1402
1404
  annotations: annotationsFor("ingest_setup_check"),
1403
- inputSchema: {}
1405
+ inputSchema: NO_ARG_INPUT
1404
1406
  },
1405
1407
  async () => {
1406
1408
  const data = await apiCall("/v1/sync/ingest-setup");
@@ -1494,9 +1496,11 @@ function createMushiServer(config) {
1494
1496
  const failed = preflight.checks.filter((c) => !c.ready);
1495
1497
  dispatchBlock = failed[0]?.hint;
1496
1498
  }
1497
- } catch {
1499
+ } catch (err) {
1498
1500
  dispatchReady = false;
1499
- dispatchBlock = "Could not run dispatch preflight \u2014 verify project_id and API key scope.";
1501
+ const detail = err instanceof Error ? err.message : String(err);
1502
+ dispatchBlock = `Could not run dispatch preflight \u2014 ${detail}`;
1503
+ apiLog.warn("diagnose_setup dispatch preflight failed", { error: detail });
1500
1504
  }
1501
1505
  }
1502
1506
  const ingestFailed = ingest.steps.filter((s) => s.required && !s.complete);
@@ -1520,7 +1524,7 @@ function createMushiServer(config) {
1520
1524
  title: titleOf("diagnose_connection"),
1521
1525
  description: descOf("diagnose_connection"),
1522
1526
  annotations: annotationsFor("diagnose_connection"),
1523
- inputSchema: {},
1527
+ inputSchema: NO_ARG_INPUT,
1524
1528
  outputSchema: {
1525
1529
  ready: z.boolean(),
1526
1530
  healthOk: z.boolean(),
@@ -1673,7 +1677,7 @@ function createMushiServer(config) {
1673
1677
  title: titleOf("list_projects"),
1674
1678
  description: descOf("list_projects"),
1675
1679
  annotations: annotationsFor("list_projects"),
1676
- inputSchema: {},
1680
+ inputSchema: NO_ARG_INPUT,
1677
1681
  outputSchema: {
1678
1682
  projects: z.array(z.unknown()),
1679
1683
  total: z.number().optional(),
@@ -1694,7 +1698,7 @@ function createMushiServer(config) {
1694
1698
  title: titleOf("get_account_overview"),
1695
1699
  description: descOf("get_account_overview"),
1696
1700
  annotations: annotationsFor("get_account_overview"),
1697
- inputSchema: {},
1701
+ inputSchema: NO_ARG_INPUT,
1698
1702
  outputSchema: {
1699
1703
  projects: z.array(z.unknown()),
1700
1704
  total: z.number(),
@@ -1813,7 +1817,7 @@ function createMushiServer(config) {
1813
1817
  description: descOf("triage_issue"),
1814
1818
  annotations: annotationsFor("triage_issue"),
1815
1819
  inputSchema: {
1816
- report_id: z.string().describe("Report UUID to triage."),
1820
+ report_id: z.string().uuid().describe("Report UUID to triage."),
1817
1821
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
1818
1822
  include_logs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
1819
1823
  },
@@ -1874,11 +1878,31 @@ function createMushiServer(config) {
1874
1878
  args: { reportId: args.report_id }
1875
1879
  });
1876
1880
  }
1881
+ const partial_errors = [];
1882
+ if (reportRes.status === "rejected") {
1883
+ partial_errors.push(`report: ${String(reportRes.reason)}`);
1884
+ }
1885
+ if (evidenceRes.status === "rejected") {
1886
+ partial_errors.push(`timeline: ${String(evidenceRes.reason)}`);
1887
+ }
1888
+ if (similarRes.status === "rejected") {
1889
+ partial_errors.push(`similarity: ${String(similarRes.reason)}`);
1890
+ }
1891
+ if (fixCtxRes.status === "rejected") {
1892
+ partial_errors.push(`fix_context: ${String(fixCtxRes.reason)}`);
1893
+ }
1894
+ if (blastRes.status === "rejected") {
1895
+ partial_errors.push(`blast_radius: ${String(blastRes.reason)}`);
1896
+ }
1897
+ if (includeLogs && logsRes.status === "rejected") {
1898
+ partial_errors.push(`recent_logs: ${String(logsRes.reason)}`);
1899
+ }
1877
1900
  const result = {
1878
1901
  report_id: args.report_id,
1879
1902
  severity,
1880
1903
  category,
1881
1904
  status,
1905
+ partial_errors,
1882
1906
  report: reportRes.status === "fulfilled" ? reportRes.value : { error: String(reportRes.reason) },
1883
1907
  reporter_thread: evidenceRes.status === "fulfilled" ? evidenceRes.value : null,
1884
1908
  similar_bugs: similarRes.status === "fulfilled" ? similarRes.value : null,
@@ -1943,7 +1967,7 @@ function createMushiServer(config) {
1943
1967
  description: descOf("dispatch_fix"),
1944
1968
  annotations: annotationsFor("dispatch_fix"),
1945
1969
  inputSchema: {
1946
- reportId: z.string().describe("Report UUID to fix"),
1970
+ reportId: z.string().uuid().describe("Report UUID to fix"),
1947
1971
  agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
1948
1972
  idempotencyKey: z.string().uuid().optional().describe("Optional RFC 4122 UUID. Resend the same key to safely retry without dispatching a duplicate fix job (Idempotency-Key IETF draft)."),
1949
1973
  inventoryActionNodeId: z.string().uuid().optional().describe("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.")
@@ -3110,6 +3134,7 @@ Be specific. No generic advice.`
3110
3134
  }
3111
3135
 
3112
3136
  // src/index.ts
3137
+ import * as Sentry from "@sentry/node";
3113
3138
  var _writeStderr = (...args) => process.stderr.write(
3114
3139
  args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") + "\n"
3115
3140
  );
@@ -3125,6 +3150,14 @@ var SCOPES_RAW = process.env.MUSHI_SCOPES ?? "";
3125
3150
  var parsedScopes = SCOPES_RAW ? SCOPES_RAW.split(",").map((s) => s.trim()).filter((s) => s === "mcp:read" || s === "mcp:write") : ALL_SCOPES;
3126
3151
  var SCOPES = SCOPES_RAW && parsedScopes.length === 0 ? ALL_SCOPES : parsedScopes;
3127
3152
  var FEATURES = parseFeaturesCsv(process.env.MUSHI_FEATURES);
3153
+ var MCP_SENTRY_DSN = process.env.MUSHI_MCP_SENTRY_DSN?.trim();
3154
+ if (MCP_SENTRY_DSN) {
3155
+ Sentry.init({
3156
+ dsn: MCP_SENTRY_DSN,
3157
+ environment: process.env.MUSHI_SENTRY_ENVIRONMENT ?? process.env.NODE_ENV ?? "development",
3158
+ tracesSampleRate: 0
3159
+ });
3160
+ }
3128
3161
  async function main() {
3129
3162
  if (!API_KEY) {
3130
3163
  log.fatal("MUSHI_API_KEY environment variable is required");
@@ -3180,7 +3213,8 @@ async function main() {
3180
3213
  }
3181
3214
  lastInventoryAt = updatedAt;
3182
3215
  }
3183
- } catch {
3216
+ } catch (pollErr) {
3217
+ log.debug("inventory poll failed", { err: String(pollErr) });
3184
3218
  }
3185
3219
  };
3186
3220
  void pollInventory();
package/dist/server.js CHANGED
@@ -2,13 +2,13 @@ import {
2
2
  CODEBASE_TOOL_CATALOG,
3
3
  TDD_TOOL_CATALOG,
4
4
  TOOL_CATALOG
5
- } from "./chunk-MM3IMFM4.js";
5
+ } from "./chunk-7UOTJ2LI.js";
6
6
  import {
7
7
  MUSHI_SERVER_METADATA
8
- } from "./chunk-EHB2BGWX.js";
8
+ } from "./chunk-EANW5YTI.js";
9
9
  import {
10
10
  toolMatchesFeatures
11
- } from "./chunk-ZX3XIJ7S.js";
11
+ } from "./chunk-PW4AK6SV.js";
12
12
 
13
13
  // src/server.ts
14
14
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -123,6 +123,7 @@ function searchMushiDocs(query, limit = 8) {
123
123
  }
124
124
 
125
125
  // src/server.ts
126
+ var NO_ARG_INPUT = {};
126
127
  var MushiApiError = class extends Error {
127
128
  constructor(status, code, message) {
128
129
  super(`[${code}] ${message}`);
@@ -359,7 +360,7 @@ function createMushiServer(config) {
359
360
  title: titleOf("get_two_way_comms_health"),
360
361
  description: descOf("get_two_way_comms_health"),
361
362
  annotations: annotationsFor("get_two_way_comms_health"),
362
- inputSchema: {}
363
+ inputSchema: NO_ARG_INPUT
363
364
  },
364
365
  async () => jsonText(await apiCall("/v1/sync/two-way-health"))
365
366
  );
@@ -666,7 +667,7 @@ function createMushiServer(config) {
666
667
  title: titleOf("ingest_setup_check"),
667
668
  description: descOf("ingest_setup_check"),
668
669
  annotations: annotationsFor("ingest_setup_check"),
669
- inputSchema: {}
670
+ inputSchema: NO_ARG_INPUT
670
671
  },
671
672
  async () => {
672
673
  const data = await apiCall("/v1/sync/ingest-setup");
@@ -760,9 +761,11 @@ function createMushiServer(config) {
760
761
  const failed = preflight.checks.filter((c) => !c.ready);
761
762
  dispatchBlock = failed[0]?.hint;
762
763
  }
763
- } catch {
764
+ } catch (err) {
764
765
  dispatchReady = false;
765
- dispatchBlock = "Could not run dispatch preflight \u2014 verify project_id and API key scope.";
766
+ const detail = err instanceof Error ? err.message : String(err);
767
+ dispatchBlock = `Could not run dispatch preflight \u2014 ${detail}`;
768
+ apiLog.warn("diagnose_setup dispatch preflight failed", { error: detail });
766
769
  }
767
770
  }
768
771
  const ingestFailed = ingest.steps.filter((s) => s.required && !s.complete);
@@ -786,7 +789,7 @@ function createMushiServer(config) {
786
789
  title: titleOf("diagnose_connection"),
787
790
  description: descOf("diagnose_connection"),
788
791
  annotations: annotationsFor("diagnose_connection"),
789
- inputSchema: {},
792
+ inputSchema: NO_ARG_INPUT,
790
793
  outputSchema: {
791
794
  ready: z.boolean(),
792
795
  healthOk: z.boolean(),
@@ -939,7 +942,7 @@ function createMushiServer(config) {
939
942
  title: titleOf("list_projects"),
940
943
  description: descOf("list_projects"),
941
944
  annotations: annotationsFor("list_projects"),
942
- inputSchema: {},
945
+ inputSchema: NO_ARG_INPUT,
943
946
  outputSchema: {
944
947
  projects: z.array(z.unknown()),
945
948
  total: z.number().optional(),
@@ -960,7 +963,7 @@ function createMushiServer(config) {
960
963
  title: titleOf("get_account_overview"),
961
964
  description: descOf("get_account_overview"),
962
965
  annotations: annotationsFor("get_account_overview"),
963
- inputSchema: {},
966
+ inputSchema: NO_ARG_INPUT,
964
967
  outputSchema: {
965
968
  projects: z.array(z.unknown()),
966
969
  total: z.number(),
@@ -1079,7 +1082,7 @@ function createMushiServer(config) {
1079
1082
  description: descOf("triage_issue"),
1080
1083
  annotations: annotationsFor("triage_issue"),
1081
1084
  inputSchema: {
1082
- report_id: z.string().describe("Report UUID to triage."),
1085
+ report_id: z.string().uuid().describe("Report UUID to triage."),
1083
1086
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
1084
1087
  include_logs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
1085
1088
  },
@@ -1140,11 +1143,31 @@ function createMushiServer(config) {
1140
1143
  args: { reportId: args.report_id }
1141
1144
  });
1142
1145
  }
1146
+ const partial_errors = [];
1147
+ if (reportRes.status === "rejected") {
1148
+ partial_errors.push(`report: ${String(reportRes.reason)}`);
1149
+ }
1150
+ if (evidenceRes.status === "rejected") {
1151
+ partial_errors.push(`timeline: ${String(evidenceRes.reason)}`);
1152
+ }
1153
+ if (similarRes.status === "rejected") {
1154
+ partial_errors.push(`similarity: ${String(similarRes.reason)}`);
1155
+ }
1156
+ if (fixCtxRes.status === "rejected") {
1157
+ partial_errors.push(`fix_context: ${String(fixCtxRes.reason)}`);
1158
+ }
1159
+ if (blastRes.status === "rejected") {
1160
+ partial_errors.push(`blast_radius: ${String(blastRes.reason)}`);
1161
+ }
1162
+ if (includeLogs && logsRes.status === "rejected") {
1163
+ partial_errors.push(`recent_logs: ${String(logsRes.reason)}`);
1164
+ }
1143
1165
  const result = {
1144
1166
  report_id: args.report_id,
1145
1167
  severity,
1146
1168
  category,
1147
1169
  status,
1170
+ partial_errors,
1148
1171
  report: reportRes.status === "fulfilled" ? reportRes.value : { error: String(reportRes.reason) },
1149
1172
  reporter_thread: evidenceRes.status === "fulfilled" ? evidenceRes.value : null,
1150
1173
  similar_bugs: similarRes.status === "fulfilled" ? similarRes.value : null,
@@ -1209,7 +1232,7 @@ function createMushiServer(config) {
1209
1232
  description: descOf("dispatch_fix"),
1210
1233
  annotations: annotationsFor("dispatch_fix"),
1211
1234
  inputSchema: {
1212
- reportId: z.string().describe("Report UUID to fix"),
1235
+ reportId: z.string().uuid().describe("Report UUID to fix"),
1213
1236
  agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
1214
1237
  idempotencyKey: z.string().uuid().optional().describe("Optional RFC 4122 UUID. Resend the same key to safely retry without dispatching a duplicate fix job (Idempotency-Key IETF draft)."),
1215
1238
  inventoryActionNodeId: z.string().uuid().optional().describe("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.")
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mushi-mushi/mcp",
3
3
  "mcpName": "io.github.kensaurus/mushi-mushi",
4
- "version": "0.17.0",
4
+ "version": "0.17.1",
5
5
  "license": "MIT",
6
6
  "description": "MCP server for Mushi Mushi — triage user-felt bugs, pull evidence (screenshots, console logs, network requests), check blast radius, and dispatch fix PRs, all from your AI coding agent",
7
7
  "type": "module",
@@ -46,8 +46,9 @@
46
46
  ],
47
47
  "dependencies": {
48
48
  "@modelcontextprotocol/sdk": "^1.29.0",
49
+ "@sentry/node": "^10.57.0",
49
50
  "zod": "^4.4.3",
50
- "@mushi-mushi/core": "^1.21.0"
51
+ "@mushi-mushi/core": "^1.22.1"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/node": "^22.19.21",