@mushi-mushi/mcp 0.15.0 → 0.16.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/CONTRIBUTING.md CHANGED
@@ -58,12 +58,12 @@ packages/
58
58
  react-native/ React Native SDK (MIT)
59
59
  cli/ CLI tool (MIT)
60
60
  mcp/ MCP server for coding agents (MIT)
61
- server/ Supabase Edge Functions (BSL)
62
- agents/ Agentic fix pipeline (BSL)
63
- verify/ Fix verification (BSL)
61
+ server/ Supabase Edge Functions (AGPLv3)
62
+ agents/ Agentic fix pipeline (AGPLv3)
63
+ verify/ Fix verification (AGPLv3)
64
64
  apps/
65
65
  admin/ Admin dashboard (React + Tailwind)
66
- docs/ Documentation site (planned)
66
+ docs/ Documentation site (Nextra — apps/docs)
67
67
  tooling/
68
68
  eslint-config/ Shared ESLint flat config
69
69
  tsconfig/ Shared TypeScript configs
@@ -81,6 +81,30 @@ tooling/
81
81
  ```
82
82
  6. Open a pull request
83
83
 
84
+ ### Documentation (docs-as-code)
85
+
86
+ If your PR changes **user-visible SDK or CLI behavior**, update docs in the **same PR**:
87
+
88
+ | Surface | When to update |
89
+ | --- | --- |
90
+ | `packages/*/README.md` | npm-facing install/API/config changes for that package |
91
+ | `apps/docs/content/**/*.mdx` | Long-form guides, quickstarts, admin console docs |
92
+ | Root `README.md` | Marketing stats only when counts change — run `pnpm check:docs-stats` |
93
+
94
+ Guardrails (run locally before pushing):
95
+
96
+ ```bash
97
+ pnpm check:docs-stats # root README stat badges
98
+ pnpm check:sdk-version-matrix # apps/docs/content/sdks/index.mdx versions
99
+ pnpm check:internal-doc-links # dead /foo links in MDX
100
+ pnpm check:onboarding-drift # phantom MCP env vars / stale tool names
101
+ pnpm check:bundle-docs # @mushi-mushi/web size-limit vs README
102
+ pnpm gen:llms-txt # regenerate apps/docs/public/llms.txt after nav changes
103
+ pnpm gen:mcp-tools-doc # regenerate sdks/mcp-tools.generated.mdx from catalog.ts
104
+ ```
105
+
106
+ README changes on npm packages only appear on [npmjs.com](https://www.npmjs.com) after the next **Changesets publish**.
107
+
84
108
  ## Changesets
85
109
 
86
110
  We use [Changesets](https://github.com/changesets/changesets) for versioning. If your PR modifies a published package (`core`, `web`, `react`, `vue`, `svelte`, `angular`, `react-native`, `cli`, `mcp`), add a changeset:
@@ -93,13 +117,30 @@ Select the affected packages, the semver bump type, and write a summary. The cha
93
117
 
94
118
  ## Release flow
95
119
 
96
- Releases are fully automated. Maintainers don't run `npm publish` by hand.
120
+ Releases are fully automated once the version PR is merged. Maintainers don't run
121
+ `npm publish` by hand for routine bumps. See **[`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md)**
122
+ for the full production runbook (edge functions, admin SPA, docs site, migrations).
97
123
 
98
124
  1. PRs land on `master` with one or more changeset files in `.changeset/`.
99
- 2. `release.yml` runs on every push to `master`. It opens (or updates) a `chore: version packages` PR that bumps every affected `package.json`, rolls up the changelogs, and deletes the consumed changesets.
100
- 3. Merging that "Version Packages" PR re-fires `release.yml`. The publish step authenticates to npm via **OpenID Connect (OIDC) Trusted Publishers** — no long-lived `NPM_TOKEN` is exchanged — and every tarball ships with a **Sigstore provenance attestation** uploaded to the public transparency log.
125
+ Orphaned changesets (only ignored packages) fail `pnpm check:changeset-orphans`.
126
+ 2. `release.yml` runs on every push to `master`. It opens (or updates) a
127
+ **`chore: version packages`** PR on `changeset-release/master` that bumps
128
+ affected `package.json` files, rolls up changelogs, and deletes consumed
129
+ changesets.
130
+ 3. **Wait for CI on the version PR.** If checks don't appear (bot-branch
131
+ suppression), push an empty commit to `changeset-release/master`. If
132
+ `check:sdk-version-matrix` fails, run `pnpm gen:sdk-version-matrix` on that
133
+ branch and commit `apps/docs/content/sdks/index.mdx`.
134
+ 4. Merge the version PR. The publish step uses **OIDC Trusted Publishers** +
135
+ Sigstore provenance (with `NPM_TOKEN` only for brand-new package bootstrap).
136
+ 5. **Manually dispatch Release** unless a run starts within ~2 minutes:
137
+ **Actions → Release → Run workflow → `master`**. Squash-merges attributed to
138
+ `github-actions[bot]` usually suppress the auto `push` trigger — manual
139
+ dispatch is the normal path, not a fallback of last resort.
101
140
 
