@mushi-mushi/mcp 0.17.0 → 0.18.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 +28 -13
- package/dist/branding.js +1 -1
- package/dist/catalog.js +1 -1
- package/dist/{chunk-EHB2BGWX.js → chunk-EANW5YTI.js} +3 -1
- package/dist/{chunk-MM3IMFM4.js → chunk-KNVB6HA7.js} +73 -105
- package/dist/{chunk-ZX3XIJ7S.js → chunk-VBXXUK5G.js} +31 -13
- package/dist/clients.d.ts +17 -1
- package/dist/clients.js +40 -19
- package/dist/feature-groups.d.ts +8 -2
- package/dist/feature-groups.js +1 -1
- package/dist/index.js +298 -368
- package/dist/server.js +179 -257
- package/package.json +3 -2
package/dist/server.js
CHANGED
|
@@ -2,13 +2,14 @@ import {
|
|
|
2
2
|
CODEBASE_TOOL_CATALOG,
|
|
3
3
|
TDD_TOOL_CATALOG,
|
|
4
4
|
TOOL_CATALOG
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KNVB6HA7.js";
|
|
6
6
|
import {
|
|
7
7
|
MUSHI_SERVER_METADATA
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-EANW5YTI.js";
|
|
9
9
|
import {
|
|
10
|
+
DEPRECATED_TOOL_ALIASES,
|
|
10
11
|
toolMatchesFeatures
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-VBXXUK5G.js";
|
|
12
13
|
|
|
13
14
|
// src/server.ts
|
|
14
15
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -45,8 +46,8 @@ var MUSHI_DOCS_INDEX = [
|
|
|
45
46
|
{
|
|
46
47
|
title: "Cursor MCP troubleshooting",
|
|
47
48
|
path: `${BASE}/guides/mcp-setup`,
|
|
48
|
-
keywords: ["cursor", "mcp.json", "red badge", "transport error", "stdout", "stderr", "json-rpc", "INVALID_TOKEN", "syntax helper", "
|
|
49
|
-
excerpt: "Cursor stdio MCP requires stdout to contain only JSON-RPC. Use the console syntax helper, restart Cursor MCP, then run
|
|
49
|
+
keywords: ["cursor", "mcp.json", "red badge", "transport error", "stdout", "stderr", "json-rpc", "INVALID_TOKEN", "syntax helper", "diagnose_setup"],
|
|
50
|
+
excerpt: "Cursor stdio MCP requires stdout to contain only JSON-RPC. Use the console syntax helper, restart Cursor MCP, then run diagnose_setup."
|
|
50
51
|
},
|
|
51
52
|
{
|
|
52
53
|
title: "Judge loop",
|
|
@@ -123,6 +124,7 @@ function searchMushiDocs(query, limit = 8) {
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
// src/server.ts
|
|
127
|
+
var NO_ARG_INPUT = {};
|
|
126
128
|
var MushiApiError = class extends Error {
|
|
127
129
|
constructor(status, code, message) {
|
|
128
130
|
super(`[${code}] ${message}`);
|
|
@@ -359,7 +361,7 @@ function createMushiServer(config) {
|
|
|
359
361
|
title: titleOf("get_two_way_comms_health"),
|
|
360
362
|
description: descOf("get_two_way_comms_health"),
|
|
361
363
|
annotations: annotationsFor("get_two_way_comms_health"),
|
|
362
|
-
inputSchema:
|
|
364
|
+
inputSchema: NO_ARG_INPUT
|
|
363
365
|
},
|
|
364
366
|
async () => jsonText(await apiCall("/v1/sync/two-way-health"))
|
|
365
367
|
);
|
|
@@ -501,14 +503,14 @@ function createMushiServer(config) {
|
|
|
501
503
|
}
|
|
502
504
|
);
|
|
503
505
|
server.registerTool(
|
|
504
|
-
"
|
|
506
|
+
"get_graph_neighborhood",
|
|
505
507
|
{
|
|
506
|
-
title: titleOf("
|
|
507
|
-
description: descOf("
|
|
508
|
-
annotations: annotationsFor("
|
|
508
|
+
title: titleOf("get_graph_neighborhood"),
|
|
509
|
+
description: descOf("get_graph_neighborhood"),
|
|
510
|
+
annotations: annotationsFor("get_graph_neighborhood"),
|
|
509
511
|
inputSchema: {
|
|
510
|
-
seed: z.string().describe("Starting node id or label"),
|
|
511
|
-
depth: z.number().optional().describe("
|
|
512
|
+
seed: z.string().describe("Starting graph node id or label to expand around (e.g. an inventory Action id or component name)"),
|
|
513
|
+
depth: z.number().int().min(1).max(4).optional().describe("BFS hops to traverse outward. Default 2; clamped to a max of 4.")
|
|
512
514
|
}
|
|
513
515
|
},
|
|
514
516
|
async (args) => {
|
|
@@ -520,65 +522,65 @@ function createMushiServer(config) {
|
|
|
520
522
|
}
|
|
521
523
|
);
|
|
522
524
|
server.registerTool(
|
|
523
|
-
"
|
|
525
|
+
"get_graph_node",
|
|
524
526
|
{
|
|
525
|
-
title: titleOf("
|
|
526
|
-
description: descOf("
|
|
527
|
-
annotations: annotationsFor("
|
|
528
|
-
inputSchema: { nodeId: z.string().describe("graph_nodes.id") }
|
|
527
|
+
title: titleOf("get_graph_node"),
|
|
528
|
+
description: descOf("get_graph_node"),
|
|
529
|
+
annotations: annotationsFor("get_graph_node"),
|
|
530
|
+
inputSchema: { nodeId: z.string().describe("The graph_nodes.id of the node to fetch (UUID).") }
|
|
529
531
|
},
|
|
530
532
|
async (args) => jsonText(await apiCall(`/v1/admin/graph/node/${args.nodeId}`))
|
|
531
533
|
);
|
|
532
534
|
server.registerTool(
|
|
533
|
-
"
|
|
535
|
+
"get_inventory",
|
|
534
536
|
{
|
|
535
|
-
title: titleOf("
|
|
536
|
-
description: descOf("
|
|
537
|
-
annotations: annotationsFor("
|
|
537
|
+
title: titleOf("get_inventory"),
|
|
538
|
+
description: descOf("get_inventory"),
|
|
539
|
+
annotations: annotationsFor("get_inventory"),
|
|
538
540
|
inputSchema: {
|
|
539
541
|
projectId: z.string().optional().describe("Project UUID \u2014 defaults to the server-configured project when omitted")
|
|
540
542
|
}
|
|
541
543
|
},
|
|
542
544
|
async (args) => {
|
|
543
545
|
const pid = args.projectId ?? projectId;
|
|
544
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for
|
|
546
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for get_inventory");
|
|
545
547
|
return jsonText(await apiCall(`/v1/admin/inventory/${pid}`));
|
|
546
548
|
}
|
|
547
549
|
);
|
|
548
550
|
server.registerTool(
|
|
549
|
-
"
|
|
551
|
+
"diff_inventory",
|
|
550
552
|
{
|
|
551
|
-
title: titleOf("
|
|
552
|
-
description: descOf("
|
|
553
|
-
annotations: annotationsFor("
|
|
553
|
+
title: titleOf("diff_inventory"),
|
|
554
|
+
description: descOf("diff_inventory"),
|
|
555
|
+
annotations: annotationsFor("diff_inventory"),
|
|
554
556
|
inputSchema: {
|
|
555
557
|
projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project"),
|
|
556
|
-
fromSha: z.string().describe("Older commit SHA (baseline)"),
|
|
557
|
-
toSha: z.string().describe("Newer commit SHA (candidate)")
|
|
558
|
+
fromSha: z.string().describe("Older commit SHA (the baseline to diff from)"),
|
|
559
|
+
toSha: z.string().describe("Newer commit SHA (the candidate to diff to)")
|
|
558
560
|
}
|
|
559
561
|
},
|
|
560
562
|
async (args) => {
|
|
561
563
|
const pid = args.projectId ?? projectId;
|
|
562
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for
|
|
564
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for diff_inventory");
|
|
563
565
|
const q = new URLSearchParams({ from: args.fromSha, to: args.toSha });
|
|
564
566
|
return jsonText(await apiCall(`/v1/admin/inventory/${pid}/diff?${q}`));
|
|
565
567
|
}
|
|
566
568
|
);
|
|
567
569
|
server.registerTool(
|
|
568
|
-
"
|
|
570
|
+
"list_gate_findings",
|
|
569
571
|
{
|
|
570
|
-
title: titleOf("
|
|
571
|
-
description: descOf("
|
|
572
|
-
annotations: annotationsFor("
|
|
572
|
+
title: titleOf("list_gate_findings"),
|
|
573
|
+
description: descOf("list_gate_findings"),
|
|
574
|
+
annotations: annotationsFor("list_gate_findings"),
|
|
573
575
|
inputSchema: {
|
|
574
576
|
projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project"),
|
|
575
|
-
gate: z.string().optional().describe("Filter
|
|
576
|
-
severity: z.string().optional().describe("
|
|
577
|
+
gate: z.string().optional().describe("Filter to one gate id: dead-handler | mock-leak | crawl | status-claim"),
|
|
578
|
+
severity: z.string().optional().describe("Minimum severity to include: low | medium | high | critical")
|
|
577
579
|
}
|
|
578
580
|
},
|
|
579
581
|
async (args) => {
|
|
580
582
|
const pid = args.projectId ?? projectId;
|
|
581
|
-
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for
|
|
583
|
+
if (!pid) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required for list_gate_findings");
|
|
582
584
|
const q = new URLSearchParams();
|
|
583
585
|
if (args.gate) q.set("gate", args.gate);
|
|
584
586
|
if (args.severity) q.set("severity", args.severity);
|
|
@@ -587,17 +589,25 @@ function createMushiServer(config) {
|
|
|
587
589
|
}
|
|
588
590
|
);
|
|
589
591
|
server.registerTool(
|
|
590
|
-
"
|
|
592
|
+
"suggest_fix",
|
|
591
593
|
{
|
|
592
|
-
title: titleOf("
|
|
593
|
-
description: descOf("
|
|
594
|
-
annotations: annotationsFor("
|
|
595
|
-
inputSchema: { reportId: z.string().describe("Report UUID") }
|
|
594
|
+
title: titleOf("suggest_fix"),
|
|
595
|
+
description: descOf("suggest_fix"),
|
|
596
|
+
annotations: annotationsFor("suggest_fix"),
|
|
597
|
+
inputSchema: { reportId: z.string().describe("Report UUID to read the Stage-2 suggested-fix slice for") },
|
|
598
|
+
outputSchema: {
|
|
599
|
+
reportId: z.string().describe("The report this slice was read from"),
|
|
600
|
+
rootCause: z.unknown().describe("Stage-2 root-cause hint, or null if not yet classified"),
|
|
601
|
+
suggestedFix: z.unknown().describe("Stage-2 suggested fix, or null if not yet classified"),
|
|
602
|
+
reproductionSteps: z.unknown().describe("Reproduction steps recorded on the report (array; [] if none)"),
|
|
603
|
+
summary: z.unknown().describe("One-line report summary, or null"),
|
|
604
|
+
component: z.unknown().describe("Component/page the bug was attributed to, or null")
|
|
605
|
+
}
|
|
596
606
|
},
|
|
597
607
|
async (args) => {
|
|
598
608
|
const report = await apiCall(`/v1/admin/reports/${args.reportId}`);
|
|
599
609
|
const s2 = report.stage2_analysis;
|
|
600
|
-
return
|
|
610
|
+
return jsonResult({
|
|
601
611
|
reportId: args.reportId,
|
|
602
612
|
rootCause: s2?.rootCause ?? null,
|
|
603
613
|
suggestedFix: s2?.suggestedFix ?? null,
|
|
@@ -623,73 +633,6 @@ function createMushiServer(config) {
|
|
|
623
633
|
return jsonText(data);
|
|
624
634
|
}
|
|
625
635
|
);
|
|
626
|
-
server.registerTool(
|
|
627
|
-
"setup_check",
|
|
628
|
-
{
|
|
629
|
-
title: titleOf("setup_check"),
|
|
630
|
-
description: descOf("setup_check"),
|
|
631
|
-
annotations: annotationsFor("setup_check"),
|
|
632
|
-
inputSchema: {
|
|
633
|
-
projectId: z.string().optional().describe(
|
|
634
|
-
"Project UUID to check. Falls back to the projectId the server was initialised with."
|
|
635
|
-
)
|
|
636
|
-
}
|
|
637
|
-
},
|
|
638
|
-
async (args) => {
|
|
639
|
-
const resolvedId = args.projectId ?? projectId;
|
|
640
|
-
if (!resolvedId) {
|
|
641
|
-
return jsonText({
|
|
642
|
-
ok: false,
|
|
643
|
-
error: "No projectId provided and none configured on the MCP server. Pass projectId explicitly."
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
const data = await apiCall(`/v1/admin/projects/${resolvedId}/preflight`);
|
|
647
|
-
const summary = data.checks.map((c) => ({
|
|
648
|
-
check: c.key,
|
|
649
|
-
label: c.label,
|
|
650
|
-
passed: c.ready,
|
|
651
|
-
hint: c.hint,
|
|
652
|
-
fixPath: c.fixHref
|
|
653
|
-
}));
|
|
654
|
-
return jsonText({
|
|
655
|
-
ready: data.ready,
|
|
656
|
-
repoUrl: data.repoUrl ?? null,
|
|
657
|
-
checks: summary,
|
|
658
|
-
// Human-readable summary for agents that paste the result into a prompt
|
|
659
|
-
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(", ")}.`
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
);
|
|
663
|
-
server.registerTool(
|
|
664
|
-
"ingest_setup_check",
|
|
665
|
-
{
|
|
666
|
-
title: titleOf("ingest_setup_check"),
|
|
667
|
-
description: descOf("ingest_setup_check"),
|
|
668
|
-
annotations: annotationsFor("ingest_setup_check"),
|
|
669
|
-
inputSchema: {}
|
|
670
|
-
},
|
|
671
|
-
async () => {
|
|
672
|
-
const data = await apiCall("/v1/sync/ingest-setup");
|
|
673
|
-
const failed = data.steps.filter((s) => s.required && !s.complete);
|
|
674
|
-
const summary = data.ready ? `Ingest setup complete (${data.required_complete}/${data.required_total}) for ${data.project_name}.` : `Ingest incomplete (${data.required_complete}/${data.required_total}) \u2014 still need: ${failed.map((s) => s.label).join(", ")}.`;
|
|
675
|
-
return jsonText({
|
|
676
|
-
ready: data.ready,
|
|
677
|
-
projectId: data.project_id,
|
|
678
|
-
projectName: data.project_name,
|
|
679
|
-
requiredComplete: data.required_complete,
|
|
680
|
-
requiredTotal: data.required_total,
|
|
681
|
-
steps: data.steps.map((s) => ({
|
|
682
|
-
id: s.id,
|
|
683
|
-
label: s.label,
|
|
684
|
-
passed: s.complete,
|
|
685
|
-
required: s.required,
|
|
686
|
-
hint: s.hint
|
|
687
|
-
})),
|
|
688
|
-
diagnostic: data.diagnostic ?? null,
|
|
689
|
-
summary
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
);
|
|
693
636
|
server.registerTool(
|
|
694
637
|
"diagnose_setup",
|
|
695
638
|
{
|
|
@@ -707,7 +650,8 @@ function createMushiServer(config) {
|
|
|
707
650
|
summary: z.string(),
|
|
708
651
|
nextAction: z.string().optional(),
|
|
709
652
|
ingest: z.unknown().optional(),
|
|
710
|
-
dispatch: z.unknown().optional()
|
|
653
|
+
dispatch: z.unknown().optional(),
|
|
654
|
+
connection: z.unknown().optional()
|
|
711
655
|
}
|
|
712
656
|
},
|
|
713
657
|
async (args) => {
|
|
@@ -747,160 +691,99 @@ function createMushiServer(config) {
|
|
|
747
691
|
dispatch: { repoUrl: data.repoUrl ?? null, checks: data.checks }
|
|
748
692
|
});
|
|
749
693
|
}
|
|
750
|
-
const
|
|
751
|
-
let
|
|
752
|
-
let dispatchBlock;
|
|
753
|
-
let dispatchChecks = null;
|
|
754
|
-
if (resolvedId) {
|
|
755
|
-
try {
|
|
756
|
-
const preflight = await apiCall(`/v1/admin/projects/${resolvedId}/preflight`);
|
|
757
|
-
dispatchReady = preflight.ready;
|
|
758
|
-
dispatchChecks = preflight.checks;
|
|
759
|
-
if (!preflight.ready) {
|
|
760
|
-
const failed = preflight.checks.filter((c) => !c.ready);
|
|
761
|
-
dispatchBlock = failed[0]?.hint;
|
|
762
|
-
}
|
|
763
|
-
} catch {
|
|
764
|
-
dispatchReady = false;
|
|
765
|
-
dispatchBlock = "Could not run dispatch preflight \u2014 verify project_id and API key scope.";
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
const ingestFailed = ingest.steps.filter((s) => s.required && !s.complete);
|
|
769
|
-
const ingestBlock = ingestFailed[0]?.hint;
|
|
770
|
-
const ready = ingest.ready && dispatchReady;
|
|
771
|
-
const nextAction = !ingest.ready ? ingestBlock ?? "Complete SDK ingest setup." : !dispatchReady ? dispatchBlock ?? "Complete dispatch preflight in Settings \u2192 Integrations." : "All setup checks pass \u2014 submit a test report to confirm end-to-end.";
|
|
772
|
-
const summary = ready ? "Mushi ingest and dispatch setup look healthy." : !ingest.ready ? `Ingest incomplete \u2014 ${ingestFailed.map((s) => s.label).join(", ")}.` : "Ingest OK; dispatch preflight still has blockers.";
|
|
773
|
-
return jsonResult({
|
|
774
|
-
mode: "full",
|
|
775
|
-
ready,
|
|
776
|
-
summary,
|
|
777
|
-
nextAction,
|
|
778
|
-
ingest: { ready: ingest.ready, steps: ingest.steps },
|
|
779
|
-
dispatch: resolvedId ? { ready: dispatchReady, checks: dispatchChecks } : null
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
);
|
|
783
|
-
server.registerTool(
|
|
784
|
-
"diagnose_connection",
|
|
785
|
-
{
|
|
786
|
-
title: titleOf("diagnose_connection"),
|
|
787
|
-
description: descOf("diagnose_connection"),
|
|
788
|
-
annotations: annotationsFor("diagnose_connection"),
|
|
789
|
-
inputSchema: {},
|
|
790
|
-
outputSchema: {
|
|
791
|
-
ready: z.boolean(),
|
|
792
|
-
healthOk: z.boolean(),
|
|
793
|
-
ingestReady: z.boolean(),
|
|
794
|
-
dispatchReady: z.boolean(),
|
|
795
|
-
endpoint: z.string().nullable(),
|
|
796
|
-
projectId: z.string().nullable(),
|
|
797
|
-
accessibleProjectCount: z.number().nullable(),
|
|
798
|
-
issues: z.array(z.unknown()),
|
|
799
|
-
nextAction: z.string(),
|
|
800
|
-
summary: z.string()
|
|
801
|
-
}
|
|
802
|
-
},
|
|
803
|
-
async () => {
|
|
804
|
-
const issues = [];
|
|
694
|
+
const connIssues = [];
|
|
695
|
+
let healthOk = false;
|
|
805
696
|
if (!apiKey?.startsWith("mushi_")) {
|
|
806
|
-
|
|
697
|
+
connIssues.push({
|
|
807
698
|
check: "mcp_api_key",
|
|
808
|
-
detail: "MCP server API key missing or malformed",
|
|
809
|
-
fix: "Run `mushi connect`
|
|
699
|
+
detail: "MCP server API key missing or malformed (expected prefix: mushi_)",
|
|
700
|
+
fix: "Run `mushi connect` or set MUSHI_API_KEY to a valid key in your MCP config."
|
|
810
701
|
});
|
|
811
702
|
}
|
|
812
703
|
if (!apiEndpoint) {
|
|
813
|
-
|
|
704
|
+
connIssues.push({
|
|
814
705
|
check: "mcp_endpoint",
|
|
815
706
|
detail: "No API endpoint configured",
|
|
816
|
-
fix: "Set MUSHI_API_ENDPOINT to your
|
|
707
|
+
fix: "Set MUSHI_API_ENDPOINT to your `.../functions/v1/api` URL in your MCP config."
|
|
817
708
|
});
|
|
818
|
-
}
|
|
819
|
-
let healthOk = false;
|
|
820
|
-
if (apiEndpoint) {
|
|
709
|
+
} else {
|
|
821
710
|
try {
|
|
822
711
|
const healthRes = await doFetch(`${apiEndpoint.replace(/\/$/, "")}/health`, {
|
|
823
712
|
signal: AbortSignal.timeout(5e3)
|
|
824
713
|
});
|
|
825
714
|
healthOk = healthRes.status === 200;
|
|
826
715
|
if (!healthOk) {
|
|
827
|
-
|
|
716
|
+
connIssues.push({
|
|
828
717
|
check: "endpoint_health",
|
|
829
718
|
detail: `GET /health \u2192 HTTP ${healthRes.status}`,
|
|
830
719
|
fix: "Verify MUSHI_API_ENDPOINT and that the Supabase edge function is deployed."
|
|
831
720
|
});
|
|
832
721
|
}
|
|
833
722
|
} catch (err) {
|
|
834
|
-
|
|
723
|
+
connIssues.push({
|
|
835
724
|
check: "endpoint_health",
|
|
836
725
|
detail: err instanceof Error ? err.message : String(err),
|
|
837
|
-
fix: "Check network connectivity and
|
|
726
|
+
fix: "Check network connectivity and MUSHI_API_ENDPOINT in your MCP config."
|
|
838
727
|
});
|
|
839
728
|
}
|
|
840
729
|
}
|
|
841
|
-
let ingestReady = false;
|
|
842
|
-
let dispatchReady = false;
|
|
843
730
|
let accessibleProjectCount = null;
|
|
844
|
-
if (!
|
|
731
|
+
if (!resolvedId && healthOk) {
|
|
845
732
|
try {
|
|
846
|
-
const overview = await apiCall(
|
|
733
|
+
const overview = await apiCall(
|
|
734
|
+
"/v1/admin/mcp/account-overview"
|
|
735
|
+
);
|
|
847
736
|
accessibleProjectCount = overview.total;
|
|
848
737
|
if (overview.total === 0) {
|
|
849
|
-
|
|
738
|
+
connIssues.push({
|
|
850
739
|
check: "no_accessible_projects",
|
|
851
740
|
detail: "API key has no accessible projects",
|
|
852
|
-
fix: "Mint an API key on a project (console \u2192 Projects \u2192 API Keys) or add MUSHI_PROJECT_ID
|
|
741
|
+
fix: "Mint an API key on a project (console \u2192 Projects \u2192 API Keys) or add MUSHI_PROJECT_ID."
|
|
853
742
|
});
|
|
854
743
|
}
|
|
855
744
|
} catch {
|
|
856
745
|
}
|
|
857
746
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
issues.push({
|
|
864
|
-
check: "ingest_setup",
|
|
865
|
-
detail: `Incomplete: ${failed.map((s) => s.label).join(", ")}`,
|
|
866
|
-
fix: failed[0]?.hint ?? "Paste the SDK snippet, start your dev server, submit a test report."
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
} catch (err) {
|
|
870
|
-
issues.push({
|
|
871
|
-
check: "ingest_setup",
|
|
872
|
-
detail: err instanceof Error ? err.message : String(err),
|
|
873
|
-
fix: "Confirm API key is active for this project (Projects \u2192 API Keys)."
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
if (projectId) {
|
|
747
|
+
const ingest = await apiCall("/v1/sync/ingest-setup");
|
|
748
|
+
let dispatchReady = true;
|
|
749
|
+
let dispatchBlock;
|
|
750
|
+
let dispatchChecks = null;
|
|
751
|
+
if (resolvedId) {
|
|
877
752
|
try {
|
|
878
|
-
const preflight = await apiCall(`/v1/admin/projects/${
|
|
753
|
+
const preflight = await apiCall(`/v1/admin/projects/${resolvedId}/preflight`);
|
|
879
754
|
dispatchReady = preflight.ready;
|
|
755
|
+
dispatchChecks = preflight.checks;
|
|
880
756
|
if (!preflight.ready) {
|
|
881
757
|
const failed = preflight.checks.filter((c) => !c.ready);
|
|
882
|
-
|
|
883
|
-
check: "dispatch_preflight",
|
|
884
|
-
detail: `Blocked: ${failed.map((c) => c.label).join(", ")}`,
|
|
885
|
-
fix: failed[0]?.hint ?? "Open Settings \u2192 Integrations and complete GitHub + BYOK setup."
|
|
886
|
-
});
|
|
758
|
+
dispatchBlock = failed[0]?.hint;
|
|
887
759
|
}
|
|
888
|
-
} catch {
|
|
760
|
+
} catch (err) {
|
|
761
|
+
dispatchReady = false;
|
|
762
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
763
|
+
dispatchBlock = `Could not run dispatch preflight \u2014 ${detail}`;
|
|
764
|
+
apiLog.warn("diagnose_setup dispatch preflight failed", { error: detail });
|
|
889
765
|
}
|
|
890
766
|
}
|
|
891
|
-
const
|
|
892
|
-
const
|
|
767
|
+
const ingestFailed = ingest.steps.filter((s) => s.required && !s.complete);
|
|
768
|
+
const ingestBlock = ingestFailed[0]?.hint;
|
|
769
|
+
const connOk = connIssues.length === 0;
|
|
770
|
+
const ready = connOk && ingest.ready && dispatchReady;
|
|
771
|
+
const nextAction = !connOk ? connIssues[0]?.fix ?? "Fix the connection issue above." : !ingest.ready ? ingestBlock ?? "Complete SDK ingest setup." : !dispatchReady ? dispatchBlock ?? "Complete dispatch preflight in Settings \u2192 Integrations." : "All setup checks pass \u2014 submit a test report to confirm end-to-end.";
|
|
772
|
+
const summary = ready ? "Mushi connection, ingest, and dispatch setup look healthy." : !connOk ? `Connection issue \u2014 ${connIssues[0]?.check}: ${connIssues[0]?.detail}` : !ingest.ready ? `Ingest incomplete \u2014 ${ingestFailed.map((s) => s.label).join(", ")}.` : "Ingest OK; dispatch preflight still has blockers.";
|
|
893
773
|
return jsonResult({
|
|
774
|
+
mode: "full",
|
|
894
775
|
ready,
|
|
895
|
-
|
|
896
|
-
ingestReady,
|
|
897
|
-
dispatchReady,
|
|
898
|
-
endpoint: apiEndpoint ?? null,
|
|
899
|
-
projectId: projectId ?? null,
|
|
900
|
-
accessibleProjectCount,
|
|
901
|
-
issues,
|
|
776
|
+
summary,
|
|
902
777
|
nextAction,
|
|
903
|
-
|
|
778
|
+
connection: {
|
|
779
|
+
healthOk,
|
|
780
|
+
endpoint: apiEndpoint ?? null,
|
|
781
|
+
projectId: resolvedId ?? null,
|
|
782
|
+
accessibleProjectCount,
|
|
783
|
+
issues: connIssues
|
|
784
|
+
},
|
|
785
|
+
ingest: { ready: ingest.ready, steps: ingest.steps },
|
|
786
|
+
dispatch: resolvedId ? { ready: dispatchReady, checks: dispatchChecks } : null
|
|
904
787
|
});
|
|
905
788
|
}
|
|
906
789
|
);
|
|
@@ -939,7 +822,7 @@ function createMushiServer(config) {
|
|
|
939
822
|
title: titleOf("list_projects"),
|
|
940
823
|
description: descOf("list_projects"),
|
|
941
824
|
annotations: annotationsFor("list_projects"),
|
|
942
|
-
inputSchema:
|
|
825
|
+
inputSchema: NO_ARG_INPUT,
|
|
943
826
|
outputSchema: {
|
|
944
827
|
projects: z.array(z.unknown()),
|
|
945
828
|
total: z.number().optional(),
|
|
@@ -960,7 +843,7 @@ function createMushiServer(config) {
|
|
|
960
843
|
title: titleOf("get_account_overview"),
|
|
961
844
|
description: descOf("get_account_overview"),
|
|
962
845
|
annotations: annotationsFor("get_account_overview"),
|
|
963
|
-
inputSchema:
|
|
846
|
+
inputSchema: NO_ARG_INPUT,
|
|
964
847
|
outputSchema: {
|
|
965
848
|
projects: z.array(z.unknown()),
|
|
966
849
|
total: z.number(),
|
|
@@ -1079,7 +962,7 @@ function createMushiServer(config) {
|
|
|
1079
962
|
description: descOf("triage_issue"),
|
|
1080
963
|
annotations: annotationsFor("triage_issue"),
|
|
1081
964
|
inputSchema: {
|
|
1082
|
-
report_id: z.string().describe("Report UUID to triage."),
|
|
965
|
+
report_id: z.string().uuid().describe("Report UUID to triage."),
|
|
1083
966
|
project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
|
|
1084
967
|
include_logs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
|
|
1085
968
|
},
|
|
@@ -1140,11 +1023,31 @@ function createMushiServer(config) {
|
|
|
1140
1023
|
args: { reportId: args.report_id }
|
|
1141
1024
|
});
|
|
1142
1025
|
}
|
|
1026
|
+
const partial_errors = [];
|
|
1027
|
+
if (reportRes.status === "rejected") {
|
|
1028
|
+
partial_errors.push(`report: ${String(reportRes.reason)}`);
|
|
1029
|
+
}
|
|
1030
|
+
if (evidenceRes.status === "rejected") {
|
|
1031
|
+
partial_errors.push(`timeline: ${String(evidenceRes.reason)}`);
|
|
1032
|
+
}
|
|
1033
|
+
if (similarRes.status === "rejected") {
|
|
1034
|
+
partial_errors.push(`similarity: ${String(similarRes.reason)}`);
|
|
1035
|
+
}
|
|
1036
|
+
if (fixCtxRes.status === "rejected") {
|
|
1037
|
+
partial_errors.push(`fix_context: ${String(fixCtxRes.reason)}`);
|
|
1038
|
+
}
|
|
1039
|
+
if (blastRes.status === "rejected") {
|
|
1040
|
+
partial_errors.push(`blast_radius: ${String(blastRes.reason)}`);
|
|
1041
|
+
}
|
|
1042
|
+
if (includeLogs && logsRes.status === "rejected") {
|
|
1043
|
+
partial_errors.push(`recent_logs: ${String(logsRes.reason)}`);
|
|
1044
|
+
}
|
|
1143
1045
|
const result = {
|
|
1144
1046
|
report_id: args.report_id,
|
|
1145
1047
|
severity,
|
|
1146
1048
|
category,
|
|
1147
1049
|
status,
|
|
1050
|
+
partial_errors,
|
|
1148
1051
|
report: reportRes.status === "fulfilled" ? reportRes.value : { error: String(reportRes.reason) },
|
|
1149
1052
|
reporter_thread: evidenceRes.status === "fulfilled" ? evidenceRes.value : null,
|
|
1150
1053
|
similar_bugs: similarRes.status === "fulfilled" ? similarRes.value : null,
|
|
@@ -1209,7 +1112,7 @@ function createMushiServer(config) {
|
|
|
1209
1112
|
description: descOf("dispatch_fix"),
|
|
1210
1113
|
annotations: annotationsFor("dispatch_fix"),
|
|
1211
1114
|
inputSchema: {
|
|
1212
|
-
reportId: z.string().describe("Report UUID to fix"),
|
|
1115
|
+
reportId: z.string().uuid().describe("Report UUID to fix"),
|
|
1213
1116
|
agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
|
|
1214
1117
|
idempotencyKey: z.string().uuid().optional().describe("Optional RFC 4122 UUID. Resend the same key to safely retry without dispatching a duplicate fix job (Idempotency-Key IETF draft)."),
|
|
1215
1118
|
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.")
|
|
@@ -1293,8 +1196,8 @@ function createMushiServer(config) {
|
|
|
1293
1196
|
server.registerTool(
|
|
1294
1197
|
"merge_fix",
|
|
1295
1198
|
{
|
|
1296
|
-
title: "
|
|
1297
|
-
description: "
|
|
1199
|
+
title: titleOf("merge_fix"),
|
|
1200
|
+
description: descOf("merge_fix"),
|
|
1298
1201
|
annotations: annotationsFor("merge_fix"),
|
|
1299
1202
|
inputSchema: {
|
|
1300
1203
|
fixId: z.string().describe("Fix attempt UUID"),
|
|
@@ -1312,8 +1215,8 @@ function createMushiServer(config) {
|
|
|
1312
1215
|
server.registerTool(
|
|
1313
1216
|
"refresh_ci",
|
|
1314
1217
|
{
|
|
1315
|
-
title: "
|
|
1316
|
-
description: "
|
|
1218
|
+
title: titleOf("refresh_ci"),
|
|
1219
|
+
description: descOf("refresh_ci"),
|
|
1317
1220
|
annotations: annotationsFor("refresh_ci"),
|
|
1318
1221
|
inputSchema: {
|
|
1319
1222
|
fixId: z.string().describe("Fix attempt UUID")
|
|
@@ -1595,6 +1498,22 @@ function createMushiServer(config) {
|
|
|
1595
1498
|
};
|
|
1596
1499
|
}
|
|
1597
1500
|
);
|
|
1501
|
+
server.registerTool(
|
|
1502
|
+
"activation_status",
|
|
1503
|
+
{
|
|
1504
|
+
title: titleOf("activation_status"),
|
|
1505
|
+
description: descOf("activation_status"),
|
|
1506
|
+
annotations: annotationsFor("activation_status"),
|
|
1507
|
+
inputSchema: {
|
|
1508
|
+
project_id: z.string().optional().describe("Project UUID (defaults to the configured project).")
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
async (args) => {
|
|
1512
|
+
const pid = args.project_id ?? projectId;
|
|
1513
|
+
const qs = pid ? `?project_id=${encodeURIComponent(pid)}` : "";
|
|
1514
|
+
return jsonResult(await apiCall(`/v1/admin/activation${qs}`));
|
|
1515
|
+
}
|
|
1516
|
+
);
|
|
1598
1517
|
server.resource(
|
|
1599
1518
|
"activation_status",
|
|
1600
1519
|
"mushi://activation",
|
|
@@ -1693,7 +1612,7 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
|
|
|
1693
1612
|
text: `You are a Mushi onboarding copilot. Use the MCP tools:
|
|
1694
1613
|
1. Read mushi://activation for unified setup posture.
|
|
1695
1614
|
2. Read project://integration-health if GitHub or Sentry looks blocked.
|
|
1696
|
-
3. Call
|
|
1615
|
+
3. Call diagnose_setup if the resource is unavailable.
|
|
1697
1616
|
|
|
1698
1617
|
Then output:
|
|
1699
1618
|
- **Status:** one sentence on what is done vs blocked
|
|
@@ -2126,36 +2045,6 @@ Be specific. No generic advice.`
|
|
|
2126
2045
|
return jsonText({ ok: true, message: `Step ${step_index} \u2192 ${args.status}` });
|
|
2127
2046
|
}
|
|
2128
2047
|
);
|
|
2129
|
-
server.registerTool(
|
|
2130
|
-
"get_activation_status",
|
|
2131
|
-
{
|
|
2132
|
-
title: titleOf("get_activation_status"),
|
|
2133
|
-
description: descOf("get_activation_status"),
|
|
2134
|
-
annotations: annotationsFor("get_activation_status"),
|
|
2135
|
-
inputSchema: {
|
|
2136
|
-
project_id: z.string().optional().describe("Optional project UUID override")
|
|
2137
|
-
}
|
|
2138
|
-
},
|
|
2139
|
-
async (args) => {
|
|
2140
|
-
const qs = args.project_id ? `?project_id=${encodeURIComponent(args.project_id)}` : "";
|
|
2141
|
-
const data = await apiCall(`/v1/admin/activation${qs}`);
|
|
2142
|
-
return jsonText(data);
|
|
2143
|
-
}
|
|
2144
|
-
);
|
|
2145
|
-
server.registerTool(
|
|
2146
|
-
"get_reporter_thread",
|
|
2147
|
-
{
|
|
2148
|
-
title: titleOf("get_reporter_thread"),
|
|
2149
|
-
description: descOf("get_reporter_thread"),
|
|
2150
|
-
annotations: annotationsFor("get_reporter_thread"),
|
|
2151
|
-
inputSchema: { reportId: z.string().describe("The report UUID") }
|
|
2152
|
-
},
|
|
2153
|
-
// The reporter thread is the `comments` lane of the unified report
|
|
2154
|
-
// timeline. There is no standalone admin `/comments` route (only the
|
|
2155
|
-
// reporter-token-gated `/v1/reporter/reports/:id/comments`), so we read the
|
|
2156
|
-
// admin-authed timeline, which also carries fix / QA / status lanes.
|
|
2157
|
-
async (args) => jsonText(await apiCall(`/v1/admin/reports/${args.reportId}/timeline`))
|
|
2158
|
-
);
|
|
2159
2048
|
server.registerTool(
|
|
2160
2049
|
"ask_codebase",
|
|
2161
2050
|
{
|
|
@@ -2372,6 +2261,39 @@ Be specific. No generic advice.`
|
|
|
2372
2261
|
return result;
|
|
2373
2262
|
});
|
|
2374
2263
|
}
|
|
2264
|
+
{
|
|
2265
|
+
const llAliasServer = server.server;
|
|
2266
|
+
const innerHandler = llAliasServer?._requestHandlers?.get("tools/call");
|
|
2267
|
+
if (typeof innerHandler === "function") {
|
|
2268
|
+
llAliasServer._requestHandlers.set("tools/call", async (...args) => {
|
|
2269
|
+
const req = args[0];
|
|
2270
|
+
const originalName = req?.params?.name;
|
|
2271
|
+
const canonicalName = originalName ? DEPRECATED_TOOL_ALIASES[originalName] : void 0;
|
|
2272
|
+
if (canonicalName) {
|
|
2273
|
+
const rewritten = {
|
|
2274
|
+
...args[0],
|
|
2275
|
+
params: { ...req?.params ?? {}, name: canonicalName }
|
|
2276
|
+
};
|
|
2277
|
+
const result = await innerHandler(rewritten, ...args.slice(1));
|
|
2278
|
+
if (result != null && typeof result === "object" && "content" in result && Array.isArray(result.content)) {
|
|
2279
|
+
const r = result;
|
|
2280
|
+
return {
|
|
2281
|
+
...r,
|
|
2282
|
+
content: [
|
|
2283
|
+
{
|
|
2284
|
+
type: "text",
|
|
2285
|
+
text: `\u26A0\uFE0F Deprecated: \`${originalName}\` was renamed to \`${canonicalName}\`. Update your agent config \u2014 this alias will be removed in the next release.`
|
|
2286
|
+
},
|
|
2287
|
+
...r.content
|
|
2288
|
+
]
|
|
2289
|
+
};
|
|
2290
|
+
}
|
|
2291
|
+
return result;
|
|
2292
|
+
}
|
|
2293
|
+
return innerHandler(...args);
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2375
2297
|
return server;
|
|
2376
2298
|
}
|
|
2377
2299
|
export {
|