@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/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-MM3IMFM4.js";
5
+ } from "./chunk-KNVB6HA7.js";
6
6
  import {
7
7
  MUSHI_SERVER_METADATA
8
- } from "./chunk-EHB2BGWX.js";
8
+ } from "./chunk-EANW5YTI.js";
9
9
  import {
10
+ DEPRECATED_TOOL_ALIASES,
10
11
  toolMatchesFeatures
11
- } from "./chunk-ZX3XIJ7S.js";
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", "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."
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
- "graph_neighborhood",
506
+ "get_graph_neighborhood",
505
507
  {
506
- title: titleOf("graph_neighborhood"),
507
- description: descOf("graph_neighborhood"),
508
- annotations: annotationsFor("graph_neighborhood"),
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("Traversal depth (default 2, max 4)")
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
- "graph_node_status",
525
+ "get_graph_node",
524
526
  {
525
- title: titleOf("graph_node_status"),
526
- description: descOf("graph_node_status"),
527
- annotations: annotationsFor("graph_node_status"),
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
- "inventory_get",
535
+ "get_inventory",
534
536
  {
535
- title: titleOf("inventory_get"),
536
- description: descOf("inventory_get"),
537
- annotations: annotationsFor("inventory_get"),
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 inventory_get");
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
- "inventory_diff",
551
+ "diff_inventory",
550
552
  {
551
- title: titleOf("inventory_diff"),
552
- description: descOf("inventory_diff"),
553
- annotations: annotationsFor("inventory_diff"),
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 inventory_diff");
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
- "inventory_findings",
570
+ "list_gate_findings",
569
571
  {
570
- title: titleOf("inventory_findings"),
571
- description: descOf("inventory_findings"),
572
- annotations: annotationsFor("inventory_findings"),
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 by gate id (e.g. dead_handler, status_claim)"),
576
- severity: z.string().optional().describe("Filter findings by severity")
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 inventory_findings");
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
- "fix_suggest",
592
+ "suggest_fix",
591
593
  {
592
- title: titleOf("fix_suggest"),
593
- description: descOf("fix_suggest"),
594
- annotations: annotationsFor("fix_suggest"),
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 jsonText({
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 ingest = await apiCall("/v1/sync/ingest-setup");
751
- let dispatchReady = true;
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
- issues.push({
697
+ connIssues.push({
807
698
  check: "mcp_api_key",
808
- detail: "MCP server API key missing or malformed",
809
- fix: "Run `mushi connect` to write MUSHI_API_KEY into .cursor/mcp.json, then restart the MCP server."
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
- issues.push({
704
+ connIssues.push({
814
705
  check: "mcp_endpoint",
815
706
  detail: "No API endpoint configured",
816
- fix: "Set MUSHI_API_ENDPOINT to your `\u2026/functions/v1/api` URL in .cursor/mcp.json."
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
- issues.push({
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
- issues.push({
723
+ connIssues.push({
835
724
  check: "endpoint_health",
836
725
  detail: err instanceof Error ? err.message : String(err),
837
- fix: "Check network connectivity and the endpoint URL in .cursor/mcp.json."
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 (!projectId && healthOk) {
731
+ if (!resolvedId && healthOk) {
845
732
  try {
846
- const overview = await apiCall("/v1/admin/mcp/account-overview");
733
+ const overview = await apiCall(
734
+ "/v1/admin/mcp/account-overview"
735
+ );
847
736
  accessibleProjectCount = overview.total;
848
737
  if (overview.total === 0) {
849
- issues.push({
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 to restrict to a single project."
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
- try {
859
- const ingest = await apiCall("/v1/sync/ingest-setup");
860
- ingestReady = ingest.ready;
861
- if (!ingest.ready) {
862
- const failed = ingest.steps.filter((s) => s.required && !s.complete);
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/${projectId}/preflight`);
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
- issues.push({
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 ready = issues.length === 0 && healthOk && ingestReady;
892
- const nextAction = issues[0]?.fix ?? (ready ? "Connection healthy \u2014 SDK ingest is working. Submit a report to confirm end-to-end." : "Run `mushi doctor` in your app repo for a full local checklist.");
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
- healthOk,
896
- ingestReady,
897
- dispatchReady,
898
- endpoint: apiEndpoint ?? null,
899
- projectId: projectId ?? null,
900
- accessibleProjectCount,
901
- issues,
776
+ summary,
902
777
  nextAction,
903
- summary: ready ? "MCP credentials valid; ingest pipeline ready." : `Connection issue \u2014 ${issues[0]?.check ?? "unknown"}: ${nextAction}`
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: "Merge fix PR",
1297
- description: "Squash-merge a fix attempt PR and mark the linked report fixed.",
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: "Refresh fix CI status",
1316
- description: "Pull the latest GitHub check-run status for a fix attempt.",
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 get_activation_status if the resource is unavailable.
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 {