@mushi-mushi/mcp 0.6.0 → 0.9.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/README.md +31 -5
- package/dist/index.js +168 -37
- package/package.json +18 -17
package/README.md
CHANGED
|
@@ -1,18 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# mushi-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Sentry sees what code throws. Mushi sees what users feel — and closes the loop with AI.**
|
|
4
4
|
|
|
5
|
-
|
|
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
|
+
```
|
|
11
|
+
|
|
12
|
+
Wire it into Cursor, Claude Code, or any MCP client in one command:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx mushi-mushi setup --ide cursor
|
|
16
|
+
# or: npx mushi-mushi setup --ide claude
|
|
17
|
+
```
|
|
18
|
+
|
|
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.
|
|
6
20
|
|
|
7
21
|
> **What this is, and what it isn't**
|
|
8
22
|
>
|
|
9
|
-
> - **This package** is the MCP **server** — runs locally next to your editor, talks to the Mushi
|
|
23
|
+
> - **This package** (`mushi-mcp`) is the MCP **server** — runs locally next to your editor, talks to the Mushi API, and presents bug reports as MCP tools/resources to your coding agent. The npm package name is `mushi-mcp`; the scoped alias `@mushi-mushi/mcp` still works.
|
|
10
24
|
> - **`@mushi-mushi/agents`** ships the MCP **client adapter** — used by the autofix orchestrator when your project's `autofix_agent = 'mcp'`. See `packages/agents/src/adapters/mcp.ts`.
|
|
11
25
|
> - The `generic_mcp` adapter shipped before V5.3 was a misnomer (it spoke plain REST). It is now `RestFixWorkerAgent`; the old export is kept as a deprecated alias for one more minor.
|
|
12
26
|
|
|
13
27
|
## Quick start
|
|
14
28
|
|
|
15
|
-
###
|
|
29
|
+
### 0. One-liner (recommended)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# First: make sure you've logged in
|
|
33
|
+
npx mushi-mushi login --api-key mushi_xxx --endpoint https://<ref>.supabase.co/functions/v1/api
|
|
34
|
+
|
|
35
|
+
# Then wire your IDE
|
|
36
|
+
npx mushi-mushi setup --ide cursor # Cursor
|
|
37
|
+
npx mushi-mushi setup --ide claude # Claude Code / Claude Desktop
|
|
38
|
+
npx mushi-mushi setup --ide cursor --with-rules # also write .cursorrules
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 1. With Claude Desktop (manual)
|
|
16
42
|
|
|
17
43
|
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
18
44
|
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,8 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
import { createLogger } from "@mushi-mushi/core";
|
|
7
7
|
|
|
8
|
-
// src/server.ts
|
|
9
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
-
import { z } from "zod";
|
|
11
|
-
|
|
12
8
|
// src/catalog.ts
|
|
9
|
+
var ALL_SCOPES = ["mcp:read", "mcp:write"];
|
|
13
10
|
var TOOL_CATALOG = [
|
|
14
11
|
{
|
|
15
12
|
name: "get_recent_reports",
|
|
@@ -132,6 +129,15 @@ var TOOL_CATALOG = [
|
|
|
132
129
|
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
133
130
|
useCase: "What did Stage 2 say we should try for this report?"
|
|
134
131
|
},
|
|
132
|
+
// --- Setup / admin -------------------------------------------------------
|
|
133
|
+
{
|
|
134
|
+
name: "setup_check",
|
|
135
|
+
title: "Dispatch preflight check",
|
|
136
|
+
description: "Run the 4 dispatch-readiness checks for a project and return their pass/fail status (GitHub repo connected, codebase indexed, Anthropic BYOK key present, autofix enabled). Also returns the target repo URL when GitHub is connected. Use this before calling dispatch_fix to understand why a dispatch might fail \u2014 or to validate that the onboarding wizard is complete.",
|
|
137
|
+
scope: "mcp:read",
|
|
138
|
+
hints: { readOnly: true, idempotent: true, openWorld: true },
|
|
139
|
+
useCase: "Is this project ready to auto-fix bugs? What is blocking me?"
|
|
140
|
+
},
|
|
135
141
|
// --- Write / agentic ----------------------------------------------------
|
|
136
142
|
{
|
|
137
143
|
name: "submit_fix_result",
|
|
@@ -145,10 +151,10 @@ var TOOL_CATALOG = [
|
|
|
145
151
|
{
|
|
146
152
|
name: "dispatch_fix",
|
|
147
153
|
title: "Dispatch Mushi fix agent",
|
|
148
|
-
description:
|
|
154
|
+
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.',
|
|
149
155
|
scope: "mcp:write",
|
|
150
156
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
151
|
-
useCase: "Let the in-repo agent attempt this fix for me."
|
|
157
|
+
useCase: "Let the in-repo agent attempt this fix for me (or: dispatch a Cursor Cloud Agent)."
|
|
152
158
|
},
|
|
153
159
|
{
|
|
154
160
|
name: "trigger_judge",
|
|
@@ -201,10 +207,20 @@ var TOOL_CATALOG = [
|
|
|
201
207
|
scope: "mcp:write",
|
|
202
208
|
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: true },
|
|
203
209
|
useCase: "Manually promote this user to Champion tier as a thank-you."
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: "setup_repo_for_mushi",
|
|
213
|
+
title: "Bootstrap repo for Mushi",
|
|
214
|
+
description: "Writes the three Mushi bootstrap files into the current repo root: `.cursorrules` (Cursor evolution-loop coding rules), `.mushi/lessons.json` (initial empty lesson cache), and `MUSHI.md` (one-page project contract for agents). Idempotent \u2014 safe to re-run after lessons sync. Requires mcp:write scope. Call this once after connecting the repo; subsequently use `mushi sync-lessons` from CI to keep lessons current.",
|
|
215
|
+
scope: "mcp:write",
|
|
216
|
+
hints: { readOnly: false, destructive: false, idempotent: true, openWorld: false },
|
|
217
|
+
useCase: "Set up this repo for the Mushi evolution loop in one step."
|
|
204
218
|
}
|
|
205
219
|
];
|
|
206
220
|
|
|
207
221
|
// src/server.ts
|
|
222
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
223
|
+
import { z } from "zod";
|
|
208
224
|
var MushiApiError = class extends Error {
|
|
209
225
|
constructor(status, code, message) {
|
|
210
226
|
super(`[${code}] ${message}`);
|
|
@@ -218,6 +234,9 @@ var MushiApiError = class extends Error {
|
|
|
218
234
|
function createMushiServer(config) {
|
|
219
235
|
const { version, apiEndpoint, apiKey, projectId } = config;
|
|
220
236
|
const doFetch = config.fetch ?? globalThis.fetch;
|
|
237
|
+
if (config.scopes !== void 0 && config.scopes.length === 0) {
|
|
238
|
+
return new McpServer({ name: "mushi-mushi", version });
|
|
239
|
+
}
|
|
221
240
|
async function apiCall(path, options) {
|
|
222
241
|
const res = await doFetch(`${apiEndpoint}${path}`, {
|
|
223
242
|
...options,
|
|
@@ -263,6 +282,12 @@ function createMushiServer(config) {
|
|
|
263
282
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
264
283
|
};
|
|
265
284
|
}
|
|
285
|
+
function jsonResult(value) {
|
|
286
|
+
return {
|
|
287
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
288
|
+
structuredContent: value
|
|
289
|
+
};
|
|
290
|
+
}
|
|
266
291
|
const server = new McpServer({
|
|
267
292
|
name: "mushi-mushi",
|
|
268
293
|
version
|
|
@@ -300,6 +325,10 @@ function createMushiServer(config) {
|
|
|
300
325
|
category: z.string().optional().describe("Filter by category: bug, slow, visual, confusing, other"),
|
|
301
326
|
severity: z.string().optional().describe("Filter by severity: critical, high, medium, low"),
|
|
302
327
|
limit: z.number().optional().describe("Max reports to return (default 20, max 100)")
|
|
328
|
+
},
|
|
329
|
+
outputSchema: {
|
|
330
|
+
reports: z.array(z.unknown()),
|
|
331
|
+
total: z.number()
|
|
303
332
|
}
|
|
304
333
|
},
|
|
305
334
|
async (args) => {
|
|
@@ -309,7 +338,7 @@ function createMushiServer(config) {
|
|
|
309
338
|
if (args.severity) params.set("severity", args.severity);
|
|
310
339
|
params.set("limit", String(Math.min(args.limit ?? 20, 100)));
|
|
311
340
|
const data = await apiCall(`/v1/admin/reports?${params}`);
|
|
312
|
-
return
|
|
341
|
+
return jsonResult(data);
|
|
313
342
|
}
|
|
314
343
|
);
|
|
315
344
|
server.registerTool(
|
|
@@ -332,6 +361,9 @@ function createMushiServer(config) {
|
|
|
332
361
|
query: z.string().describe("Natural-language search text or component path"),
|
|
333
362
|
limit: z.number().optional().describe("Max results (default 10, max 50)"),
|
|
334
363
|
threshold: z.number().optional().describe("Similarity threshold 0..1, default 0.2")
|
|
364
|
+
},
|
|
365
|
+
outputSchema: {
|
|
366
|
+
results: z.array(z.unknown())
|
|
335
367
|
}
|
|
336
368
|
},
|
|
337
369
|
async (args) => {
|
|
@@ -344,7 +376,7 @@ function createMushiServer(config) {
|
|
|
344
376
|
...projectId ? { projectId } : {}
|
|
345
377
|
})
|
|
346
378
|
});
|
|
347
|
-
return
|
|
379
|
+
return jsonResult(data);
|
|
348
380
|
}
|
|
349
381
|
);
|
|
350
382
|
server.registerTool(
|
|
@@ -552,6 +584,43 @@ function createMushiServer(config) {
|
|
|
552
584
|
return jsonText(data);
|
|
553
585
|
}
|
|
554
586
|
);
|
|
587
|
+
server.registerTool(
|
|
588
|
+
"setup_check",
|
|
589
|
+
{
|
|
590
|
+
title: titleOf("setup_check"),
|
|
591
|
+
description: descOf("setup_check"),
|
|
592
|
+
annotations: annotationsFor("setup_check"),
|
|
593
|
+
inputSchema: {
|
|
594
|
+
projectId: z.string().optional().describe(
|
|
595
|
+
"Project UUID to check. Falls back to the projectId the server was initialised with."
|
|
596
|
+
)
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
async (args) => {
|
|
600
|
+
const resolvedId = args.projectId ?? projectId;
|
|
601
|
+
if (!resolvedId) {
|
|
602
|
+
return jsonText({
|
|
603
|
+
ok: false,
|
|
604
|
+
error: "No projectId provided and none configured on the MCP server. Pass projectId explicitly."
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
const data = await apiCall(`/v1/admin/projects/${resolvedId}/preflight`);
|
|
608
|
+
const summary = data.checks.map((c) => ({
|
|
609
|
+
check: c.key,
|
|
610
|
+
label: c.label,
|
|
611
|
+
passed: c.ready,
|
|
612
|
+
hint: c.hint,
|
|
613
|
+
fixPath: c.fixHref
|
|
614
|
+
}));
|
|
615
|
+
return jsonText({
|
|
616
|
+
ready: data.ready,
|
|
617
|
+
repoUrl: data.repoUrl ?? null,
|
|
618
|
+
checks: summary,
|
|
619
|
+
// Human-readable summary for agents that paste the result into a prompt
|
|
620
|
+
summary: data.ready ? `Project ${resolvedId} is ready to dispatch auto-fixes${data.repoUrl ? ` (target: ${data.repoUrl})` : ""}.` : `Project ${resolvedId} cannot dispatch yet \u2014 ${summary.filter((c) => !c.passed).map((c) => c.label).join(", ")}.`
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
);
|
|
555
624
|
server.registerTool(
|
|
556
625
|
"submit_fix_result",
|
|
557
626
|
{
|
|
@@ -598,6 +667,10 @@ function createMushiServer(config) {
|
|
|
598
667
|
agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
|
|
599
668
|
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)."),
|
|
600
669
|
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.")
|
|
670
|
+
},
|
|
671
|
+
outputSchema: {
|
|
672
|
+
fixId: z.string(),
|
|
673
|
+
status: z.string()
|
|
601
674
|
}
|
|
602
675
|
},
|
|
603
676
|
async (args, extra) => {
|
|
@@ -625,7 +698,7 @@ function createMushiServer(config) {
|
|
|
625
698
|
...projectId ? { projectId } : {}
|
|
626
699
|
})
|
|
627
700
|
});
|
|
628
|
-
return
|
|
701
|
+
return jsonResult(data);
|
|
629
702
|
}
|
|
630
703
|
);
|
|
631
704
|
server.registerTool(
|
|
@@ -691,6 +764,26 @@ function createMushiServer(config) {
|
|
|
691
764
|
return jsonText(data);
|
|
692
765
|
}
|
|
693
766
|
);
|
|
767
|
+
server.registerTool(
|
|
768
|
+
"setup_repo_for_mushi",
|
|
769
|
+
{
|
|
770
|
+
title: titleOf("setup_repo_for_mushi"),
|
|
771
|
+
description: descOf("setup_repo_for_mushi"),
|
|
772
|
+
annotations: annotationsFor("setup_repo_for_mushi"),
|
|
773
|
+
inputSchema: {
|
|
774
|
+
projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
async (args) => {
|
|
778
|
+
const pid = args.projectId ?? projectId;
|
|
779
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for setup_repo_for_mushi");
|
|
780
|
+
const data = await apiCall(`/v1/admin/projects/${pid}/repo/bootstrap`, {
|
|
781
|
+
method: "POST",
|
|
782
|
+
body: JSON.stringify({})
|
|
783
|
+
});
|
|
784
|
+
return jsonText(data);
|
|
785
|
+
}
|
|
786
|
+
);
|
|
694
787
|
server.resource(
|
|
695
788
|
"project_stats",
|
|
696
789
|
"project://stats",
|
|
@@ -715,17 +808,15 @@ function createMushiServer(config) {
|
|
|
715
808
|
contents: [{ uri: "project://dashboard", mimeType: "application/json", text: JSON.stringify(await apiCall("/v1/admin/dashboard"), null, 2) }]
|
|
716
809
|
})
|
|
717
810
|
);
|
|
718
|
-
|
|
811
|
+
const rewardsMeta = (name) => annotationsFor(name);
|
|
812
|
+
server.tool(
|
|
719
813
|
"list_top_contributors",
|
|
814
|
+
rewardsMeta("list_top_contributors").title,
|
|
720
815
|
{
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
inputSchema: {
|
|
724
|
-
limit: z.number().int().min(1).max(100).optional().default(10).describe("Max rows to return (default 10, max 100)"),
|
|
725
|
-
range: z.enum(["30d", "90d", "all"]).optional().default("30d").describe("Time window for points calculation")
|
|
726
|
-
},
|
|
727
|
-
annotations: annotationsFor("list_top_contributors")
|
|
816
|
+
limit: z.number().int().min(1).max(100).optional().default(10).describe("Max rows to return (default 10, max 100)"),
|
|
817
|
+
range: z.enum(["30d", "90d", "all"]).optional().default("30d").describe("Time window for points calculation")
|
|
728
818
|
},
|
|
819
|
+
rewardsMeta("list_top_contributors"),
|
|
729
820
|
async ({ limit, range }) => ({
|
|
730
821
|
content: [{
|
|
731
822
|
type: "text",
|
|
@@ -737,18 +828,15 @@ function createMushiServer(config) {
|
|
|
737
828
|
}]
|
|
738
829
|
})
|
|
739
830
|
);
|
|
740
|
-
server.
|
|
831
|
+
server.tool(
|
|
741
832
|
"award_bonus_points",
|
|
833
|
+
rewardsMeta("award_bonus_points").title,
|
|
742
834
|
{
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
|
|
747
|
-
points: z.number().int().min(1).max(5e4).describe("Bonus points to award (max 50,000 per call)"),
|
|
748
|
-
reason: z.string().max(200).describe("Human-readable reason, logged to end_user_activity")
|
|
749
|
-
},
|
|
750
|
-
annotations: annotationsFor("award_bonus_points")
|
|
835
|
+
external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
|
|
836
|
+
points: z.number().int().min(1).max(5e4).describe("Bonus points to award (max 50,000 per call)"),
|
|
837
|
+
reason: z.string().max(200).describe("Human-readable reason, logged to end_user_activity")
|
|
751
838
|
},
|
|
839
|
+
rewardsMeta("award_bonus_points"),
|
|
752
840
|
async ({ external_user_id, points, reason }) => ({
|
|
753
841
|
content: [{
|
|
754
842
|
type: "text",
|
|
@@ -764,18 +852,15 @@ function createMushiServer(config) {
|
|
|
764
852
|
}]
|
|
765
853
|
})
|
|
766
854
|
);
|
|
767
|
-
server.
|
|
855
|
+
server.tool(
|
|
768
856
|
"set_tier",
|
|
857
|
+
rewardsMeta("set_tier").title,
|
|
769
858
|
{
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
|
|
774
|
-
tier_slug: z.string().describe('Tier slug to assign, e.g. "champion", "contributor", "explorer"'),
|
|
775
|
-
reason: z.string().max(200).optional().describe("Optional reason for manual override")
|
|
776
|
-
},
|
|
777
|
-
annotations: annotationsFor("set_tier")
|
|
859
|
+
external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
|
|
860
|
+
tier_slug: z.string().describe('Tier slug to assign, e.g. "champion", "contributor", "explorer"'),
|
|
861
|
+
reason: z.string().max(200).optional().describe("Optional reason for manual override")
|
|
778
862
|
},
|
|
863
|
+
rewardsMeta("set_tier"),
|
|
779
864
|
async ({ external_user_id, tier_slug, reason }) => ({
|
|
780
865
|
content: [{
|
|
781
866
|
type: "text",
|
|
@@ -791,6 +876,34 @@ function createMushiServer(config) {
|
|
|
791
876
|
}]
|
|
792
877
|
})
|
|
793
878
|
);
|
|
879
|
+
server.resource(
|
|
880
|
+
"privacy_status",
|
|
881
|
+
"privacy://status",
|
|
882
|
+
{
|
|
883
|
+
description: "Returns the privacy posture for this project: storage region, LLM provider, whether BYOK is configured, data retention window, and last audit timestamp."
|
|
884
|
+
},
|
|
885
|
+
async () => ({
|
|
886
|
+
contents: [{
|
|
887
|
+
uri: "privacy://status",
|
|
888
|
+
mimeType: "application/json",
|
|
889
|
+
text: JSON.stringify(await apiCall("/v1/admin/privacy/status"), null, 2)
|
|
890
|
+
}]
|
|
891
|
+
})
|
|
892
|
+
);
|
|
893
|
+
server.resource(
|
|
894
|
+
"evolution_history",
|
|
895
|
+
"evolution://history",
|
|
896
|
+
{
|
|
897
|
+
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."
|
|
898
|
+
},
|
|
899
|
+
async () => ({
|
|
900
|
+
contents: [{
|
|
901
|
+
uri: "evolution://history",
|
|
902
|
+
mimeType: "application/json",
|
|
903
|
+
text: JSON.stringify(await apiCall("/v1/admin/evolution/history"), null, 2)
|
|
904
|
+
}]
|
|
905
|
+
})
|
|
906
|
+
);
|
|
794
907
|
server.resource(
|
|
795
908
|
"project_integration_health",
|
|
796
909
|
"project://integration-health",
|
|
@@ -891,6 +1004,15 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
|
|
|
891
1004
|
}]
|
|
892
1005
|
})
|
|
893
1006
|
);
|
|
1007
|
+
const grantedScopes = config.scopes;
|
|
1008
|
+
if (grantedScopes !== void 0) {
|
|
1009
|
+
const toolRegistry = server._registeredTools;
|
|
1010
|
+
for (const spec of TOOL_CATALOG) {
|
|
1011
|
+
if (!grantedScopes.includes(spec.scope)) {
|
|
1012
|
+
toolRegistry[spec.name]?.remove();
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
894
1016
|
return server;
|
|
895
1017
|
}
|
|
896
1018
|
|
|
@@ -901,6 +1023,9 @@ var log = createLogger({ scope: "mushi:mcp", level: "info" });
|
|
|
901
1023
|
var API_ENDPOINT = process.env.MUSHI_API_ENDPOINT ?? "";
|
|
902
1024
|
var API_KEY = process.env.MUSHI_API_KEY ?? "";
|
|
903
1025
|
var PROJECT_ID = process.env.MUSHI_PROJECT_ID ?? "";
|
|
1026
|
+
var SCOPES_RAW = process.env.MUSHI_SCOPES ?? "";
|
|
1027
|
+
var parsedScopes = SCOPES_RAW ? SCOPES_RAW.split(",").map((s) => s.trim()).filter((s) => s === "mcp:read" || s === "mcp:write") : ALL_SCOPES;
|
|
1028
|
+
var SCOPES = SCOPES_RAW && parsedScopes.length === 0 ? ALL_SCOPES : parsedScopes;
|
|
904
1029
|
async function main() {
|
|
905
1030
|
if (!API_KEY) {
|
|
906
1031
|
log.fatal("MUSHI_API_KEY environment variable is required");
|
|
@@ -916,12 +1041,18 @@ async function main() {
|
|
|
916
1041
|
"[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."
|
|
917
1042
|
);
|
|
918
1043
|
}
|
|
919
|
-
log.info("Starting Mushi MCP server", {
|
|
1044
|
+
log.info("Starting Mushi MCP server", {
|
|
1045
|
+
version: VERSION,
|
|
1046
|
+
endpoint: API_ENDPOINT || "(unset)",
|
|
1047
|
+
hasProjectId: !!PROJECT_ID,
|
|
1048
|
+
scopes: SCOPES.join(",")
|
|
1049
|
+
});
|
|
920
1050
|
const server = createMushiServer({
|
|
921
1051
|
version: VERSION,
|
|
922
1052
|
apiEndpoint: API_ENDPOINT,
|
|
923
1053
|
apiKey: API_KEY,
|
|
924
|
-
projectId: PROJECT_ID || void 0
|
|
1054
|
+
projectId: PROJECT_ID || void 0,
|
|
1055
|
+
scopes: SCOPES
|
|
925
1056
|
});
|
|
926
1057
|
const transport = new StdioServerTransport();
|
|
927
1058
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushi-mushi/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "MCP server exposing Mushi Mushi reports to coding agents",
|
|
6
6
|
"type": "module",
|
|
@@ -23,18 +23,30 @@
|
|
|
23
23
|
"CODE_OF_CONDUCT.md",
|
|
24
24
|
"SECURITY.md"
|
|
25
25
|
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"clean:types": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"lint": "eslint src/",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:smoke": "node scripts/smoke-stdio.mjs",
|
|
33
|
+
"test:localhost": "node scripts/localhost-e2e.mjs",
|
|
34
|
+
"demo": "node scripts/demo-terminal-config.mjs",
|
|
35
|
+
"inspector": "npx --yes @modelcontextprotocol/inspector@latest node ./dist/index.js",
|
|
36
|
+
"typecheck": "tsc --noEmit"
|
|
37
|
+
},
|
|
26
38
|
"dependencies": {
|
|
27
39
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
40
|
+
"@mushi-mushi/core": "workspace:^",
|
|
41
|
+
"zod": "^4.4.2"
|
|
30
42
|
},
|
|
31
43
|
"devDependencies": {
|
|
44
|
+
"@mushi-mushi/eslint-config": "workspace:*",
|
|
32
45
|
"@types/node": "^22.19.17",
|
|
33
46
|
"eslint": "^10.3.0",
|
|
34
47
|
"tsup": "^8.5.1",
|
|
35
48
|
"typescript": "^6.0.3",
|
|
36
|
-
"vitest": "^4.1.5"
|
|
37
|
-
"@mushi-mushi/eslint-config": "0.0.0"
|
|
49
|
+
"vitest": "^4.1.5"
|
|
38
50
|
},
|
|
39
51
|
"repository": {
|
|
40
52
|
"type": "git",
|
|
@@ -73,16 +85,5 @@
|
|
|
73
85
|
],
|
|
74
86
|
"engines": {
|
|
75
87
|
"node": ">=20"
|
|
76
|
-
},
|
|
77
|
-
"scripts": {
|
|
78
|
-
"build": "tsup",
|
|
79
|
-
"clean:types": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
80
|
-
"dev": "tsup --watch",
|
|
81
|
-
"lint": "eslint src/",
|
|
82
|
-
"test": "vitest run",
|
|
83
|
-
"test:smoke": "node scripts/smoke-stdio.mjs",
|
|
84
|
-
"test:localhost": "node scripts/localhost-e2e.mjs",
|
|
85
|
-
"demo": "node scripts/demo-terminal-config.mjs",
|
|
86
|
-
"typecheck": "tsc --noEmit"
|
|
87
88
|
}
|
|
88
|
-
}
|
|
89
|
+
}
|