@mushi-mushi/mcp 0.6.0 → 0.8.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 +11 -0
- package/dist/index.js +108 -45
- package/package.json +3 -2
package/CONTRIBUTING.md
CHANGED
|
@@ -101,6 +101,17 @@ Releases are fully automated. Maintainers don't run `npm publish` by hand.
|
|
|
101
101
|
|
|
102
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**.
|
|
103
103
|
|
|
104
|
+
### Known CI/CD quirks and their automatic safeguards
|
|
105
|
+
|
|
106
|
+
A handful of GitHub-Actions × Changesets edge cases have caused release-pipeline stalls in the past. Each is now caught automatically — keep these in mind when you see the symptom:
|
|
107
|
+
|
|
108
|
+
| Symptom | Root cause | Automatic safeguard |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| The `Build & Test` required check never registers on the `chore: version packages` PR — the PR stays "Required check missing" forever | `changeset-release/master` is pushed by `github-actions[bot]`. GitHub silently drops the downstream `pull_request` event to prevent bot loops (observed on PR #45, #102, #124). | `ci.yml` now also triggers on `push` to `changeset-release/master`, so `Build & Test` reports against the head commit directly. No empty-commit nudge needed. |
|
|
111
|
+
| Release workflow fails with `No commits between master and changeset-release/master` after merging a PR with a new changeset. | A `.changeset/*.md` file whose YAML frontmatter only targets packages listed in `.changeset/config.json#ignore` (e.g. `@mushi-mushi/server`, `@mushi-mushi/admin`). `changeset version` produces no bumps, the version PR is empty, the next push errors (PR #102 / #121, 2026-05-19). | `pnpm check:changeset-orphans` runs in both `ci.yml` and `release.yml`. PR CI fails with an actionable message naming the orphan file *before* it can reach master. If you legitimately need to record an internal-only change, omit the changeset entirely — the diff lives in git history. |
|
|
112
|
+
| Release workflow's `Audit signatures of installed dependencies` step fails with `npm ETARGET / No matching version found for @mushi-mushi/<pkg>@<ver>` seconds after the publish step succeeded. | npm's registry CDN can take up to ~30s to propagate a freshly-published manifest. The audit step shells out to `npm install` against the just-published version and races the CDN (observed 2026-05-20, run 26149167393). | The audit step retries with exponential backoff (1, 2, 4, 8, 16, 32s — 63s total) before failing. Sigstore signatures are written at publish time, so a one-off audit failure never indicates a corrupted package — `pnpm view <pkg> version` is the ground truth. |
|
|
113
|
+
| Push to `master` after merging a PR doesn't fire the `Release` workflow. | Same anti-loop protection: when a squash merge is attributed to `github-actions[bot]`, GitHub may suppress the downstream `push` trigger. Sporadic — observed twice in the last 60 days. | `release.yml` keeps `workflow_dispatch` as the manual fallback. Recovery: **Actions → Release → Run workflow → master**. The `Build & Verify` job re-runs identically to the auto-fired path. |
|
|
114
|
+
|
|
104
115
|
### Adding a brand-new publishable package
|
|
105
116
|
|
|
106
117
|
Trusted Publisher bindings are configured **per package** on `npmjs.com` and require the package to already exist on the registry. New packages therefore need a one-time bootstrap before OIDC can take over.
|
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",
|
|
@@ -145,10 +142,10 @@ var TOOL_CATALOG = [
|
|
|
145
142
|
{
|
|
146
143
|
name: "dispatch_fix",
|
|
147
144
|
title: "Dispatch Mushi fix agent",
|
|
148
|
-
description:
|
|
145
|
+
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
146
|
scope: "mcp:write",
|
|
150
147
|
hints: { readOnly: false, destructive: false, idempotent: false, openWorld: true },
|
|
151
|
-
useCase: "Let the in-repo agent attempt this fix for me."
|
|
148
|
+
useCase: "Let the in-repo agent attempt this fix for me (or: dispatch a Cursor Cloud Agent)."
|
|
152
149
|
},
|
|
153
150
|
{
|
|
154
151
|
name: "trigger_judge",
|
|
@@ -205,6 +202,8 @@ var TOOL_CATALOG = [
|
|
|
205
202
|
];
|
|
206
203
|
|
|
207
204
|
// src/server.ts
|
|
205
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
206
|
+
import { z } from "zod";
|
|
208
207
|
var MushiApiError = class extends Error {
|
|
209
208
|
constructor(status, code, message) {
|
|
210
209
|
super(`[${code}] ${message}`);
|
|
@@ -218,6 +217,7 @@ var MushiApiError = class extends Error {
|
|
|
218
217
|
function createMushiServer(config) {
|
|
219
218
|
const { version, apiEndpoint, apiKey, projectId } = config;
|
|
220
219
|
const doFetch = config.fetch ?? globalThis.fetch;
|
|
220
|
+
const grantedScopes = new Set(config.scopes ?? ALL_SCOPES);
|
|
221
221
|
async function apiCall(path, options) {
|
|
222
222
|
const res = await doFetch(`${apiEndpoint}${path}`, {
|
|
223
223
|
...options,
|
|
@@ -289,7 +289,23 @@ function createMushiServer(config) {
|
|
|
289
289
|
if (!spec) throw new Error(`[mushi-mcp] tool "${name}" is missing from TOOL_CATALOG`);
|
|
290
290
|
return spec.title;
|
|
291
291
|
}
|
|
292
|
-
|
|
292
|
+
function shouldRegister(name) {
|
|
293
|
+
const spec = TOOL_CATALOG.find((t) => t.name === name);
|
|
294
|
+
if (!spec) throw new Error(`[mushi-mcp] tool "${name}" is missing from TOOL_CATALOG`);
|
|
295
|
+
return grantedScopes.has(spec.scope);
|
|
296
|
+
}
|
|
297
|
+
function jsonResult(value) {
|
|
298
|
+
return {
|
|
299
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
300
|
+
structuredContent: value
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const _serverRegisterTool = server.registerTool.bind(server);
|
|
304
|
+
const registerScopedTool = ((name, ...rest) => {
|
|
305
|
+
if (!shouldRegister(name)) return void 0;
|
|
306
|
+
return _serverRegisterTool(name, ...rest);
|
|
307
|
+
});
|
|
308
|
+
registerScopedTool(
|
|
293
309
|
"get_recent_reports",
|
|
294
310
|
{
|
|
295
311
|
title: titleOf("get_recent_reports"),
|
|
@@ -300,6 +316,13 @@ function createMushiServer(config) {
|
|
|
300
316
|
category: z.string().optional().describe("Filter by category: bug, slow, visual, confusing, other"),
|
|
301
317
|
severity: z.string().optional().describe("Filter by severity: critical, high, medium, low"),
|
|
302
318
|
limit: z.number().optional().describe("Max reports to return (default 20, max 100)")
|
|
319
|
+
},
|
|
320
|
+
// Output schema (MCP 2025-06-18): when set, the SDK validates the
|
|
321
|
+
// tool's `structuredContent` and lets typed clients deserialize
|
|
322
|
+
// without re-parsing the text payload.
|
|
323
|
+
outputSchema: {
|
|
324
|
+
reports: z.array(z.record(z.string(), z.unknown())).describe("Array of report rows"),
|
|
325
|
+
total: z.number().describe("Total matching rows (before limit)")
|
|
303
326
|
}
|
|
304
327
|
},
|
|
305
328
|
async (args) => {
|
|
@@ -309,10 +332,13 @@ function createMushiServer(config) {
|
|
|
309
332
|
if (args.severity) params.set("severity", args.severity);
|
|
310
333
|
params.set("limit", String(Math.min(args.limit ?? 20, 100)));
|
|
311
334
|
const data = await apiCall(`/v1/admin/reports?${params}`);
|
|
312
|
-
return
|
|
335
|
+
return jsonResult({
|
|
336
|
+
reports: data.reports ?? [],
|
|
337
|
+
total: data.total ?? 0
|
|
338
|
+
});
|
|
313
339
|
}
|
|
314
340
|
);
|
|
315
|
-
|
|
341
|
+
registerScopedTool(
|
|
316
342
|
"get_report_detail",
|
|
317
343
|
{
|
|
318
344
|
title: titleOf("get_report_detail"),
|
|
@@ -322,7 +348,7 @@ function createMushiServer(config) {
|
|
|
322
348
|
},
|
|
323
349
|
async (args) => jsonText(await apiCall(`/v1/admin/reports/${args.reportId}`))
|
|
324
350
|
);
|
|
325
|
-
|
|
351
|
+
registerScopedTool(
|
|
326
352
|
"search_reports",
|
|
327
353
|
{
|
|
328
354
|
title: titleOf("search_reports"),
|
|
@@ -332,6 +358,9 @@ function createMushiServer(config) {
|
|
|
332
358
|
query: z.string().describe("Natural-language search text or component path"),
|
|
333
359
|
limit: z.number().optional().describe("Max results (default 10, max 50)"),
|
|
334
360
|
threshold: z.number().optional().describe("Similarity threshold 0..1, default 0.2")
|
|
361
|
+
},
|
|
362
|
+
outputSchema: {
|
|
363
|
+
results: z.array(z.record(z.string(), z.unknown())).describe("Ranked report rows with similarity scores")
|
|
335
364
|
}
|
|
336
365
|
},
|
|
337
366
|
async (args) => {
|
|
@@ -344,10 +373,10 @@ function createMushiServer(config) {
|
|
|
344
373
|
...projectId ? { projectId } : {}
|
|
345
374
|
})
|
|
346
375
|
});
|
|
347
|
-
return
|
|
376
|
+
return jsonResult({ results: data.results ?? [] });
|
|
348
377
|
}
|
|
349
378
|
);
|
|
350
|
-
|
|
379
|
+
registerScopedTool(
|
|
351
380
|
"get_similar_bugs",
|
|
352
381
|
{
|
|
353
382
|
title: titleOf("get_similar_bugs"),
|
|
@@ -356,6 +385,9 @@ function createMushiServer(config) {
|
|
|
356
385
|
inputSchema: {
|
|
357
386
|
query: z.string().describe("Component name, page path, or bug description"),
|
|
358
387
|
limit: z.number().optional().describe("Max results (default 5, max 20)")
|
|
388
|
+
},
|
|
389
|
+
outputSchema: {
|
|
390
|
+
results: z.array(z.record(z.string(), z.unknown())).describe("Ranked report rows with similarity scores")
|
|
359
391
|
}
|
|
360
392
|
},
|
|
361
393
|
async (args) => {
|
|
@@ -368,10 +400,10 @@ function createMushiServer(config) {
|
|
|
368
400
|
...projectId ? { projectId } : {}
|
|
369
401
|
})
|
|
370
402
|
});
|
|
371
|
-
return
|
|
403
|
+
return jsonResult({ results: data.results ?? [] });
|
|
372
404
|
}
|
|
373
405
|
);
|
|
374
|
-
|
|
406
|
+
registerScopedTool(
|
|
375
407
|
"get_fix_context",
|
|
376
408
|
{
|
|
377
409
|
title: titleOf("get_fix_context"),
|
|
@@ -390,7 +422,7 @@ function createMushiServer(config) {
|
|
|
390
422
|
});
|
|
391
423
|
}
|
|
392
424
|
);
|
|
393
|
-
|
|
425
|
+
registerScopedTool(
|
|
394
426
|
"get_fix_timeline",
|
|
395
427
|
{
|
|
396
428
|
title: titleOf("get_fix_timeline"),
|
|
@@ -400,7 +432,7 @@ function createMushiServer(config) {
|
|
|
400
432
|
},
|
|
401
433
|
async (args) => jsonText(await apiCall(`/v1/admin/fixes/${args.fixId}/timeline`))
|
|
402
434
|
);
|
|
403
|
-
|
|
435
|
+
registerScopedTool(
|
|
404
436
|
"get_blast_radius",
|
|
405
437
|
{
|
|
406
438
|
title: titleOf("get_blast_radius"),
|
|
@@ -410,7 +442,7 @@ function createMushiServer(config) {
|
|
|
410
442
|
},
|
|
411
443
|
async (args) => jsonText(await apiCall(`/v1/admin/graph/blast-radius/${args.nodeId}`))
|
|
412
444
|
);
|
|
413
|
-
|
|
445
|
+
registerScopedTool(
|
|
414
446
|
"get_knowledge_graph",
|
|
415
447
|
{
|
|
416
448
|
title: titleOf("get_knowledge_graph"),
|
|
@@ -429,7 +461,7 @@ function createMushiServer(config) {
|
|
|
429
461
|
return jsonText(await apiCall(`/v1/admin/graph/traverse?${params}`));
|
|
430
462
|
}
|
|
431
463
|
);
|
|
432
|
-
|
|
464
|
+
registerScopedTool(
|
|
433
465
|
"graph_neighborhood",
|
|
434
466
|
{
|
|
435
467
|
title: titleOf("graph_neighborhood"),
|
|
@@ -448,7 +480,7 @@ function createMushiServer(config) {
|
|
|
448
480
|
return jsonText(await apiCall(`/v1/admin/graph/traverse?${params}`));
|
|
449
481
|
}
|
|
450
482
|
);
|
|
451
|
-
|
|
483
|
+
registerScopedTool(
|
|
452
484
|
"graph_node_status",
|
|
453
485
|
{
|
|
454
486
|
title: titleOf("graph_node_status"),
|
|
@@ -458,7 +490,7 @@ function createMushiServer(config) {
|
|
|
458
490
|
},
|
|
459
491
|
async (args) => jsonText(await apiCall(`/v1/admin/graph/node/${args.nodeId}`))
|
|
460
492
|
);
|
|
461
|
-
|
|
493
|
+
registerScopedTool(
|
|
462
494
|
"inventory_get",
|
|
463
495
|
{
|
|
464
496
|
title: titleOf("inventory_get"),
|
|
@@ -474,7 +506,7 @@ function createMushiServer(config) {
|
|
|
474
506
|
return jsonText(await apiCall(`/v1/admin/inventory/${pid}`));
|
|
475
507
|
}
|
|
476
508
|
);
|
|
477
|
-
|
|
509
|
+
registerScopedTool(
|
|
478
510
|
"inventory_diff",
|
|
479
511
|
{
|
|
480
512
|
title: titleOf("inventory_diff"),
|
|
@@ -493,7 +525,7 @@ function createMushiServer(config) {
|
|
|
493
525
|
return jsonText(await apiCall(`/v1/admin/inventory/${pid}/diff?${q}`));
|
|
494
526
|
}
|
|
495
527
|
);
|
|
496
|
-
|
|
528
|
+
registerScopedTool(
|
|
497
529
|
"inventory_findings",
|
|
498
530
|
{
|
|
499
531
|
title: titleOf("inventory_findings"),
|
|
@@ -515,7 +547,7 @@ function createMushiServer(config) {
|
|
|
515
547
|
return jsonText(await apiCall(`/v1/admin/inventory/${pid}/findings${suffix}`));
|
|
516
548
|
}
|
|
517
549
|
);
|
|
518
|
-
|
|
550
|
+
registerScopedTool(
|
|
519
551
|
"fix_suggest",
|
|
520
552
|
{
|
|
521
553
|
title: titleOf("fix_suggest"),
|
|
@@ -536,7 +568,7 @@ function createMushiServer(config) {
|
|
|
536
568
|
});
|
|
537
569
|
}
|
|
538
570
|
);
|
|
539
|
-
|
|
571
|
+
registerScopedTool(
|
|
540
572
|
"run_nl_query",
|
|
541
573
|
{
|
|
542
574
|
title: titleOf("run_nl_query"),
|
|
@@ -552,7 +584,7 @@ function createMushiServer(config) {
|
|
|
552
584
|
return jsonText(data);
|
|
553
585
|
}
|
|
554
586
|
);
|
|
555
|
-
|
|
587
|
+
registerScopedTool(
|
|
556
588
|
"submit_fix_result",
|
|
557
589
|
{
|
|
558
590
|
title: titleOf("submit_fix_result"),
|
|
@@ -587,7 +619,7 @@ function createMushiServer(config) {
|
|
|
587
619
|
return jsonText({ ok: true, fixId: created.fixId });
|
|
588
620
|
}
|
|
589
621
|
);
|
|
590
|
-
|
|
622
|
+
registerScopedTool(
|
|
591
623
|
"dispatch_fix",
|
|
592
624
|
{
|
|
593
625
|
title: titleOf("dispatch_fix"),
|
|
@@ -595,9 +627,20 @@ function createMushiServer(config) {
|
|
|
595
627
|
annotations: annotationsFor("dispatch_fix"),
|
|
596
628
|
inputSchema: {
|
|
597
629
|
reportId: z.string().describe("Report UUID to fix"),
|
|
598
|
-
agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe(
|
|
630
|
+
agent: z.enum(["claude_code", "codex", "rest_worker", "mcp", "cursor_cloud"]).optional().describe('Override the agent adapter. Use "cursor_cloud" to dispatch a Cursor Cloud Agent that opens a signed draft PR.'),
|
|
631
|
+
backend: z.enum(["default", "claude_code", "cursor_cloud", "mcp"]).optional().describe("Alias for agent \u2014 prefer agent. When both are set, agent wins."),
|
|
632
|
+
cursorModel: z.string().optional().describe('Optional model override when agent=cursor_cloud (e.g. "composer-latest").'),
|
|
599
633
|
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
634
|
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.")
|
|
635
|
+
},
|
|
636
|
+
// Typed write-tool result. fixId is the cursor for get_fix_timeline so
|
|
637
|
+
// downstream tools can chain without re-parsing the text payload.
|
|
638
|
+
outputSchema: {
|
|
639
|
+
fixId: z.string().describe("Newly created fix_attempt UUID"),
|
|
640
|
+
status: z.string().optional().describe("Initial status (queued, running, delegated, \u2026)"),
|
|
641
|
+
agentId: z.string().optional().describe("Cursor agent ID (bc-\u2026) when agent=cursor_cloud"),
|
|
642
|
+
runId: z.string().optional().describe("Cursor run ID when agent=cursor_cloud"),
|
|
643
|
+
prUrl: z.string().optional().describe("Draft PR URL when agent=cursor_cloud and auto_create_pr=true")
|
|
601
644
|
}
|
|
602
645
|
},
|
|
603
646
|
async (args, extra) => {
|
|
@@ -615,20 +658,31 @@ function createMushiServer(config) {
|
|
|
615
658
|
} catch {
|
|
616
659
|
}
|
|
617
660
|
}
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
661
|
+
const resolvedAgent = args.agent ?? (args.backend !== "default" ? args.backend : void 0);
|
|
662
|
+
const data = await apiCall(
|
|
663
|
+
"/v1/admin/fixes/dispatch",
|
|
664
|
+
{
|
|
665
|
+
method: "POST",
|
|
666
|
+
headers: args.idempotencyKey ? { "Idempotency-Key": args.idempotencyKey } : void 0,
|
|
667
|
+
body: JSON.stringify({
|
|
668
|
+
reportId: args.reportId,
|
|
669
|
+
agent: resolvedAgent,
|
|
670
|
+
inventoryActionNodeId: args.inventoryActionNodeId,
|
|
671
|
+
...args.cursorModel ? { cursorModel: args.cursorModel } : {},
|
|
672
|
+
...projectId ? { projectId } : {}
|
|
673
|
+
})
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
return jsonResult({
|
|
677
|
+
fixId: data.fixId ?? "",
|
|
678
|
+
...data.status ? { status: data.status } : {},
|
|
679
|
+
...data.agentId ? { agentId: data.agentId } : {},
|
|
680
|
+
...data.runId ? { runId: data.runId } : {},
|
|
681
|
+
...data.prUrl ? { prUrl: data.prUrl } : {}
|
|
627
682
|
});
|
|
628
|
-
return jsonText(data);
|
|
629
683
|
}
|
|
630
684
|
);
|
|
631
|
-
|
|
685
|
+
registerScopedTool(
|
|
632
686
|
"trigger_judge",
|
|
633
687
|
{
|
|
634
688
|
title: titleOf("trigger_judge"),
|
|
@@ -650,7 +704,7 @@ function createMushiServer(config) {
|
|
|
650
704
|
return jsonText(data);
|
|
651
705
|
}
|
|
652
706
|
);
|
|
653
|
-
|
|
707
|
+
registerScopedTool(
|
|
654
708
|
"test_gen_from_report",
|
|
655
709
|
{
|
|
656
710
|
title: titleOf("test_gen_from_report"),
|
|
@@ -671,7 +725,7 @@ function createMushiServer(config) {
|
|
|
671
725
|
return jsonText(data);
|
|
672
726
|
}
|
|
673
727
|
);
|
|
674
|
-
|
|
728
|
+
registerScopedTool(
|
|
675
729
|
"transition_status",
|
|
676
730
|
{
|
|
677
731
|
title: titleOf("transition_status"),
|
|
@@ -715,7 +769,7 @@ function createMushiServer(config) {
|
|
|
715
769
|
contents: [{ uri: "project://dashboard", mimeType: "application/json", text: JSON.stringify(await apiCall("/v1/admin/dashboard"), null, 2) }]
|
|
716
770
|
})
|
|
717
771
|
);
|
|
718
|
-
|
|
772
|
+
registerScopedTool(
|
|
719
773
|
"list_top_contributors",
|
|
720
774
|
{
|
|
721
775
|
title: titleOf("list_top_contributors"),
|
|
@@ -737,7 +791,7 @@ function createMushiServer(config) {
|
|
|
737
791
|
}]
|
|
738
792
|
})
|
|
739
793
|
);
|
|
740
|
-
|
|
794
|
+
registerScopedTool(
|
|
741
795
|
"award_bonus_points",
|
|
742
796
|
{
|
|
743
797
|
title: titleOf("award_bonus_points"),
|
|
@@ -764,7 +818,7 @@ function createMushiServer(config) {
|
|
|
764
818
|
}]
|
|
765
819
|
})
|
|
766
820
|
);
|
|
767
|
-
|
|
821
|
+
registerScopedTool(
|
|
768
822
|
"set_tier",
|
|
769
823
|
{
|
|
770
824
|
title: titleOf("set_tier"),
|
|
@@ -901,6 +955,9 @@ var log = createLogger({ scope: "mushi:mcp", level: "info" });
|
|
|
901
955
|
var API_ENDPOINT = process.env.MUSHI_API_ENDPOINT ?? "";
|
|
902
956
|
var API_KEY = process.env.MUSHI_API_KEY ?? "";
|
|
903
957
|
var PROJECT_ID = process.env.MUSHI_PROJECT_ID ?? "";
|
|
958
|
+
var SCOPES_RAW = process.env.MUSHI_SCOPES ?? "";
|
|
959
|
+
var parsedScopes = SCOPES_RAW ? SCOPES_RAW.split(",").map((s) => s.trim()).filter((s) => s === "mcp:read" || s === "mcp:write") : ALL_SCOPES;
|
|
960
|
+
var SCOPES = SCOPES_RAW && parsedScopes.length === 0 ? ALL_SCOPES : parsedScopes;
|
|
904
961
|
async function main() {
|
|
905
962
|
if (!API_KEY) {
|
|
906
963
|
log.fatal("MUSHI_API_KEY environment variable is required");
|
|
@@ -916,12 +973,18 @@ async function main() {
|
|
|
916
973
|
"[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
974
|
);
|
|
918
975
|
}
|
|
919
|
-
log.info("Starting Mushi MCP server", {
|
|
976
|
+
log.info("Starting Mushi MCP server", {
|
|
977
|
+
version: VERSION,
|
|
978
|
+
endpoint: API_ENDPOINT || "(unset)",
|
|
979
|
+
hasProjectId: !!PROJECT_ID,
|
|
980
|
+
scopes: SCOPES.join(",")
|
|
981
|
+
});
|
|
920
982
|
const server = createMushiServer({
|
|
921
983
|
version: VERSION,
|
|
922
984
|
apiEndpoint: API_ENDPOINT,
|
|
923
985
|
apiKey: API_KEY,
|
|
924
|
-
projectId: PROJECT_ID || void 0
|
|
986
|
+
projectId: PROJECT_ID || void 0,
|
|
987
|
+
scopes: SCOPES
|
|
925
988
|
});
|
|
926
989
|
const transport = new StdioServerTransport();
|
|
927
990
|
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.8.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "MCP server exposing Mushi Mushi reports to coding agents",
|
|
6
6
|
"type": "module",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
28
|
"zod": "^4.4.2",
|
|
29
|
-
"@mushi-mushi/core": "^1.
|
|
29
|
+
"@mushi-mushi/core": "^1.5.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.19.17",
|
|
@@ -83,6 +83,7 @@
|
|
|
83
83
|
"test:smoke": "node scripts/smoke-stdio.mjs",
|
|
84
84
|
"test:localhost": "node scripts/localhost-e2e.mjs",
|
|
85
85
|
"demo": "node scripts/demo-terminal-config.mjs",
|
|
86
|
+
"inspector": "npx --yes @modelcontextprotocol/inspector@latest node ./dist/index.js",
|
|
86
87
|
"typecheck": "tsc --noEmit"
|
|
87
88
|
}
|
|
88
89
|
}
|