@mushi-mushi/mcp 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +23 -1
- package/README.md +12 -1
- package/dist/catalog.js +1 -1
- package/dist/{chunk-FD6GVGQS.js → chunk-PZEGN4XD.js} +2 -2
- package/dist/index.js +90 -20
- package/dist/server.js +79 -16
- package/package.json +2 -2
package/CONTRIBUTING.md
CHANGED
|
@@ -63,7 +63,7 @@ packages/
|
|
|
63
63
|
verify/ Fix verification (BSL)
|
|
64
64
|
apps/
|
|
65
65
|
admin/ Admin dashboard (React + Tailwind)
|
|
66
|
-
docs/ Documentation site (
|
|
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,28 @@ 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:bundle-docs # @mushi-mushi/web size-limit vs README
|
|
101
|
+
pnpm gen:llms-txt # regenerate apps/docs/public/llms.txt after nav changes
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
README changes on npm packages only appear on [npmjs.com](https://www.npmjs.com) after the next **Changesets publish**.
|
|
105
|
+
|
|
84
106
|
## Changesets
|
|
85
107
|
|
|
86
108
|
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:
|
package/README.md
CHANGED
|
@@ -146,7 +146,7 @@ The Mushi backend now exposes the same tool catalog over the **Streamable HTTP**
|
|
|
146
146
|
|
|
147
147
|
### Feature groups (`?features=` / `MUSHI_FEATURES`)
|
|
148
148
|
|
|
149
|
-
Full catalog is **
|
|
149
|
+
Full catalog is **79 tools**. New installs default to a lean set via:
|
|
150
150
|
|
|
151
151
|
- **Stdio:** `MUSHI_FEATURES=triage,fixes,inventory,setup,docs`
|
|
152
152
|
- **HTTP:** `?features=triage,fixes,inventory,setup,docs` on the MCP URL
|
|
@@ -157,6 +157,17 @@ New unified tools: `diagnose_setup` (replaces setup trilogy), `search_mushi_docs
|
|
|
157
157
|
|
|
158
158
|
Append `?read_only=1` to the URL to hide write tools (Supabase-parity mode).
|
|
159
159
|
|
|
160
|
+
## Programmatic imports
|
|
161
|
+
|
|
162
|
+
For custom MCP hosts or tests:
|
|
163
|
+
|
|
164
|
+
| Subpath | Exports |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| `@mushi-mushi/mcp/catalog` | `TOOL_CATALOG`, scopes, tool specs |
|
|
167
|
+
| `@mushi-mushi/mcp/server` | `createMushiServer()` |
|
|
168
|
+
| `@mushi-mushi/mcp/branding` | Icons and server metadata |
|
|
169
|
+
| `@mushi-mushi/mcp/feature-groups` | `MUSHI_FEATURES` filtering |
|
|
170
|
+
|
|
160
171
|
## Cursor config hygiene
|
|
161
172
|
|
|
162
173
|
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
|
@@ -274,7 +274,7 @@ var TOOL_CATALOG = [
|
|
|
274
274
|
{
|
|
275
275
|
name: "list_projects",
|
|
276
276
|
title: "List accessible projects",
|
|
277
|
-
description:
|
|
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
|
|
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
|
@@ -281,7 +281,7 @@ var TOOL_CATALOG = [
|
|
|
281
281
|
{
|
|
282
282
|
name: "list_projects",
|
|
283
283
|
title: "List accessible projects",
|
|
284
|
-
description:
|
|
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
|
|
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
|
|
978
|
-
const extraHeaders = 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
|
});
|
|
@@ -1436,6 +1481,7 @@ function createMushiServer(config) {
|
|
|
1436
1481
|
dispatchReady: z.boolean(),
|
|
1437
1482
|
endpoint: z.string().nullable(),
|
|
1438
1483
|
projectId: z.string().nullable(),
|
|
1484
|
+
accessibleProjectCount: z.number().nullable(),
|
|
1439
1485
|
issues: z.array(z.unknown()),
|
|
1440
1486
|
nextAction: z.string(),
|
|
1441
1487
|
summary: z.string()
|
|
@@ -1450,13 +1496,6 @@ function createMushiServer(config) {
|
|
|
1450
1496
|
fix: "Run `mushi connect` to write MUSHI_API_KEY into .cursor/mcp.json, then restart the MCP server."
|
|
1451
1497
|
});
|
|
1452
1498
|
}
|
|
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
1499
|
if (!apiEndpoint) {
|
|
1461
1500
|
issues.push({
|
|
1462
1501
|
check: "mcp_endpoint",
|
|
@@ -1488,6 +1527,21 @@ function createMushiServer(config) {
|
|
|
1488
1527
|
}
|
|
1489
1528
|
let ingestReady = false;
|
|
1490
1529
|
let dispatchReady = false;
|
|
1530
|
+
let accessibleProjectCount = null;
|
|
1531
|
+
if (!projectId && healthOk) {
|
|
1532
|
+
try {
|
|
1533
|
+
const overview = await apiCall("/v1/admin/mcp/account-overview");
|
|
1534
|
+
accessibleProjectCount = overview.total;
|
|
1535
|
+
if (overview.total === 0) {
|
|
1536
|
+
issues.push({
|
|
1537
|
+
check: "no_accessible_projects",
|
|
1538
|
+
detail: "API key has no accessible projects",
|
|
1539
|
+
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."
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
} catch {
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1491
1545
|
try {
|
|
1492
1546
|
const ingest = await apiCall("/v1/sync/ingest-setup");
|
|
1493
1547
|
ingestReady = ingest.ready;
|
|
@@ -1530,6 +1584,7 @@ function createMushiServer(config) {
|
|
|
1530
1584
|
dispatchReady,
|
|
1531
1585
|
endpoint: apiEndpoint ?? null,
|
|
1532
1586
|
projectId: projectId ?? null,
|
|
1587
|
+
accessibleProjectCount,
|
|
1533
1588
|
issues,
|
|
1534
1589
|
nextAction,
|
|
1535
1590
|
summary: ready ? "MCP credentials valid; ingest pipeline ready." : `Connection issue \u2014 ${issues[0]?.check ?? "unknown"}: ${nextAction}`
|
|
@@ -1595,16 +1650,24 @@ function createMushiServer(config) {
|
|
|
1595
1650
|
inputSchema: {},
|
|
1596
1651
|
outputSchema: {
|
|
1597
1652
|
projects: z.array(z.unknown()),
|
|
1653
|
+
total: z.number(),
|
|
1598
1654
|
active_project_id: z.string().nullable(),
|
|
1655
|
+
toolCount: z.number().optional(),
|
|
1656
|
+
resourceCount: z.number().optional(),
|
|
1657
|
+
promptCount: z.number().optional(),
|
|
1599
1658
|
multi_project_hint: z.string()
|
|
1600
1659
|
}
|
|
1601
1660
|
},
|
|
1602
1661
|
async () => {
|
|
1603
|
-
const data = await apiCall("/v1/admin/mcp/
|
|
1662
|
+
const data = await apiCall("/v1/admin/mcp/account-overview");
|
|
1604
1663
|
const projects = Array.isArray(data?.projects) ? data.projects : [];
|
|
1605
1664
|
return jsonResult({
|
|
1606
1665
|
projects,
|
|
1666
|
+
total: data?.total ?? projects.length,
|
|
1607
1667
|
active_project_id: projectId ?? null,
|
|
1668
|
+
toolCount: data?.toolCount,
|
|
1669
|
+
resourceCount: data?.resourceCount,
|
|
1670
|
+
promptCount: data?.promptCount,
|
|
1608
1671
|
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
1672
|
});
|
|
1610
1673
|
}
|
|
@@ -2956,9 +3019,14 @@ Be specific. No generic advice.`
|
|
|
2956
3019
|
}
|
|
2957
3020
|
|
|
2958
3021
|
// src/index.ts
|
|
3022
|
+
var _writeStderr = (...args) => process.stderr.write(
|
|
3023
|
+
args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ") + "\n"
|
|
3024
|
+
);
|
|
3025
|
+
console.log = _writeStderr;
|
|
3026
|
+
console.warn = _writeStderr;
|
|
2959
3027
|
var require2 = createRequire(import.meta.url);
|
|
2960
3028
|
var VERSION = require2("../package.json").version;
|
|
2961
|
-
var log = createLogger2({ scope: "mushi:mcp", level: "info" });
|
|
3029
|
+
var log = createLogger2({ scope: "mushi:mcp", level: "info", destination: "stderr" });
|
|
2962
3030
|
var API_ENDPOINT = process.env.MUSHI_API_ENDPOINT ?? "";
|
|
2963
3031
|
var API_KEY = process.env.MUSHI_API_KEY ?? "";
|
|
2964
3032
|
var PROJECT_ID = process.env.MUSHI_PROJECT_ID ?? "";
|
|
@@ -2976,13 +3044,15 @@ async function main() {
|
|
|
2976
3044
|
"[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
3045
|
);
|
|
2978
3046
|
}
|
|
3047
|
+
const mode = PROJECT_ID ? "single-project" : "account";
|
|
2979
3048
|
if (!PROJECT_ID) {
|
|
2980
|
-
|
|
2981
|
-
"[mushi-mcp]
|
|
3049
|
+
log.info(
|
|
3050
|
+
"[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
3051
|
);
|
|
2983
3052
|
}
|
|
2984
3053
|
log.info("Starting Mushi MCP server", {
|
|
2985
3054
|
version: VERSION,
|
|
3055
|
+
mode,
|
|
2986
3056
|
endpoint: API_ENDPOINT || "(unset)",
|
|
2987
3057
|
hasProjectId: !!PROJECT_ID,
|
|
2988
3058
|
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-
|
|
5
|
+
} from "./chunk-PZEGN4XD.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
|
|
256
|
-
const extraHeaders = 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
|
});
|
|
@@ -714,6 +759,7 @@ function createMushiServer(config) {
|
|
|
714
759
|
dispatchReady: z.boolean(),
|
|
715
760
|
endpoint: z.string().nullable(),
|
|
716
761
|
projectId: z.string().nullable(),
|
|
762
|
+
accessibleProjectCount: z.number().nullable(),
|
|
717
763
|
issues: z.array(z.unknown()),
|
|
718
764
|
nextAction: z.string(),
|
|
719
765
|
summary: z.string()
|
|
@@ -728,13 +774,6 @@ function createMushiServer(config) {
|
|
|
728
774
|
fix: "Run `mushi connect` to write MUSHI_API_KEY into .cursor/mcp.json, then restart the MCP server."
|
|
729
775
|
});
|
|
730
776
|
}
|
|
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
777
|
if (!apiEndpoint) {
|
|
739
778
|
issues.push({
|
|
740
779
|
check: "mcp_endpoint",
|
|
@@ -766,6 +805,21 @@ function createMushiServer(config) {
|
|
|
766
805
|
}
|
|
767
806
|
let ingestReady = false;
|
|
768
807
|
let dispatchReady = false;
|
|
808
|
+
let accessibleProjectCount = null;
|
|
809
|
+
if (!projectId && healthOk) {
|
|
810
|
+
try {
|
|
811
|
+
const overview = await apiCall("/v1/admin/mcp/account-overview");
|
|
812
|
+
accessibleProjectCount = overview.total;
|
|
813
|
+
if (overview.total === 0) {
|
|
814
|
+
issues.push({
|
|
815
|
+
check: "no_accessible_projects",
|
|
816
|
+
detail: "API key has no accessible projects",
|
|
817
|
+
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."
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
} catch {
|
|
821
|
+
}
|
|
822
|
+
}
|
|
769
823
|
try {
|
|
770
824
|
const ingest = await apiCall("/v1/sync/ingest-setup");
|
|
771
825
|
ingestReady = ingest.ready;
|
|
@@ -808,6 +862,7 @@ function createMushiServer(config) {
|
|
|
808
862
|
dispatchReady,
|
|
809
863
|
endpoint: apiEndpoint ?? null,
|
|
810
864
|
projectId: projectId ?? null,
|
|
865
|
+
accessibleProjectCount,
|
|
811
866
|
issues,
|
|
812
867
|
nextAction,
|
|
813
868
|
summary: ready ? "MCP credentials valid; ingest pipeline ready." : `Connection issue \u2014 ${issues[0]?.check ?? "unknown"}: ${nextAction}`
|
|
@@ -873,16 +928,24 @@ function createMushiServer(config) {
|
|
|
873
928
|
inputSchema: {},
|
|
874
929
|
outputSchema: {
|
|
875
930
|
projects: z.array(z.unknown()),
|
|
931
|
+
total: z.number(),
|
|
876
932
|
active_project_id: z.string().nullable(),
|
|
933
|
+
toolCount: z.number().optional(),
|
|
934
|
+
resourceCount: z.number().optional(),
|
|
935
|
+
promptCount: z.number().optional(),
|
|
877
936
|
multi_project_hint: z.string()
|
|
878
937
|
}
|
|
879
938
|
},
|
|
880
939
|
async () => {
|
|
881
|
-
const data = await apiCall("/v1/admin/mcp/
|
|
940
|
+
const data = await apiCall("/v1/admin/mcp/account-overview");
|
|
882
941
|
const projects = Array.isArray(data?.projects) ? data.projects : [];
|
|
883
942
|
return jsonResult({
|
|
884
943
|
projects,
|
|
944
|
+
total: data?.total ?? projects.length,
|
|
885
945
|
active_project_id: projectId ?? null,
|
|
946
|
+
toolCount: data?.toolCount,
|
|
947
|
+
resourceCount: data?.resourceCount,
|
|
948
|
+
promptCount: data?.promptCount,
|
|
886
949
|
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
950
|
});
|
|
888
951
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"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
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
44
44
|
"zod": "^4.4.2",
|
|
45
|
-
"@mushi-mushi/core": "^1.
|
|
45
|
+
"@mushi-mushi/core": "^1.15.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.19.17",
|