102
- If GitHub's anti-loop protection suppresses the auto re-fire (the squash merge can be attributed to `github-actions[bot]`), trigger the workflow manually: **Actions → release → Run workflow → master**.
141
+ ```bash
142
+ gh workflow run release.yml --ref master
143
+ ```
103
144
 
104
145
  ### Adding a brand-new publishable package
105
146
 
@@ -146,7 +187,7 @@ chore: bump dependencies
146
187
  ## License
147
188
 
148
189
  - SDK packages are MIT — your contributions will be MIT-licensed
149
- - Server/agents/verify are BSL 1.1 — contributions to those packages fall under BSL
190
+ - Server/agents/verify are AGPLv3 — contributions to those packages fall under AGPLv3
150
191
 
151
192
  ## Questions?
152
193
 
package/README.md CHANGED
@@ -1,13 +1,8 @@
1
1
  # @mushi-mushi/mcp
2
2
 
3
- > **Sentry sees what code throws. Mushi sees what users feel — and closes the loop with AI.**
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's **evolution loop** into your AI coding agent. The loop is already running in your Mushi project:
6
-
7
- ```
8
- User feels a bug → Mushi captures it → AI triages → AI opens a PR
9
- → QA verifies → Judge scores → Lesson library remembers → next agent is smarter
10
- ```
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.
11
6
 
12
7
  Wire it into Cursor, Claude Code, or any MCP client in one command:
13
8
 
@@ -16,7 +11,7 @@ npx mushi-mushi setup --ide cursor
16
11
  # or: npx mushi-mushi setup --ide claude
17
12
  ```
18
13
 
19
- That command reads `~/.mushirc`, writes `.cursor/mcp.json` with the `mushi` server block, and prints "Done — restart Cursor and ask: `list mushi tools`". No copy-pasting environment variables.
14
+ That command reads CLI config at **`~/.config/mushi/config.json`** (legacy `~/.mushirc` auto-migrates), writes `.cursor/mcp.json` with the `mushi-<slug>` server block, and prints "Done — restart Cursor and ask: `list mushi tools`". No copy-pasting environment variables.
20
15
 
21
16
  ### Codebase Understand tools (indexed repos)
22
17
 
@@ -146,7 +141,7 @@ The Mushi backend now exposes the same tool catalog over the **Streamable HTTP**
146
141
 
147
142
  ### Feature groups (`?features=` / `MUSHI_FEATURES`)
148
143
 
149
- Full catalog is **72 tools** (64 core + 8 project-context tools). New installs default to a lean set via:
144
+ Full catalog is **71 tools**. New installs default to a lean set via:
150
145
 
151
146
  - **Stdio:** `MUSHI_FEATURES=triage,fixes,inventory,setup,docs`
152
147
  - **HTTP:** `?features=triage,fixes,inventory,setup,docs` on the MCP URL
@@ -157,6 +152,17 @@ New unified tools: `diagnose_setup` (replaces setup trilogy), `search_mushi_docs
157
152
 
158
153
  Append `?read_only=1` to the URL to hide write tools (Supabase-parity mode).
159
154
 
155
+ ## Programmatic imports
156
+
157
+ For custom MCP hosts or tests:
158
+
159
+ | Subpath | Exports |
160
+ | --- | --- |
161
+ | `@mushi-mushi/mcp/catalog` | `TOOL_CATALOG`, scopes, tool specs |
162
+ | `@mushi-mushi/mcp/server` | `createMushiServer()` |
163
+ | `@mushi-mushi/mcp/branding` | Icons and server metadata |
164
+ | `@mushi-mushi/mcp/feature-groups` | `MUSHI_FEATURES` filtering |
165
+
160
166
  ## Cursor config hygiene
161
167
 
162
168
  1. **One global MCP config** — keep `mushi` in `~/.cursor/mcp.json` only. Do **not** duplicate `mushi` / `mushi-stdio` in per-project `.cursor/mcp.json` (prevents connection storms). Project repos may ship `{}` or a comment pointing to the global file.
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-FD6GVGQS.js";
8
+ } from "./chunk-UYZEIJ5G.js";
9
9
  export {
10
10
  ALL_SCOPES,
11
11
  CODEBASE_TOOL_CATALOG,
@@ -44,7 +44,7 @@ var TOOL_CATALOG = [
44
44
  {
45
45
  name: "get_fix_context",
46
46
  title: "Fix context bundle",
47
- description: "Bundle the full context an agent needs to fix a bug: report detail, reproduction steps, component, root cause, ontology tags. One call instead of several.",
47
+ description: "Bundle the full context an agent needs to fix a bug: a paste-ready fixPrompt (plain-English diagnosis + reproduction + suggested fix + relevant code + blast radius), plus report detail, reproduction steps, component, root cause, and ontology tags. One call instead of several. No second LLM key needed.",
48
48
  scope: "mcp:read",
49
49
  hints: { readOnly: true, idempotent: true, openWorld: true },
50
50
  useCase: "Give me everything I need to fix this in one payload."
@@ -274,7 +274,7 @@ var TOOL_CATALOG = [
274
274
  {
275
275
  name: "list_projects",
276
276
  title: "List accessible projects",
277
- description: `List the Mushi projects accessible to this API key. For project-scoped keys (the typical case), returns a single-item list with the bound project's id, name, created_at. Use this when MUSHI_PROJECT_ID is not configured \u2014 call list_projects first, then pass the id to other tools. Multi-project note: each Mushi project uses its own API key. To connect multiple projects, run "Add to Cursor" in the Mushi console for each project \u2014 each deeplink creates a uniquely-named MCP server entry (mushi-{slug}-{id}). You can then call list_projects on each named server to confirm which project it serves.`,
277
+ description: "List all Mushi projects accessible to this API key. For project-scoped keys returns a single-item list; for org-scoped keys (account mode) returns every project owned by this account. Call this first when MUSHI_PROJECT_ID is not configured \u2014 use the returned id with subsequent tool calls. If multiple projects are returned, pass the target projectId explicitly to project-scoped tools. Multi-project tip: run `mushi setup --all-projects` in your terminal to create one named MCP server entry per project in .cursor/mcp.json.",
278
278
  scope: "mcp:read",
279
279
  hints: { readOnly: true, idempotent: true, openWorld: true },
280
280
  useCase: "Which projects can I access with this API key?"
@@ -282,7 +282,7 @@ var TOOL_CATALOG = [
282
282
  {
283
283
  name: "get_account_overview",
284
284
  title: "Account overview \u2014 all projects",
285
- description: "Return a summary of every Mushi project accessible to this API key, including report counts, SDK heartbeat status, and the last ingest timestamp for each. For project-scoped keys this is a one-item list; for future org-level keys it lists every project. Also includes a hint explaining how to connect additional projects to your IDE. Call this at the start of a multi-repo or multi-app triage session to orient yourself.",
285
+ description: "Return an enriched summary of every Mushi project accessible to this API key: id, name, recent report count (last 30 days), number of connected MCP keys, and last-seen heartbeat timestamp. For project-scoped keys this is a one-item list; for org-scoped (account) keys it lists every owned project. Also includes toolCount, resourceCount, promptCount so agents know how many tools are available. Call this at the start of a multi-repo or multi-app triage session to orient yourself.",
286
286
  scope: "mcp:read",
287
287
  hints: { readOnly: true, idempotent: true, openWorld: true },
288
288
  useCase: "Show me all my Mushi projects and their health at a glance."
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ var TOOL_CATALOG = [
51
51
  {
52
52
  name: "get_fix_context",
53
53
  title: "Fix context bundle",
54
- description: "Bundle the full context an agent needs to fix a bug: report detail, reproduction steps, component, root cause, ontology tags. One call instead of several.",
54
+ description: "Bundle the full context an agent needs to fix a bug: a paste-ready fixPrompt (plain-English diagnosis + reproduction + suggested fix + relevant code + blast radius), plus report detail, reproduction steps, component, root cause, and ontology tags. One call instead of several. No second LLM key needed.",
55
55
  scope: "mcp:read",
56
56
  hints: { readOnly: true, idempotent: true, openWorld: true },
57
57
  useCase: "Give me everything I need to fix this in one payload."
@@ -281,7 +281,7 @@ var TOOL_CATALOG = [
281
281
  {
282
282
  name: "list_projects",
283
283
  title: "List accessible projects",
284
- description: `List the Mushi projects accessible to this API key. For project-scoped keys (the typical case), returns a single-item list with the bound project's id, name, created_at. Use this when MUSHI_PROJECT_ID is not configured \u2014 call list_projects first, then pass the id to other tools. Multi-project note: each Mushi project uses its own API key. To connect multiple projects, run "Add to Cursor" in the Mushi console for each project \u2014 each deeplink creates a uniquely-named MCP server entry (mushi-{slug}-{id}). You can then call list_projects on each named server to confirm which project it serves.`,
284
+ description: "List all Mushi projects accessible to this API key. For project-scoped keys returns a single-item list; for org-scoped keys (account mode) returns every project owned by this account. Call this first when MUSHI_PROJECT_ID is not configured \u2014 use the returned id with subsequent tool calls. If multiple projects are returned, pass the target projectId explicitly to project-scoped tools. Multi-project tip: run `mushi setup --all-projects` in your terminal to create one named MCP server entry per project in .cursor/mcp.json.",
285
285
  scope: "mcp:read",
286
286
  hints: { readOnly: true, idempotent: true, openWorld: true },
287
287
  useCase: "Which projects can I access with this API key?"
@@ -289,7 +289,7 @@ var TOOL_CATALOG = [
289
289
  {
290
290
  name: "get_account_overview",
291
291
  title: "Account overview \u2014 all projects",
292
- description: "Return a summary of every Mushi project accessible to this API key, including report counts, SDK heartbeat status, and the last ingest timestamp for each. For project-scoped keys this is a one-item list; for future org-level keys it lists every project. Also includes a hint explaining how to connect additional projects to your IDE. Call this at the start of a multi-repo or multi-app triage session to orient yourself.",
292
+ description: "Return an enriched summary of every Mushi project accessible to this API key: id, name, recent report count (last 30 days), number of connected MCP keys, and last-seen heartbeat timestamp. For project-scoped keys this is a one-item list; for org-scoped (account) keys it lists every owned project. Also includes toolCount, resourceCount, promptCount so agents know how many tools are available. Call this at the start of a multi-repo or multi-app triage session to orient yourself.",
293
293
  scope: "mcp:read",
294
294
  hints: { readOnly: true, idempotent: true, openWorld: true },
295
295
  useCase: "Show me all my Mushi projects and their health at a glance."
@@ -749,14 +749,26 @@ var MUSHI_DOCS_INDEX = [
749
749
  {
750
750
  title: "MCP setup",
751
751
  path: `${BASE}/guides/mcp-setup`,
752
- keywords: ["mcp", "cursor", "api key", "stdio", "http"],
753
- excerpt: "Wire Mushi MCP into Cursor, Claude, or any MCP client."
752
+ keywords: ["mcp", "cursor", "api key", "stdio", "http", "multi-project", "account mode", "install", "setup", "deeplink"],
753
+ excerpt: "Wire Mushi MCP into Cursor, Claude, or any MCP client. Supports per-project and account-level keys."
754
754
  },
755
755
  {
756
756
  title: "API key scopes",
757
757
  path: `${BASE}/guides/api-keys`,
758
- keywords: ["api key", "mcp:read", "mcp:write", "scopes", "security"],
759
- excerpt: "Separate SDK ingest keys from MCP read/write keys."
758
+ keywords: ["api key", "mcp:read", "mcp:write", "scopes", "security", "account key", "org-scoped", "multi-project"],
759
+ excerpt: "Separate SDK ingest keys from MCP read/write keys. Mint an org-scoped account key for multi-project access."
760
+ },
761
+ {
762
+ title: "Account key (org-scoped)",
763
+ path: `${BASE}/guides/mcp-setup`,
764
+ keywords: ["account key", "org-scoped", "multi-project", "MUSHI_PROJECT_ID", "all projects", "mint-org-key", "get_account_overview"],
765
+ excerpt: "Mint one org-scoped key that covers all your projects; no MUSHI_PROJECT_ID needed. The agent calls get_account_overview to discover projects."
766
+ },
767
+ {
768
+ title: "Cursor MCP troubleshooting",
769
+ path: `${BASE}/guides/mcp-setup`,
770
+ keywords: ["cursor", "mcp.json", "red badge", "transport error", "stdout", "stderr", "json-rpc", "INVALID_TOKEN", "syntax helper", "diagnose_connection"],
771
+ excerpt: "Cursor stdio MCP requires stdout to contain only JSON-RPC. Use the console syntax helper, restart Cursor MCP, then run diagnose_connection."
760
772
  },
761
773
  {
762
774
  title: "Judge loop",
@@ -848,7 +860,8 @@ function createMushiServer(config) {
848
860
  const doFetch = config.fetch ?? globalThis.fetch;
849
861
  const apiLog = createLogger({
850
862
  scope: "mushi:mcp:api",
851
- level: process.env.MUSHI_LOG_LEVEL ?? "info"
863
+ level: process.env.MUSHI_LOG_LEVEL ?? "info",
864
+ destination: "stderr"
852
865
  });
853
866
  if (config.scopes !== void 0 && config.scopes.length === 0) {
854
867
  return new McpServer({
@@ -907,6 +920,38 @@ function createMushiServer(config) {
907
920
  apiLog.info("api.done", { path, requestId, status: res.status, durationMs });
908
921
  return body;
909
922
  }
923
+ async function resolveProjectId(explicitId) {
924
+ if (explicitId) return explicitId;
925
+ if (projectId) return projectId;
926
+ try {
927
+ const data = await apiCall(
928
+ "/v1/admin/mcp/account-overview"
929
+ );
930
+ if (data.total === 1 && data.projects[0]) {
931
+ return data.projects[0].id;
932
+ }
933
+ if (data.total === 0) {
934
+ throw new MushiApiError(
935
+ 400,
936
+ "NO_ACCESSIBLE_PROJECTS",
937
+ "This API key has no accessible projects. Mint an API key on a project in the admin console."
938
+ );
939
+ }
940
+ const projectList = data.projects.map((p) => `${p.name ?? "unnamed"} (${p.id})`).join(", ");
941
+ throw new MushiApiError(
942
+ 400,
943
+ "PROJECT_ID_REQUIRED",
944
+ `Multiple projects are accessible with this key: ${projectList}. Pass projectId explicitly to target a specific project, or set MUSHI_PROJECT_ID in your MCP env config.`
945
+ );
946
+ } catch (err) {
947
+ if (err instanceof MushiApiError) throw err;
948
+ throw new MushiApiError(
949
+ 400,
950
+ "MISSING_PROJECT_ID",
951
+ "projectId is required. Set MUSHI_PROJECT_ID in .cursor/mcp.json or pass it explicitly."
952
+ );
953
+ }
954
+ }
910
955
  function jsonText(value) {
911
956
  return {
912
957
  content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
@@ -974,8 +1019,8 @@ function createMushiServer(config) {
974
1019
  if (args.category) params.set("category", args.category);
975
1020
  if (args.severity) params.set("severity", args.severity);
976
1021
  params.set("limit", String(Math.min(args.limit ?? 20, 100)));
977
- const pid = args.project_id ?? projectId;
978
- const extraHeaders = pid && pid !== projectId ? { "X-Mushi-Project-Id": pid } : {};
1022
+ const pid = await resolveProjectId(args.project_id);
1023
+ const extraHeaders = pid !== projectId ? { "X-Mushi-Project-Id": pid } : {};
979
1024
  const data = await apiCall(`/v1/admin/reports?${params}`, {
980
1025
  headers: extraHeaders
981
1026
  });
@@ -1086,6 +1131,7 @@ function createMushiServer(config) {
1086
1131
  inputSchema: { reportId: z.string().describe("The report UUID to fix") },
1087
1132
  outputSchema: {
1088
1133
  report: z.unknown(),
1134
+ fixPrompt: z.unknown(),
1089
1135
  reproductionSteps: z.unknown(),
1090
1136
  component: z.unknown(),
1091
1137
  rootCause: z.unknown(),
@@ -1096,6 +1142,10 @@ function createMushiServer(config) {
1096
1142
  const report = await apiCall(`/v1/admin/reports/${args.reportId}`);
1097
1143
  return jsonResult({
1098
1144
  report,
1145
+ // Paste-ready fix prompt composed server-side by composeFixPacket()
1146
+ // (diagnosis + repro + suggested fix + relevant code + blast radius).
1147
+ // Hand this straight to the editing agent — no second LLM key needed.
1148
+ fixPrompt: report.fix_packet ?? null,
1099
1149
  reproductionSteps: report.reproduction_steps ?? [],
1100
1150
  component: report.component,
1101
1151
  rootCause: report.stage2_analysis?.rootCause,
@@ -1436,6 +1486,7 @@ function createMushiServer(config) {
1436
1486
  dispatchReady: z.boolean(),
1437
1487
  endpoint: z.string().nullable(),
1438
1488
  projectId: z.string().nullable(),
1489
+ accessibleProjectCount: z.number().nullable(),
1439
1490
  issues: z.array(z.unknown()),
1440
1491
  nextAction: z.string(),
1441
1492
  summary: z.string()
@@ -1450,13 +1501,6 @@ function createMushiServer(config) {
1450
1501
  fix: "Run `mushi connect` to write MUSHI_API_KEY into .cursor/mcp.json, then restart the MCP server."
1451
1502
  });
1452
1503
  }
1453
- if (!projectId) {
1454
- issues.push({
1455
- check: "mcp_project_id",
1456
- detail: "No projectId configured on the MCP server",
1457
- fix: "Add MUSHI_PROJECT_ID to .cursor/mcp.json (copy UUID from console \u2192 Projects)."
1458
- });
1459
- }
1460
1504
  if (!apiEndpoint) {
1461
1505
  issues.push({
1462
1506
  check: "mcp_endpoint",
@@ -1488,6 +1532,21 @@ function createMushiServer(config) {
1488
1532
  }
1489
1533
  let ingestReady = false;
1490
1534
  let dispatchReady = false;
1535
+ let accessibleProjectCount = null;
1536
+ if (!projectId && healthOk) {
1537
+ try {
1538
+ const overview = await apiCall("/v1/admin/mcp/account-overview");
1539
+ accessibleProjectCount = overview.total;
1540
+ if (overview.total === 0) {
1541
+ issues.push({
1542
+ check: "no_accessible_projects",
1543
+ detail: "API key has no accessible projects",
1544
+ fix: "Mint an API key on a project (console \u2192 Projects \u2192 API Keys) or add MUSHI_PROJECT_ID to restrict to a single project."
1545
+ });
1546
+ }
1547
+ } catch {
1548
+ }
1549
+ }
1491
1550
  try {
1492
1551
  const ingest = await apiCall("/v1/sync/ingest-setup");
1493
1552
  ingestReady = ingest.ready;
@@ -1530,6 +1589,7 @@ function createMushiServer(config) {
1530
1589
  dispatchReady,
1531
1590
  endpoint: apiEndpoint ?? null,
1532
1591
  projectId: projectId ?? null,
1592
+ accessibleProjectCount,
1533
1593
  issues,
1534
1594
  nextAction,
1535
1595
  summary: ready ? "MCP credentials valid; ingest pipeline ready." : `Connection issue \u2014 ${issues[0]?.check ?? "unknown"}: ${nextAction}`
@@ -1595,16 +1655,24 @@ function createMushiServer(config) {
1595
1655
  inputSchema: {},
1596
1656
  outputSchema: {
1597
1657
  projects: z.array(z.unknown()),
1658
+ total: z.number(),
1598
1659
  active_project_id: z.string().nullable(),
1660
+ toolCount: z.number().optional(),
1661
+ resourceCount: z.number().optional(),
1662
+ promptCount: z.number().optional(),
1599
1663
  multi_project_hint: z.string()
1600
1664
  }
1601
1665
  },
1602
1666
  async () => {
1603
- const data = await apiCall("/v1/admin/mcp/projects");
1667
+ const data = await apiCall("/v1/admin/mcp/account-overview");
1604
1668
  const projects = Array.isArray(data?.projects) ? data.projects : [];
1605
1669
  return jsonResult({
1606
1670
  projects,
1671
+ total: data?.total ?? projects.length,
1607
1672
  active_project_id: projectId ?? null,
1673
+ toolCount: data?.toolCount,
1674
+ resourceCount: data?.resourceCount,
1675
+ promptCount: data?.promptCount,
1608
1676
  multi_project_hint: projects.length <= 1 ? 'You are currently connected to one project. To connect additional Mushi projects, open the Mushi console \u2192 switch to each project \u2192 MCP \u2192 Setup \u2192 "\u26A1 Add to Cursor". Each click adds a uniquely-named entry (mushi-{name}-{id}) to your global ~/.cursor/mcp.json so you can triage reports across all your apps from a single Cursor session.' : `You have access to ${projects.length} projects. To connect any unconnected project, open the Mushi console \u2192 switch to that project \u2192 MCP \u2192 Setup \u2192 "\u26A1 Add to Cursor".`
1609
1677
  });
1610
1678
  }
@@ -2956,9 +3024,14 @@ Be specific. No generic advice.`
2956
3024
  }
2957
3025
 
2958
3026
  // src/index.ts
3027
+ var _writeStderr = (...args) => process.stderr.write(
3028
+ args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") + "\n"
3029
+ );
3030
+ console.log = _writeStderr;
3031
+ console.warn = _writeStderr;
2959
3032
  var require2 = createRequire(import.meta.url);
2960
3033
  var VERSION = require2("../package.json").version;
2961
- var log = createLogger2({ scope: "mushi:mcp", level: "info" });
3034
+ var log = createLogger2({ scope: "mushi:mcp", level: "info", destination: "stderr" });
2962
3035
  var API_ENDPOINT = process.env.MUSHI_API_ENDPOINT ?? "";
2963
3036
  var API_KEY = process.env.MUSHI_API_KEY ?? "";
2964
3037
  var PROJECT_ID = process.env.MUSHI_PROJECT_ID ?? "";
@@ -2976,13 +3049,15 @@ async function main() {
2976
3049
  "[mushi-mcp] MUSHI_API_ENDPOINT is not set. All tool calls will fail.\nSet MUSHI_API_ENDPOINT to your Supabase edge function URL, e.g. https://xyz.supabase.co/functions/v1/api"
2977
3050
  );
2978
3051
  }
3052
+ const mode = PROJECT_ID ? "single-project" : "account";
2979
3053
  if (!PROJECT_ID) {
2980
- console.error(
2981
- "[mushi-mcp] MUSHI_PROJECT_ID is not set.\n\nTools that scope to a project (get_recent_reports, get_report_detail,\nsearch_reports, etc.) will require you to pass projectId explicitly on\nevery call. To set it once and never pass it again:\n\n 1. Open the Mushi admin console \u2192 Projects\n https://kensaur.us/mushi-mushi/projects\n 2. Click your project \u2014 copy the UUID below the project name.\n 3. Add it to your MCP env config:\n MUSHI_PROJECT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n\nOr visit Admin \u2192 MCP for a pre-filled config snippet with your actual UUID."
3054
+ log.info(
3055
+ "[mushi-mcp] Running in account mode (no MUSHI_PROJECT_ID set). Project-scoped tools accept an explicit projectId argument. Run `get_account_overview` to see accessible projects."
2982
3056
  );
2983
3057
  }
2984
3058
  log.info("Starting Mushi MCP server", {
2985
3059
  version: VERSION,
3060
+ mode,
2986
3061
  endpoint: API_ENDPOINT || "(unset)",
2987
3062
  hasProjectId: !!PROJECT_ID,
2988
3063
  scopes: SCOPES.join(",")
package/dist/server.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  CODEBASE_TOOL_CATALOG,
3
3
  TDD_TOOL_CATALOG,
4
4
  TOOL_CATALOG
5
- } from "./chunk-FD6GVGQS.js";
5
+ } from "./chunk-UYZEIJ5G.js";
6
6
  import {
7
7
  MUSHI_SERVER_METADATA
8
8
  } from "./chunk-EHB2BGWX.js";
@@ -27,14 +27,26 @@ var MUSHI_DOCS_INDEX = [
27
27
  {
28
28
  title: "MCP setup",
29
29
  path: `${BASE}/guides/mcp-setup`,
30
- keywords: ["mcp", "cursor", "api key", "stdio", "http"],
31
- excerpt: "Wire Mushi MCP into Cursor, Claude, or any MCP client."
30
+ keywords: ["mcp", "cursor", "api key", "stdio", "http", "multi-project", "account mode", "install", "setup", "deeplink"],
31
+ excerpt: "Wire Mushi MCP into Cursor, Claude, or any MCP client. Supports per-project and account-level keys."
32
32
  },
33
33
  {
34
34
  title: "API key scopes",
35
35
  path: `${BASE}/guides/api-keys`,
36
- keywords: ["api key", "mcp:read", "mcp:write", "scopes", "security"],
37
- excerpt: "Separate SDK ingest keys from MCP read/write keys."
36
+ keywords: ["api key", "mcp:read", "mcp:write", "scopes", "security", "account key", "org-scoped", "multi-project"],
37
+ excerpt: "Separate SDK ingest keys from MCP read/write keys. Mint an org-scoped account key for multi-project access."
38
+ },
39
+ {
40
+ title: "Account key (org-scoped)",
41
+ path: `${BASE}/guides/mcp-setup`,
42
+ keywords: ["account key", "org-scoped", "multi-project", "MUSHI_PROJECT_ID", "all projects", "mint-org-key", "get_account_overview"],
43
+ excerpt: "Mint one org-scoped key that covers all your projects; no MUSHI_PROJECT_ID needed. The agent calls get_account_overview to discover projects."
44
+ },
45
+ {
46
+ title: "Cursor MCP troubleshooting",
47
+ path: `${BASE}/guides/mcp-setup`,
48
+ keywords: ["cursor", "mcp.json", "red badge", "transport error", "stdout", "stderr", "json-rpc", "INVALID_TOKEN", "syntax helper", "diagnose_connection"],
49
+ excerpt: "Cursor stdio MCP requires stdout to contain only JSON-RPC. Use the console syntax helper, restart Cursor MCP, then run diagnose_connection."
38
50
  },
39
51
  {
40
52
  title: "Judge loop",
@@ -126,7 +138,8 @@ function createMushiServer(config) {
126
138
  const doFetch = config.fetch ?? globalThis.fetch;
127
139
  const apiLog = createLogger({
128
140
  scope: "mushi:mcp:api",
129
- level: process.env.MUSHI_LOG_LEVEL ?? "info"
141
+ level: process.env.MUSHI_LOG_LEVEL ?? "info",
142
+ destination: "stderr"
130
143
  });
131
144
  if (config.scopes !== void 0 && config.scopes.length === 0) {
132
145
  return new McpServer({
@@ -185,6 +198,38 @@ function createMushiServer(config) {
185
198
  apiLog.info("api.done", { path, requestId, status: res.status, durationMs });
186
199
  return body;
187
200
  }
201
+ async function resolveProjectId(explicitId) {
202
+ if (explicitId) return explicitId;
203
+ if (projectId) return projectId;
204
+ try {
205
+ const data = await apiCall(
206
+ "/v1/admin/mcp/account-overview"
207
+ );
208
+ if (data.total === 1 && data.projects[0]) {
209
+ return data.projects[0].id;
210
+ }
211
+ if (data.total === 0) {
212
+ throw new MushiApiError(
213
+ 400,
214
+ "NO_ACCESSIBLE_PROJECTS",
215
+ "This API key has no accessible projects. Mint an API key on a project in the admin console."
216
+ );
217
+ }
218
+ const projectList = data.projects.map((p) => `${p.name ?? "unnamed"} (${p.id})`).join(", ");
219
+ throw new MushiApiError(
220
+ 400,
221
+ "PROJECT_ID_REQUIRED",
222
+ `Multiple projects are accessible with this key: ${projectList}. Pass projectId explicitly to target a specific project, or set MUSHI_PROJECT_ID in your MCP env config.`
223
+ );
224
+ } catch (err) {
225
+ if (err instanceof MushiApiError) throw err;
226
+ throw new MushiApiError(
227
+ 400,
228
+ "MISSING_PROJECT_ID",
229
+ "projectId is required. Set MUSHI_PROJECT_ID in .cursor/mcp.json or pass it explicitly."
230
+ );
231
+ }
232
+ }
188
233
  function jsonText(value) {
189
234
  return {
190
235
  content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
@@ -252,8 +297,8 @@ function createMushiServer(config) {
252
297
  if (args.category) params.set("category", args.category);
253
298
  if (args.severity) params.set("severity", args.severity);
254
299
  params.set("limit", String(Math.min(args.limit ?? 20, 100)));
255
- const pid = args.project_id ?? projectId;
256
- const extraHeaders = pid && pid !== projectId ? { "X-Mushi-Project-Id": pid } : {};
300
+ const pid = await resolveProjectId(args.project_id);
301
+ const extraHeaders = pid !== projectId ? { "X-Mushi-Project-Id": pid } : {};
257
302
  const data = await apiCall(`/v1/admin/reports?${params}`, {
258
303
  headers: extraHeaders
259
304
  });
@@ -364,6 +409,7 @@ function createMushiServer(config) {
364
409
  inputSchema: { reportId: z.string().describe("The report UUID to fix") },
365
410
  outputSchema: {
366
411
  report: z.unknown(),
412
+ fixPrompt: z.unknown(),
367
413
  reproductionSteps: z.unknown(),
368
414
  component: z.unknown(),
369
415
  rootCause: z.unknown(),
@@ -374,6 +420,10 @@ function createMushiServer(config) {
374
420
  const report = await apiCall(`/v1/admin/reports/${args.reportId}`);
375
421
  return jsonResult({
376
422
  report,
423
+ // Paste-ready fix prompt composed server-side by composeFixPacket()
424
+ // (diagnosis + repro + suggested fix + relevant code + blast radius).
425
+ // Hand this straight to the editing agent — no second LLM key needed.
426
+ fixPrompt: report.fix_packet ?? null,
377
427
  reproductionSteps: report.reproduction_steps ?? [],
378
428
  component: report.component,
379
429
  rootCause: report.stage2_analysis?.rootCause,
@@ -714,6 +764,7 @@ function createMushiServer(config) {
714
764
  dispatchReady: z.boolean(),
715
765
  endpoint: z.string().nullable(),
716
766
  projectId: z.string().nullable(),
767
+ accessibleProjectCount: z.number().nullable(),
717
768
  issues: z.array(z.unknown()),
718
769
  nextAction: z.string(),
719
770
  summary: z.string()
@@ -728,13 +779,6 @@ function createMushiServer(config) {
728
779
  fix: "Run `mushi connect` to write MUSHI_API_KEY into .cursor/mcp.json, then restart the MCP server."
729
780
  });
730
781
  }
731
- if (!projectId) {
732
- issues.push({
733
- check: "mcp_project_id",
734
- detail: "No projectId configured on the MCP server",
735
- fix: "Add MUSHI_PROJECT_ID to .cursor/mcp.json (copy UUID from console \u2192 Projects)."
736
- });
737
- }
738
782
  if (!apiEndpoint) {
739
783
  issues.push({
740
784
  check: "mcp_endpoint",
@@ -766,6 +810,21 @@ function createMushiServer(config) {
766
810
  }
767
811
  let ingestReady = false;
768
812
  let dispatchReady = false;
813
+ let accessibleProjectCount = null;
814
+ if (!projectId && healthOk) {
815
+ try {
816
+ const overview = await apiCall("/v1/admin/mcp/account-overview");
817
+ accessibleProjectCount = overview.total;
818
+ if (overview.total === 0) {
819
+ issues.push({
820
+ check: "no_accessible_projects",
821
+ detail: "API key has no accessible projects",
822
+ fix: "Mint an API key on a project (console \u2192 Projects \u2192 API Keys) or add MUSHI_PROJECT_ID to restrict to a single project."
823
+ });
824
+ }
825
+ } catch {
826
+ }
827
+ }
769
828
  try {
770
829
  const ingest = await apiCall("/v1/sync/ingest-setup");
771
830
  ingestReady = ingest.ready;
@@ -808,6 +867,7 @@ function createMushiServer(config) {
808
867
  dispatchReady,
809
868
  endpoint: apiEndpoint ?? null,
810
869
  projectId: projectId ?? null,
870
+ accessibleProjectCount,
811
871
  issues,
812
872
  nextAction,
813
873
  summary: ready ? "MCP credentials valid; ingest pipeline ready." : `Connection issue \u2014 ${issues[0]?.check ?? "unknown"}: ${nextAction}`
@@ -873,16 +933,24 @@ function createMushiServer(config) {
873
933
  inputSchema: {},
874
934
  outputSchema: {
875
935
  projects: z.array(z.unknown()),
936
+ total: z.number(),
876
937
  active_project_id: z.string().nullable(),
938
+ toolCount: z.number().optional(),
939
+ resourceCount: z.number().optional(),
940
+ promptCount: z.number().optional(),
877
941
  multi_project_hint: z.string()
878
942
  }
879
943
  },
880
944
  async () => {
881
- const data = await apiCall("/v1/admin/mcp/projects");
945
+ const data = await apiCall("/v1/admin/mcp/account-overview");
882
946
  const projects = Array.isArray(data?.projects) ? data.projects : [];
883
947
  return jsonResult({
884
948
  projects,
949
+ total: data?.total ?? projects.length,
885
950
  active_project_id: projectId ?? null,
951
+ toolCount: data?.toolCount,
952
+ resourceCount: data?.resourceCount,
953
+ promptCount: data?.promptCount,
886
954
  multi_project_hint: projects.length <= 1 ? 'You are currently connected to one project. To connect additional Mushi projects, open the Mushi console \u2192 switch to each project \u2192 MCP \u2192 Setup \u2192 "\u26A1 Add to Cursor". Each click adds a uniquely-named entry (mushi-{name}-{id}) to your global ~/.cursor/mcp.json so you can triage reports across all your apps from a single Cursor session.' : `You have access to ${projects.length} projects. To connect any unconnected project, open the Mushi console \u2192 switch to that project \u2192 MCP \u2192 Setup \u2192 "\u26A1 Add to Cursor".`
887
955
  });
888
956
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@mushi-mushi/mcp",
3
- "version": "0.15.0",
3
+ "mcpName": "io.github.kensaurus/mushi-mushi",
4
+ "version": "0.16.1",
4
5
  "license": "MIT",
5
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",
6
7
  "type": "module",
@@ -42,7 +43,7 @@
42
43
  "dependencies": {
43
44
  "@modelcontextprotocol/sdk": "^1.29.0",
44
45
  "zod": "^4.4.2",
45
- "@mushi-mushi/core": "^1.14.0"
46
+ "@mushi-mushi/core": "^1.16.0"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/node": "^22.19.17",