@mushi-mushi/mcp 0.20.2 → 0.21.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
@@ -3,17 +3,17 @@ import {
3
3
  TDD_TOOL_CATALOG,
4
4
  TOOL_CATALOG,
5
5
  USE_MUSHI_INTENTS
6
- } from "./chunk-RVZIKP4X.js";
6
+ } from "./chunk-FNDGCFSJ.js";
7
7
  import {
8
8
  MUSHI_SERVER_METADATA
9
9
  } from "./chunk-EANW5YTI.js";
10
10
  import {
11
11
  DEPRECATED_TOOL_ALIASES,
12
12
  toolMatchesFeatures
13
- } from "./chunk-ZSVRLSW5.js";
13
+ } from "./chunk-XSL2FPLV.js";
14
14
 
15
15
  // src/server.ts
16
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
+ import { McpServer } from "@modelcontextprotocol/server";
17
17
  import { z } from "zod";
18
18
  import { createLogger } from "@mushi-mushi/core";
19
19
 
@@ -211,7 +211,7 @@ function safeStringify(value) {
211
211
 
212
212
  // src/server.ts
213
213
  installStdoutGuard();
214
- var NO_ARG_INPUT = {};
214
+ var NO_ARG_INPUT = z.object({});
215
215
  var DEFAULT_API_TIMEOUT_MS = 15e3;
216
216
  var MIN_API_TIMEOUT_MS = 250;
217
217
  var MAX_API_TIMEOUT_MS = 3e5;
@@ -485,7 +485,7 @@ function createMushiServer(config) {
485
485
  title: titleOf("get_recent_reports"),
486
486
  description: descOf("get_recent_reports"),
487
487
  annotations: annotationsFor("get_recent_reports"),
488
- inputSchema: {
488
+ inputSchema: z.object({
489
489
  status: z.string().optional().describe("Filter by status: new, classified, grouped, fixing, fixed, dismissed"),
490
490
  category: z.string().optional().describe("Filter by category: bug, slow, visual, confusing, other"),
491
491
  severity: z.string().optional().describe("Filter by severity: critical, high, medium, low"),
@@ -493,11 +493,11 @@ function createMushiServer(config) {
493
493
  project_id: z.string().optional().describe(
494
494
  "Project UUID \u2014 defaults to the server-configured project. Useful when you have multiple projects and want to query a specific one by its ID. Get IDs by calling list_projects or get_account_overview first."
495
495
  )
496
- },
497
- outputSchema: {
496
+ }),
497
+ outputSchema: z.object({
498
498
  reports: z.array(z.unknown()),
499
499
  total: z.number()
500
- }
500
+ })
501
501
  },
502
502
  async (args) => {
503
503
  const params = new URLSearchParams();
@@ -522,11 +522,11 @@ function createMushiServer(config) {
522
522
  title: titleOf("get_report_detail"),
523
523
  description: descOf("get_report_detail"),
524
524
  annotations: annotationsFor("get_report_detail"),
525
- inputSchema: {
525
+ inputSchema: z.object({
526
526
  reportId: z.string().describe("The report UUID"),
527
527
  project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
528
- },
529
- outputSchema: { report: z.unknown() }
528
+ }),
529
+ outputSchema: z.object({ report: z.unknown() })
530
530
  },
531
531
  async (args) => {
532
532
  const { headers } = await projectScopeHeaders(args.project_id);
@@ -549,7 +549,7 @@ function createMushiServer(config) {
549
549
  title: titleOf("get_report_timeline"),
550
550
  description: descOf("get_report_timeline"),
551
551
  annotations: annotationsFor("get_report_timeline"),
552
- inputSchema: { reportId: z.string().describe("The report UUID") }
552
+ inputSchema: z.object({ reportId: z.string().describe("The report UUID") })
553
553
  },
554
554
  async (args) => jsonText(await apiCall(`/v1/sync/reports/${args.reportId}/timeline`))
555
555
  );
@@ -569,14 +569,14 @@ function createMushiServer(config) {
569
569
  title: titleOf("search_reports"),
570
570
  description: descOf("search_reports"),
571
571
  annotations: annotationsFor("search_reports"),
572
- inputSchema: {
572
+ inputSchema: z.object({
573
573
  query: z.string().describe("Natural-language search text or component path"),
574
574
  limit: z.number().optional().describe("Max results (default 10, max 50)"),
575
575
  threshold: z.number().optional().describe("Similarity threshold 0..1, default 0.2")
576
- },
577
- outputSchema: {
576
+ }),
577
+ outputSchema: z.object({
578
578
  results: z.array(z.unknown())
579
- }
579
+ })
580
580
  },
581
581
  async (args) => {
582
582
  const data = await apiCall("/v1/admin/reports/similarity", {
@@ -597,13 +597,13 @@ function createMushiServer(config) {
597
597
  title: titleOf("get_similar_bugs"),
598
598
  description: descOf("get_similar_bugs"),
599
599
  annotations: annotationsFor("get_similar_bugs"),
600
- inputSchema: {
600
+ inputSchema: z.object({
601
601
  query: z.string().describe("Component name, page path, or bug description"),
602
602
  limit: z.number().optional().describe("Max results (default 5, max 20)")
603
- },
604
- outputSchema: {
603
+ }),
604
+ outputSchema: z.object({
605
605
  results: z.array(z.unknown())
606
- }
606
+ })
607
607
  },
608
608
  async (args) => {
609
609
  const data = await apiCall("/v1/admin/reports/similarity", {
@@ -624,18 +624,18 @@ function createMushiServer(config) {
624
624
  title: titleOf("get_fix_context"),
625
625
  description: descOf("get_fix_context"),
626
626
  annotations: annotationsFor("get_fix_context"),
627
- inputSchema: {
627
+ inputSchema: z.object({
628
628
  reportId: z.string().describe("The report UUID to fix"),
629
629
  project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
630
- },
631
- outputSchema: {
630
+ }),
631
+ outputSchema: z.object({
632
632
  report: z.unknown(),
633
633
  fixPrompt: z.unknown(),
634
634
  reproductionSteps: z.unknown(),
635
635
  component: z.unknown(),
636
636
  rootCause: z.unknown(),
637
637
  bugOntologyTags: z.unknown()
638
- }
638
+ })
639
639
  },
640
640
  async (args) => {
641
641
  const { headers } = await projectScopeHeaders(args.project_id);
@@ -664,10 +664,10 @@ function createMushiServer(config) {
664
664
  title: titleOf("get_fix_timeline"),
665
665
  description: descOf("get_fix_timeline"),
666
666
  annotations: annotationsFor("get_fix_timeline"),
667
- inputSchema: {
667
+ inputSchema: z.object({
668
668
  fixId: z.string().describe("fix_attempt UUID"),
669
669
  project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
670
- }
670
+ })
671
671
  },
672
672
  async (args) => {
673
673
  const { headers } = await projectScopeHeaders(args.project_id);
@@ -680,7 +680,7 @@ function createMushiServer(config) {
680
680
  title: titleOf("get_blast_radius"),
681
681
  description: descOf("get_blast_radius"),
682
682
  annotations: annotationsFor("get_blast_radius"),
683
- inputSchema: { nodeId: z.string().describe("Graph node UUID") }
683
+ inputSchema: z.object({ nodeId: z.string().describe("Graph node UUID") })
684
684
  },
685
685
  async (args) => jsonText(await apiCall(`/v1/admin/graph/blast-radius/${args.nodeId}`))
686
686
  );
@@ -690,14 +690,14 @@ function createMushiServer(config) {
690
690
  title: titleOf("get_knowledge_graph"),
691
691
  description: descOf("get_knowledge_graph"),
692
692
  annotations: annotationsFor("get_knowledge_graph"),
693
- inputSchema: {
693
+ inputSchema: z.object({
694
694
  seed: z.string().describe("Starting graph node id or human-readable label to traverse from"),
695
695
  depth: z.number().optional().describe("BFS hops outward from seed \u2014 clamped to 4 in the handler (default 2)")
696
- },
697
- outputSchema: {
696
+ }),
697
+ outputSchema: z.object({
698
698
  nodes: z.array(z.unknown()).describe("Graph nodes within the depth budget"),
699
699
  edges: z.array(z.unknown()).describe("Edges connecting the returned nodes")
700
- }
700
+ })
701
701
  },
702
702
  async (args) => {
703
703
  const params = new URLSearchParams({
@@ -716,16 +716,16 @@ function createMushiServer(config) {
716
716
  title: titleOf("get_graph_neighborhood"),
717
717
  description: descOf("get_graph_neighborhood"),
718
718
  annotations: annotationsFor("get_graph_neighborhood"),
719
- inputSchema: {
719
+ inputSchema: z.object({
720
720
  seed: z.string().describe(
721
721
  "Starting graph node id or label to expand around (e.g. an inventory Action id or component name)"
722
722
  ),
723
723
  depth: z.number().int().min(1).max(4).optional().describe("BFS hops to traverse outward. Default 2; clamped to a max of 4.")
724
- },
725
- outputSchema: {
724
+ }),
725
+ outputSchema: z.object({
726
726
  nodes: z.array(z.unknown()).describe("Graph nodes within the depth budget"),
727
727
  edges: z.array(z.unknown()).describe("Edges connecting the returned nodes")
728
- }
728
+ })
729
729
  },
730
730
  async (args) => {
731
731
  const params = new URLSearchParams({
@@ -744,12 +744,12 @@ function createMushiServer(config) {
744
744
  title: titleOf("get_graph_node"),
745
745
  description: descOf("get_graph_node"),
746
746
  annotations: annotationsFor("get_graph_node"),
747
- inputSchema: {
747
+ inputSchema: z.object({
748
748
  nodeId: z.string().describe("The graph_nodes.id of the node to fetch (UUID).")
749
- },
750
- outputSchema: {
749
+ }),
750
+ outputSchema: z.object({
751
751
  node: z.unknown().describe("Single graph_nodes row including metadata (Action nodes carry v2 status)")
752
- }
752
+ })
753
753
  },
754
754
  async (args) => {
755
755
  const data = await apiCall(`/v1/admin/graph/node/${args.nodeId}`);
@@ -762,9 +762,9 @@ function createMushiServer(config) {
762
762
  title: titleOf("get_inventory"),
763
763
  description: descOf("get_inventory"),
764
764
  annotations: annotationsFor("get_inventory"),
765
- inputSchema: {
765
+ inputSchema: z.object({
766
766
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to the server-configured project when omitted")
767
- }
767
+ })
768
768
  },
769
769
  async (args) => {
770
770
  const pid = args.projectId ?? projectId;
@@ -779,11 +779,11 @@ function createMushiServer(config) {
779
779
  title: titleOf("diff_inventory"),
780
780
  description: descOf("diff_inventory"),
781
781
  annotations: annotationsFor("diff_inventory"),
782
- inputSchema: {
782
+ inputSchema: z.object({
783
783
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project"),
784
784
  fromSha: z.string().describe("Older commit SHA (the baseline to diff from)"),
785
785
  toSha: z.string().describe("Newer commit SHA (the candidate to diff to)")
786
- }
786
+ })
787
787
  },
788
788
  async (args) => {
789
789
  const pid = args.projectId ?? projectId;
@@ -799,15 +799,15 @@ function createMushiServer(config) {
799
799
  title: titleOf("list_gate_findings"),
800
800
  description: descOf("list_gate_findings"),
801
801
  annotations: annotationsFor("list_gate_findings"),
802
- inputSchema: {
802
+ inputSchema: z.object({
803
803
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project"),
804
804
  gate: z.string().optional().describe("Filter to one gate id: dead-handler | mock-leak | crawl | status-claim"),
805
805
  severity: z.string().optional().describe("Minimum severity to include: low | medium | high | critical")
806
- },
807
- outputSchema: {
806
+ }),
807
+ outputSchema: z.object({
808
808
  runs: z.array(z.unknown()).describe("Recent gate_runs rows, newest first"),
809
809
  findings: z.array(z.unknown()).describe("gate_findings rows for those runs")
810
- }
810
+ })
811
811
  },
812
812
  async (args) => {
813
813
  const pid = args.projectId ?? projectId;
@@ -833,18 +833,20 @@ function createMushiServer(config) {
833
833
  title: titleOf("suggest_fix"),
834
834
  description: descOf("suggest_fix"),
835
835
  annotations: annotationsFor("suggest_fix"),
836
- inputSchema: {
836
+ inputSchema: z.object({
837
837
  reportId: z.string().describe("Report UUID to read the Stage-2 suggested-fix slice for")
838
- },
839
- outputSchema: {
838
+ }),
839
+ outputSchema: z.object({
840
840
  reportId: z.string().describe("The report this slice was read from"),
841
841
  rootCause: z.unknown().describe("Stage-2 root-cause hint, or null if not yet classified"),
842
842
  suggestedFix: z.unknown().describe("Stage-2 suggested fix, or null if not yet classified"),
843
843
  reproductionSteps: z.unknown().describe("Reproduction steps recorded on the report (array; [] if none)"),
844
844
  summary: z.unknown().describe("One-line report summary, or null"),
845
845
  component: z.unknown().describe("Component/page the bug was attributed to, or null"),
846
- note: z.string().optional().describe("Present when no Stage-2 analysis exists \u2014 explains why and what to call instead")
847
- }
846
+ note: z.string().optional().describe(
847
+ "Present when no Stage-2 analysis exists \u2014 explains why and what to call instead"
848
+ )
849
+ })
848
850
  },
849
851
  async (args) => {
850
852
  const report = await apiCall(`/v1/admin/reports/${args.reportId}`);
@@ -876,11 +878,11 @@ function createMushiServer(config) {
876
878
  title: titleOf("run_nl_query"),
877
879
  description: descOf("run_nl_query"),
878
880
  annotations: annotationsFor("run_nl_query"),
879
- inputSchema: {
881
+ inputSchema: z.object({
880
882
  question: z.string().describe(
881
883
  'Question in plain English, e.g. "Which components had the most critical bugs this week?"'
882
884
  )
883
- }
885
+ })
884
886
  },
885
887
  async (args) => {
886
888
  const data = await apiCall("/v1/admin/query", {
@@ -896,14 +898,14 @@ function createMushiServer(config) {
896
898
  title: titleOf("diagnose_setup"),
897
899
  description: descOf("diagnose_setup"),
898
900
  annotations: annotationsFor("diagnose_setup"),
899
- inputSchema: {
901
+ inputSchema: z.object({
900
902
  mode: z.enum(["full", "ingest", "dispatch"]).optional().describe(
901
903
  "full (default) = ingest + dispatch; ingest = SDK pipeline only; dispatch = fix preflight only."
902
904
  ),
903
905
  project_id: z.string().optional().describe("Project UUID for dispatch checks (defaults to configured project)."),
904
906
  projectId: z.string().optional().describe("Alias for project_id.")
905
- },
906
- outputSchema: {
907
+ }),
908
+ outputSchema: z.object({
907
909
  mode: z.string(),
908
910
  ready: z.boolean(),
909
911
  summary: z.string(),
@@ -911,7 +913,7 @@ function createMushiServer(config) {
911
913
  ingest: z.unknown().optional(),
912
914
  dispatch: z.unknown().optional(),
913
915
  connection: z.unknown().optional()
914
- }
916
+ })
915
917
  },
916
918
  async (args) => {
917
919
  const mode = args.mode ?? "full";
@@ -1050,17 +1052,71 @@ function createMushiServer(config) {
1050
1052
  });
1051
1053
  }
1052
1054
  );
1055
+ server.registerTool(
1056
+ "check_sdk_version",
1057
+ {
1058
+ title: titleOf("check_sdk_version"),
1059
+ description: descOf("check_sdk_version"),
1060
+ annotations: annotationsFor("check_sdk_version"),
1061
+ inputSchema: z.object({
1062
+ package: z.string().optional().describe(
1063
+ "npm package name (default @mushi-mushi/web). Examples: @mushi-mushi/web, @mushi-mushi/node, @mushi-mushi/core."
1064
+ ),
1065
+ current: z.string().optional().describe("Installed version from package.json, if known (e.g. 1.27.0).")
1066
+ }),
1067
+ outputSchema: z.object({
1068
+ package: z.string(),
1069
+ latest: z.string().optional(),
1070
+ current: z.string().optional(),
1071
+ outdated: z.boolean().optional(),
1072
+ suggestedActions: z.array(
1073
+ z.object({
1074
+ type: z.literal("tool_call"),
1075
+ toolName: z.string(),
1076
+ arguments: z.record(z.string(), z.unknown()),
1077
+ reason: z.string()
1078
+ })
1079
+ ).optional()
1080
+ })
1081
+ },
1082
+ async (args) => {
1083
+ const pkg = args.package?.trim() || "@mushi-mushi/web";
1084
+ const query = new URLSearchParams({ package: pkg }).toString();
1085
+ const data = await apiCall(
1086
+ `/v1/sdk/latest-version?${query}`
1087
+ );
1088
+ const latest = data.latest ?? data.version;
1089
+ const current = args.current;
1090
+ const outdated = current && latest ? current !== latest : void 0;
1091
+ return jsonResult({
1092
+ package: data.package ?? pkg,
1093
+ latest,
1094
+ current,
1095
+ outdated,
1096
+ ...outdated ? {
1097
+ suggestedActions: [
1098
+ {
1099
+ type: "tool_call",
1100
+ toolName: "search_mushi_docs",
1101
+ arguments: { query: `${pkg} upgrade mushi-sdk-upgrade` },
1102
+ reason: "Read current upgrade notes, then apply the mushi-sdk-upgrade skill. This tool does not bump the pin."
1103
+ }
1104
+ ]
1105
+ } : {}
1106
+ });
1107
+ }
1108
+ );
1053
1109
  server.registerTool(
1054
1110
  "search_mushi_docs",
1055
1111
  {
1056
1112
  title: titleOf("search_mushi_docs"),
1057
1113
  description: descOf("search_mushi_docs"),
1058
1114
  annotations: annotationsFor("search_mushi_docs"),
1059
- inputSchema: {
1115
+ inputSchema: z.object({
1060
1116
  query: z.string().describe("Keywords to search official Mushi docs (guides, MCP, inventory, QA)."),
1061
1117
  limit: z.number().int().min(1).max(20).optional().describe("Max results (default 8).")
1062
- },
1063
- outputSchema: {
1118
+ }),
1119
+ outputSchema: z.object({
1064
1120
  query: z.string(),
1065
1121
  results: z.array(
1066
1122
  z.object({
@@ -1070,7 +1126,7 @@ function createMushiServer(config) {
1070
1126
  score: z.number()
1071
1127
  })
1072
1128
  )
1073
- }
1129
+ })
1074
1130
  },
1075
1131
  async (args) => {
1076
1132
  const query = args.query ?? "";
@@ -1091,11 +1147,11 @@ function createMushiServer(config) {
1091
1147
  description: descOf("list_projects"),
1092
1148
  annotations: annotationsFor("list_projects"),
1093
1149
  inputSchema: NO_ARG_INPUT,
1094
- outputSchema: {
1150
+ outputSchema: z.object({
1095
1151
  projects: z.array(z.unknown()),
1096
1152
  total: z.number().optional(),
1097
1153
  _multi_project_hint: z.string().optional()
1098
- }
1154
+ })
1099
1155
  },
1100
1156
  async () => {
1101
1157
  const data = await apiCall("/v1/admin/mcp/projects");
@@ -1112,7 +1168,7 @@ function createMushiServer(config) {
1112
1168
  description: descOf("get_account_overview"),
1113
1169
  annotations: annotationsFor("get_account_overview"),
1114
1170
  inputSchema: NO_ARG_INPUT,
1115
- outputSchema: {
1171
+ outputSchema: z.object({
1116
1172
  projects: z.array(z.unknown()),
1117
1173
  total: z.number(),
1118
1174
  active_project_id: z.string().nullable(),
@@ -1120,7 +1176,7 @@ function createMushiServer(config) {
1120
1176
  resourceCount: z.number().optional(),
1121
1177
  promptCount: z.number().optional(),
1122
1178
  multi_project_hint: z.string()
1123
- }
1179
+ })
1124
1180
  },
1125
1181
  async () => {
1126
1182
  const data = await apiCall("/v1/admin/mcp/account-overview");
@@ -1142,9 +1198,9 @@ function createMushiServer(config) {
1142
1198
  title: titleOf("get_project_context"),
1143
1199
  description: descOf("get_project_context"),
1144
1200
  annotations: annotationsFor("get_project_context"),
1145
- inputSchema: {
1201
+ inputSchema: z.object({
1146
1202
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project.")
1147
- }
1203
+ })
1148
1204
  },
1149
1205
  async (args) => {
1150
1206
  const pid = args.project_id ?? projectId;
@@ -1166,13 +1222,13 @@ function createMushiServer(config) {
1166
1222
  title: titleOf("get_pipeline_logs"),
1167
1223
  description: descOf("get_pipeline_logs"),
1168
1224
  annotations: annotationsFor("get_pipeline_logs"),
1169
- inputSchema: {
1225
+ inputSchema: z.object({
1170
1226
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
1171
1227
  service: z.enum(["fix-worker", "qa-story-runner", "pipeline", "all"]).optional().describe("Pipeline service to filter (default: all)."),
1172
1228
  since: z.string().optional().describe("ISO-8601 timestamp \u2014 return only entries after this time."),
1173
1229
  limit: z.number().optional().describe("Max entries to return (default 50, max 200)."),
1174
1230
  level: z.enum(["info", "warn", "error", "fatal"]).optional().describe("Minimum severity level (default: warn).")
1175
- }
1231
+ })
1176
1232
  },
1177
1233
  async (args) => {
1178
1234
  const pid = args.project_id ?? projectId;
@@ -1192,9 +1248,9 @@ function createMushiServer(config) {
1192
1248
  title: titleOf("get_report_evidence"),
1193
1249
  description: descOf("get_report_evidence"),
1194
1250
  annotations: annotationsFor("get_report_evidence"),
1195
- inputSchema: {
1251
+ inputSchema: z.object({
1196
1252
  report_id: z.string().describe("Report UUID.")
1197
- }
1253
+ })
1198
1254
  },
1199
1255
  async (args) => {
1200
1256
  const [reportRes, timelineRes] = await Promise.allSettled([
@@ -1242,12 +1298,12 @@ function createMushiServer(config) {
1242
1298
  title: titleOf("triage_issue"),
1243
1299
  description: descOf("triage_issue"),
1244
1300
  annotations: annotationsFor("triage_issue"),
1245
- inputSchema: {
1301
+ inputSchema: z.object({
1246
1302
  report_id: z.string().uuid().describe("Report UUID to triage."),
1247
1303
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
1248
1304
  include_logs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
1249
- },
1250
- outputSchema: {
1305
+ }),
1306
+ outputSchema: z.object({
1251
1307
  report_id: z.string(),
1252
1308
  severity: z.unknown(),
1253
1309
  category: z.unknown(),
@@ -1260,7 +1316,7 @@ function createMushiServer(config) {
1260
1316
  recent_logs: z.unknown().nullable(),
1261
1317
  recommended_actions: z.array(z.unknown()),
1262
1318
  triage_summary: z.string()
1263
- }
1319
+ })
1264
1320
  },
1265
1321
  async (args) => {
1266
1322
  const { projectId: pid, headers } = await projectScopeHeaders(args.project_id);
@@ -1384,7 +1440,7 @@ function createMushiServer(config) {
1384
1440
  title: titleOf("submit_fix_result"),
1385
1441
  description: descOf("submit_fix_result"),
1386
1442
  annotations: annotationsFor("submit_fix_result"),
1387
- inputSchema: {
1443
+ inputSchema: z.object({
1388
1444
  reportId: z.string().describe("Report UUID to attach this external fix attempt to"),
1389
1445
  branch: z.string().describe("Git branch name where the fix was implemented"),
1390
1446
  prUrl: z.string().optional().describe("GitHub pull request URL, if a PR was opened"),
@@ -1394,19 +1450,14 @@ function createMushiServer(config) {
1394
1450
  idempotencyKey: z.string().uuid().optional().describe(
1395
1451
  "Optional UUID \u2014 resend the same key to safely retry without creating duplicate fix rows. Omit it and a stable key is derived from reportId + branch + prUrl, so a retried submission carries the identical key instead of a fresh random one."
1396
1452
  )
1397
- },
1398
- outputSchema: {
1453
+ }),
1454
+ outputSchema: z.object({
1399
1455
  ok: z.boolean().describe("True when the fix_attempt row was created and marked completed"),
1400
1456
  fixId: z.string().describe("UUID of the new fix_attempt row")
1401
- }
1457
+ })
1402
1458
  },
1403
1459
  async (args) => {
1404
- const idemKey = args.idempotencyKey ?? await stableUuidFrom(
1405
- IDEMPOTENCY_NAMESPACE,
1406
- args.reportId,
1407
- args.branch,
1408
- args.prUrl ?? ""
1409
- );
1460
+ const idemKey = args.idempotencyKey ?? await stableUuidFrom(IDEMPOTENCY_NAMESPACE, args.reportId, args.branch, args.prUrl ?? "");
1410
1461
  const created = await apiCall("/v1/admin/fixes", {
1411
1462
  method: "POST",
1412
1463
  headers: { "Idempotency-Key": idemKey },
@@ -1442,10 +1493,10 @@ function createMushiServer(config) {
1442
1493
  title: titleOf("triage_next_steps"),
1443
1494
  description: descOf("triage_next_steps"),
1444
1495
  annotations: annotationsFor("triage_next_steps"),
1445
- inputSchema: {
1496
+ inputSchema: z.object({
1446
1497
  project_id: z.string().uuid().optional().describe("Project UUID (defaults to the configured project).")
1447
- },
1448
- outputSchema: {
1498
+ }),
1499
+ outputSchema: z.object({
1449
1500
  steps: z.array(
1450
1501
  z.object({
1451
1502
  priority: z.number(),
@@ -1456,7 +1507,7 @@ function createMushiServer(config) {
1456
1507
  })
1457
1508
  ),
1458
1509
  summary: z.string()
1459
- }
1510
+ })
1460
1511
  },
1461
1512
  async (args) => {
1462
1513
  const pid = await resolveProjectId(args.project_id);
@@ -1491,7 +1542,9 @@ function createMushiServer(config) {
1491
1542
  args: { report_id: r.id }
1492
1543
  });
1493
1544
  }
1494
- const openUserReports = reports.filter((r) => (r.status === "new" || r.status === "classified") && !isCron(r) && !isBlocked(r)).sort((a, b) => {
1545
+ const openUserReports = reports.filter(
1546
+ (r) => (r.status === "new" || r.status === "classified") && !isCron(r) && !isBlocked(r)
1547
+ ).sort((a, b) => {
1495
1548
  const rank = { critical: 0, high: 1, medium: 2, low: 3 };
1496
1549
  return (rank[String(a.severity)] ?? 4) - (rank[String(b.severity)] ?? 4);
1497
1550
  });
@@ -1524,7 +1577,7 @@ function createMushiServer(config) {
1524
1577
  title: titleOf("dispatch_fix"),
1525
1578
  description: descOf("dispatch_fix"),
1526
1579
  annotations: annotationsFor("dispatch_fix"),
1527
- inputSchema: {
1580
+ inputSchema: z.object({
1528
1581
  reportId: z.string().uuid().describe("Report UUID to fix"),
1529
1582
  agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
1530
1583
  idempotencyKey: z.string().uuid().optional().describe(
@@ -1533,19 +1586,19 @@ function createMushiServer(config) {
1533
1586
  inventoryActionNodeId: z.string().uuid().optional().describe(
1534
1587
  "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."
1535
1588
  )
1536
- },
1537
- outputSchema: {
1589
+ }),
1590
+ outputSchema: z.object({
1538
1591
  fixId: z.string(),
1539
1592
  status: z.string()
1540
- }
1593
+ })
1541
1594
  },
1542
- async (args, extra) => {
1543
- if (extra?.sendNotification && extra?._meta?.progressToken) {
1595
+ async (args, ctx) => {
1596
+ if (ctx?.mcpReq.notify && ctx?.mcpReq._meta?.progressToken) {
1544
1597
  try {
1545
- await extra.sendNotification({
1598
+ await ctx.mcpReq.notify({
1546
1599
  method: "notifications/progress",
1547
1600
  params: {
1548
- progressToken: extra._meta.progressToken,
1601
+ progressToken: ctx.mcpReq._meta.progressToken,
1549
1602
  progress: 0,
1550
1603
  total: 100,
1551
1604
  message: "Dispatching Mushi fix orchestrator\u2026"
@@ -1578,13 +1631,13 @@ function createMushiServer(config) {
1578
1631
  title: titleOf("trigger_judge"),
1579
1632
  description: descOf("trigger_judge"),
1580
1633
  annotations: annotationsFor("trigger_judge"),
1581
- inputSchema: {
1634
+ inputSchema: z.object({
1582
1635
  limit: z.number().optional().describe("Max reports to judge in this batch (default 25, max 100)"),
1583
1636
  projectId: z.string().optional().describe("Restrict to one project when the API key owns multiple")
1584
- },
1585
- outputSchema: {
1637
+ }),
1638
+ outputSchema: z.object({
1586
1639
  dispatched: z.number().describe("Number of judge-batch jobs dispatched (one per accessible project)")
1587
- }
1640
+ })
1588
1641
  },
1589
1642
  async (args) => {
1590
1643
  const data = await apiCall("/v1/admin/judge/run", {
@@ -1603,10 +1656,10 @@ function createMushiServer(config) {
1603
1656
  title: titleOf("test_gen_from_report"),
1604
1657
  description: descOf("test_gen_from_report"),
1605
1658
  annotations: annotationsFor("test_gen_from_report"),
1606
- inputSchema: {
1659
+ inputSchema: z.object({
1607
1660
  reportId: z.string().describe("Report UUID to turn into a Playwright PR"),
1608
1661
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
1609
- }
1662
+ })
1610
1663
  },
1611
1664
  async (args) => {
1612
1665
  const pid = args.projectId ?? projectId;
@@ -1632,16 +1685,16 @@ function createMushiServer(config) {
1632
1685
  title: titleOf("merge_fix"),
1633
1686
  description: descOf("merge_fix"),
1634
1687
  annotations: annotationsFor("merge_fix"),
1635
- inputSchema: {
1688
+ inputSchema: z.object({
1636
1689
  fixId: z.string().describe("Fix attempt UUID whose GitHub PR should be squash-merged"),
1637
1690
  mergeMethod: z.enum(["squash", "merge", "rebase"]).optional().describe("GitHub merge method (default squash)")
1638
- },
1639
- outputSchema: {
1691
+ }),
1692
+ outputSchema: z.object({
1640
1693
  merged: z.boolean().optional().describe("True when GitHub accepted the merge in this call"),
1641
1694
  alreadyMerged: z.boolean().optional().describe("True when the PR was already merged (idempotent no-op)"),
1642
1695
  reportId: z.string().describe("Report UUID linked to this fix attempt"),
1643
1696
  reportStatus: z.string().describe("Report workflow status after merge bookkeeping")
1644
- }
1697
+ })
1645
1698
  },
1646
1699
  async (args) => {
1647
1700
  const data = await apiCall(`/v1/admin/fixes/${args.fixId}/merge`, {
@@ -1657,14 +1710,14 @@ function createMushiServer(config) {
1657
1710
  title: titleOf("refresh_ci"),
1658
1711
  description: descOf("refresh_ci"),
1659
1712
  annotations: annotationsFor("refresh_ci"),
1660
- inputSchema: {
1713
+ inputSchema: z.object({
1661
1714
  fixId: z.string().describe("Fix attempt UUID whose PR check-runs should be re-polled")
1662
- },
1663
- outputSchema: {
1715
+ }),
1716
+ outputSchema: z.object({
1664
1717
  check_run_status: z.string().nullable().describe("GitHub check run status after refresh"),
1665
1718
  check_run_conclusion: z.string().nullable().describe("success | failure | neutral | null while pending"),
1666
1719
  check_run_updated_at: z.string().nullable().describe("ISO timestamp when CI status was last persisted")
1667
- }
1720
+ })
1668
1721
  },
1669
1722
  async (args) => {
1670
1723
  const data = await apiCall(`/v1/admin/fixes/${args.fixId}/refresh-ci`, { method: "POST" });
@@ -1677,13 +1730,13 @@ function createMushiServer(config) {
1677
1730
  title: titleOf("reopen_report"),
1678
1731
  description: descOf("reopen_report"),
1679
1732
  annotations: annotationsFor("reopen_report"),
1680
- inputSchema: {
1733
+ inputSchema: z.object({
1681
1734
  reportId: z.string().describe("Report UUID to move back to reopened status"),
1682
1735
  note: z.string().optional().describe("Operator note recorded on the reopen transition")
1683
- },
1684
- outputSchema: {
1736
+ }),
1737
+ outputSchema: z.object({
1685
1738
  report: z.unknown().describe("Updated report row with status=reopened")
1686
- }
1739
+ })
1687
1740
  },
1688
1741
  async (args) => {
1689
1742
  const report = await apiCall(`/v1/sync/reports/${args.reportId}`, {
@@ -1699,7 +1752,7 @@ function createMushiServer(config) {
1699
1752
  title: titleOf("transition_status"),
1700
1753
  description: descOf("transition_status"),
1701
1754
  annotations: annotationsFor("transition_status"),
1702
- inputSchema: {
1755
+ inputSchema: z.object({
1703
1756
  reportId: z.string().describe("Report UUID"),
1704
1757
  status: z.enum([
1705
1758
  "pending",
@@ -1718,7 +1771,7 @@ function createMushiServer(config) {
1718
1771
  "dismissed"
1719
1772
  ]).describe("Target status (resolved is stored as fixed)"),
1720
1773
  reason: z.string().optional().describe("Reason for the transition (audit trail)")
1721
- }
1774
+ })
1722
1775
  },
1723
1776
  async (args) => {
1724
1777
  const data = await apiCall(`/v1/admin/reports/${args.reportId}`, {
@@ -1734,9 +1787,9 @@ function createMushiServer(config) {
1734
1787
  title: titleOf("setup_repo_for_mushi"),
1735
1788
  description: descOf("setup_repo_for_mushi"),
1736
1789
  annotations: annotationsFor("setup_repo_for_mushi"),
1737
- inputSchema: {
1790
+ inputSchema: z.object({
1738
1791
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
1739
- }
1792
+ })
1740
1793
  },
1741
1794
  async (args) => {
1742
1795
  const pid = args.projectId ?? projectId;
@@ -1759,12 +1812,12 @@ function createMushiServer(config) {
1759
1812
  title: titleOf("query_lessons"),
1760
1813
  description: descOf("query_lessons"),
1761
1814
  annotations: annotationsFor("query_lessons"),
1762
- inputSchema: {
1815
+ inputSchema: z.object({
1763
1816
  diff_text: z.string().describe("The PR diff, code snippet, or description of the change being made."),
1764
1817
  max_tokens: z.number().optional().describe("Maximum tokens for returned lessons context (default 3000, max 8000)."),
1765
1818
  top_k: z.number().optional().describe("Max number of lessons to return (default 15, max 50)."),
1766
1819
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project.")
1767
- }
1820
+ })
1768
1821
  },
1769
1822
  async (args) => {
1770
1823
  const pid = args.project_id ?? projectId;
@@ -1786,14 +1839,14 @@ function createMushiServer(config) {
1786
1839
  title: titleOf("list_lessons"),
1787
1840
  description: descOf("list_lessons"),
1788
1841
  annotations: annotationsFor("list_lessons"),
1789
- inputSchema: {
1842
+ inputSchema: z.object({
1790
1843
  severity: z.enum(["info", "warn", "critical"]).optional().describe("Filter to one severity level"),
1791
1844
  limit: z.number().optional().describe("Max lessons to return (default 50, max 200)"),
1792
1845
  project_id: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
1793
- },
1794
- outputSchema: {
1846
+ }),
1847
+ outputSchema: z.object({
1795
1848
  lessons: z.array(z.unknown()).describe("Promoted lesson rows ordered by frequency")
1796
- }
1849
+ })
1797
1850
  },
1798
1851
  async (args) => {
1799
1852
  const pid = args.project_id ?? projectId;
@@ -1805,7 +1858,7 @@ function createMushiServer(config) {
1805
1858
  return wrappedJsonResult({ lessons }, "lesson text");
1806
1859
  }
1807
1860
  );
1808
- server.resource(
1861
+ server.registerResource(
1809
1862
  "project_stats",
1810
1863
  "project://stats",
1811
1864
  { description: "Report counts, category breakdown, severity distribution" },
@@ -1819,7 +1872,7 @@ function createMushiServer(config) {
1819
1872
  ]
1820
1873
  })
1821
1874
  );
1822
- server.resource(
1875
+ server.registerResource(
1823
1876
  "project_settings",
1824
1877
  "project://settings",
1825
1878
  {
@@ -1835,7 +1888,7 @@ function createMushiServer(config) {
1835
1888
  ]
1836
1889
  })
1837
1890
  );
1838
- server.resource(
1891
+ server.registerResource(
1839
1892
  "project_dashboard",
1840
1893
  "project://dashboard",
1841
1894
  {
@@ -1857,10 +1910,10 @@ function createMushiServer(config) {
1857
1910
  title: titleOf("list_top_contributors"),
1858
1911
  description: descOf("list_top_contributors"),
1859
1912
  annotations: annotationsFor("list_top_contributors"),
1860
- inputSchema: {
1913
+ inputSchema: z.object({
1861
1914
  limit: z.number().int().min(1).max(100).optional().default(10).describe("Max rows to return (default 10, max 100)"),
1862
1915
  range: z.enum(["30d", "90d", "all"]).optional().default("30d").describe("Time window for points calculation")
1863
- }
1916
+ })
1864
1917
  },
1865
1918
  async ({ limit, range }) => ({
1866
1919
  content: [
@@ -1881,11 +1934,11 @@ function createMushiServer(config) {
1881
1934
  title: titleOf("award_bonus_points"),
1882
1935
  description: descOf("award_bonus_points"),
1883
1936
  annotations: annotationsFor("award_bonus_points"),
1884
- inputSchema: {
1937
+ inputSchema: z.object({
1885
1938
  external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
1886
1939
  points: z.number().int().min(1).max(5e4).describe("Bonus points to award (max 50,000 per call)"),
1887
1940
  reason: z.string().max(200).describe("Human-readable reason, logged to end_user_activity")
1888
- }
1941
+ })
1889
1942
  },
1890
1943
  async ({ external_user_id, points, reason }) => ({
1891
1944
  content: [
@@ -1910,11 +1963,11 @@ function createMushiServer(config) {
1910
1963
  title: titleOf("set_tier"),
1911
1964
  description: descOf("set_tier"),
1912
1965
  annotations: annotationsFor("set_tier"),
1913
- inputSchema: {
1966
+ inputSchema: z.object({
1914
1967
  external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
1915
1968
  tier_slug: z.string().describe('Tier slug to assign, e.g. "champion", "contributor", "explorer"'),
1916
1969
  reason: z.string().max(200).optional().describe("Optional reason for manual override")
1917
- }
1970
+ })
1918
1971
  },
1919
1972
  async ({ external_user_id, tier_slug, reason }) => ({
1920
1973
  content: [
@@ -1933,7 +1986,7 @@ function createMushiServer(config) {
1933
1986
  ]
1934
1987
  })
1935
1988
  );
1936
- server.resource(
1989
+ server.registerResource(
1937
1990
  "privacy_status",
1938
1991
  "privacy://status",
1939
1992
  {
@@ -1949,7 +2002,7 @@ function createMushiServer(config) {
1949
2002
  ]
1950
2003
  })
1951
2004
  );
1952
- server.resource(
2005
+ server.registerResource(
1953
2006
  "evolution_history",
1954
2007
  "evolution://history",
1955
2008
  {
@@ -1971,7 +2024,7 @@ function createMushiServer(config) {
1971
2024
  ]
1972
2025
  })
1973
2026
  );
1974
- server.resource(
2027
+ server.registerResource(
1975
2028
  "project_integration_health",
1976
2029
  "project://integration-health",
1977
2030
  {
@@ -1987,7 +2040,7 @@ function createMushiServer(config) {
1987
2040
  ]
1988
2041
  })
1989
2042
  );
1990
- server.resource(
2043
+ server.registerResource(
1991
2044
  "inventory_current",
1992
2045
  "inventory://current",
1993
2046
  {
@@ -2012,9 +2065,9 @@ function createMushiServer(config) {
2012
2065
  title: titleOf("activation_status"),
2013
2066
  description: descOf("activation_status"),
2014
2067
  annotations: annotationsFor("activation_status"),
2015
- inputSchema: {
2068
+ inputSchema: z.object({
2016
2069
  project_id: z.string().optional().describe("Project UUID (defaults to the configured project).")
2017
- }
2070
+ })
2018
2071
  },
2019
2072
  async (args) => {
2020
2073
  const pid = args.project_id ?? projectId;
@@ -2022,7 +2075,7 @@ function createMushiServer(config) {
2022
2075
  return jsonResult(await apiCall(`/v1/admin/activation${qs}`));
2023
2076
  }
2024
2077
  );
2025
- server.resource(
2078
+ server.registerResource(
2026
2079
  "activation_status",
2027
2080
  "mushi://activation",
2028
2081
  {
@@ -2041,10 +2094,12 @@ function createMushiServer(config) {
2041
2094
  };
2042
2095
  }
2043
2096
  );
2044
- server.prompt(
2097
+ server.registerPrompt(
2045
2098
  "summarize_report_for_fix",
2046
- "Turn a Mushi report into a one-line root cause, smallest file set, repro steps, and blast-radius warnings. Use before asking an agent to write the patch.",
2047
- { reportId: z.string().describe("The report UUID to summarize") },
2099
+ {
2100
+ description: "Turn a Mushi report into a one-line root cause, smallest file set, repro steps, and blast-radius warnings. Use before asking an agent to write the patch.",
2101
+ argsSchema: z.object({ reportId: z.string().describe("The report UUID to summarize") })
2102
+ },
2048
2103
  ({ reportId }) => ({
2049
2104
  messages: [
2050
2105
  {
@@ -2069,10 +2124,12 @@ Lead with the fix. Skip the preamble.`
2069
2124
  ]
2070
2125
  })
2071
2126
  );
2072
- server.prompt(
2127
+ server.registerPrompt(
2073
2128
  "explain_judge_result",
2074
- "Turn raw Sonnet-as-Judge scores into ship / iterate / dismiss guidance. Use after a fix attempt has been judged.",
2075
- { fixId: z.string().describe("The fix_attempt UUID to explain") },
2129
+ {
2130
+ description: "Turn raw Sonnet-as-Judge scores into ship / iterate / dismiss guidance. Use after a fix attempt has been judged.",
2131
+ argsSchema: z.object({ fixId: z.string().describe("The fix_attempt UUID to explain") })
2132
+ },
2076
2133
  ({ fixId }) => ({
2077
2134
  messages: [
2078
2135
  {
@@ -2093,10 +2150,12 @@ No preamble, no score-by-score recap \u2014 the numbers are in the tool output.`
2093
2150
  ]
2094
2151
  })
2095
2152
  );
2096
- server.prompt(
2153
+ server.registerPrompt(
2097
2154
  "triage_next_steps",
2098
- 'Answer "what should I focus on right now?" \u2014 five-item markdown list drawn from the dashboard + recent classified queue.',
2099
- {},
2155
+ {
2156
+ description: 'Answer "what should I focus on right now?" \u2014 five-item markdown list drawn from the dashboard + recent classified queue.',
2157
+ argsSchema: z.object({})
2158
+ },
2100
2159
  () => ({
2101
2160
  messages: [
2102
2161
  {
@@ -2116,10 +2175,12 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
2116
2175
  ]
2117
2176
  })
2118
2177
  );
2119
- server.prompt(
2178
+ server.registerPrompt(
2120
2179
  "mushi_setup",
2121
- "Diagnose why Mushi setup is stuck and return the single next command or console step to unblock it.",
2122
- {},
2180
+ {
2181
+ description: "Diagnose why Mushi setup is stuck and return the single next command or console step to unblock it.",
2182
+ argsSchema: z.object({})
2183
+ },
2123
2184
  () => ({
2124
2185
  messages: [
2125
2186
  {
@@ -2149,13 +2210,13 @@ Be specific. No generic advice.`
2149
2210
  title: titleOf("map_user_stories", TDD_TOOL_CATALOG),
2150
2211
  description: descOf("map_user_stories", TDD_TOOL_CATALOG),
2151
2212
  annotations: annotationsFor("map_user_stories", TDD_TOOL_CATALOG),
2152
- inputSchema: {
2213
+ inputSchema: z.object({
2153
2214
  projectId: z.string().describe("Project id to map stories for"),
2154
2215
  baseUrl: z.string().url().describe("Live app URL to crawl"),
2155
2216
  maxPages: z.number().int().min(1).max(50).optional().describe("Max pages to crawl (default 20)"),
2156
2217
  provider: z.enum(["firecrawl", "browserbase"]).optional().describe("Crawl provider (default: firecrawl)"),
2157
2218
  cursorCloudRefine: z.boolean().optional().describe("Dispatch Cursor Cloud agent to refine and open a PR")
2158
- }
2219
+ })
2159
2220
  },
2160
2221
  async ({ projectId: projectId2, baseUrl, maxPages, provider, cursorCloudRefine }) => {
2161
2222
  if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
@@ -2180,9 +2241,9 @@ Be specific. No generic advice.`
2180
2241
  title: titleOf("get_map_run_status", TDD_TOOL_CATALOG),
2181
2242
  description: descOf("get_map_run_status", TDD_TOOL_CATALOG),
2182
2243
  annotations: annotationsFor("get_map_run_status", TDD_TOOL_CATALOG),
2183
- inputSchema: {
2244
+ inputSchema: z.object({
2184
2245
  projectId: z.string().describe("Project id")
2185
- }
2246
+ })
2186
2247
  },
2187
2248
  async ({ projectId: projectId2 }) => {
2188
2249
  const data = await apiCall(`/v1/admin/inventory/${projectId2}/map-runs`);
@@ -2195,7 +2256,7 @@ Be specific. No generic advice.`
2195
2256
  title: titleOf("generate_tdd_from_story", TDD_TOOL_CATALOG),
2196
2257
  description: descOf("generate_tdd_from_story", TDD_TOOL_CATALOG),
2197
2258
  annotations: annotationsFor("generate_tdd_from_story", TDD_TOOL_CATALOG),
2198
- inputSchema: {
2259
+ inputSchema: z.object({
2199
2260
  projectId: z.string().describe("Project UUID that owns the inventory story"),
2200
2261
  storyNodeId: z.string().describe(
2201
2262
  "User story node id/slug from accepted inventory (from map_user_stories or inventory.yaml)"
@@ -2203,13 +2264,13 @@ Be specific. No generic advice.`
2203
2264
  automationMode: z.enum(["auto", "review", "approve"]).optional().describe("Approval gate for the generated qa_story (default review \u2192 pending_review)"),
2204
2265
  baseUrl: z.string().url().optional().describe("Override the live app URL the Playwright test should target"),
2205
2266
  openPr: z.boolean().optional().describe("Open a draft GitHub PR with the generated spec (default true)")
2206
- },
2207
- outputSchema: {
2267
+ }),
2268
+ outputSchema: z.object({
2208
2269
  qaStoryId: z.string().describe("UUID of the inserted qa_stories row"),
2209
2270
  prUrl: z.string().nullable().describe("GitHub PR URL when openPr=true and GitHub is connected"),
2210
2271
  approvalStatus: z.string().describe("pending_review | approved \u2014 follows automationMode and project defaults"),
2211
2272
  needsHumanReview: z.boolean().describe("True when the story requires operator approval before scheduled runs")
2212
- }
2273
+ })
2213
2274
  },
2214
2275
  async ({ projectId: projectId2, storyNodeId, automationMode, baseUrl, openPr }) => {
2215
2276
  if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
@@ -2230,9 +2291,9 @@ Be specific. No generic advice.`
2230
2291
  title: titleOf("improve_qa_story", TDD_TOOL_CATALOG),
2231
2292
  description: descOf("improve_qa_story", TDD_TOOL_CATALOG),
2232
2293
  annotations: annotationsFor("improve_qa_story", TDD_TOOL_CATALOG),
2233
- inputSchema: {
2294
+ inputSchema: z.object({
2234
2295
  projectId: z.string().optional().describe("Project id (omit to run across all projects)")
2235
- }
2296
+ })
2236
2297
  },
2237
2298
  async ({ projectId: projectId2 }) => {
2238
2299
  const data = await apiCall("/v1/admin/pdca/improve-qa-stories", {
@@ -2248,10 +2309,10 @@ Be specific. No generic advice.`
2248
2309
  title: titleOf("run_qa_story", TDD_TOOL_CATALOG),
2249
2310
  description: descOf("run_qa_story", TDD_TOOL_CATALOG),
2250
2311
  annotations: annotationsFor("run_qa_story", TDD_TOOL_CATALOG),
2251
- inputSchema: {
2312
+ inputSchema: z.object({
2252
2313
  projectId: z.string().describe("Project id"),
2253
2314
  qaStoryId: z.string().describe("qa_story id to run")
2254
- }
2315
+ })
2255
2316
  },
2256
2317
  async ({ projectId: projectId2, qaStoryId }) => {
2257
2318
  const data = await apiCall(
@@ -2267,9 +2328,9 @@ Be specific. No generic advice.`
2267
2328
  title: titleOf("list_byok_keys", TDD_TOOL_CATALOG),
2268
2329
  description: descOf("list_byok_keys", TDD_TOOL_CATALOG),
2269
2330
  annotations: annotationsFor("list_byok_keys", TDD_TOOL_CATALOG),
2270
- inputSchema: {
2331
+ inputSchema: z.object({
2271
2332
  projectId: z.string().describe("Project id")
2272
- }
2333
+ })
2273
2334
  },
2274
2335
  async ({ projectId: projectId2 }) => {
2275
2336
  const data = await apiCall(
@@ -2284,14 +2345,14 @@ Be specific. No generic advice.`
2284
2345
  title: titleOf("add_byok_key", TDD_TOOL_CATALOG),
2285
2346
  description: descOf("add_byok_key", TDD_TOOL_CATALOG),
2286
2347
  annotations: annotationsFor("add_byok_key", TDD_TOOL_CATALOG),
2287
- inputSchema: {
2348
+ inputSchema: z.object({
2288
2349
  projectId: z.string().describe("Project id"),
2289
2350
  provider: z.enum(["anthropic", "openai", "firecrawl", "browserbase", "cursor"]).describe("Provider slug"),
2290
2351
  key: z.string().min(8).max(4096).describe("The API key value to add"),
2291
2352
  label: z.string().optional().describe("Human-readable label for this key"),
2292
2353
  priority: z.number().int().min(0).max(1e4).optional().describe("Priority for ordering (lower = higher priority)"),
2293
2354
  baseUrl: z.string().url().max(2048).optional().describe("Allow-listed OpenAI-compatible HTTPS base URL")
2294
- }
2355
+ })
2295
2356
  },
2296
2357
  async ({ projectId: projectId2, provider, key, label, priority, baseUrl }) => {
2297
2358
  const data = await apiCall("/v1/admin/byok/keys", {
@@ -2314,10 +2375,10 @@ Be specific. No generic advice.`
2314
2375
  title: titleOf("test_byok_key", TDD_TOOL_CATALOG),
2315
2376
  description: descOf("test_byok_key", TDD_TOOL_CATALOG),
2316
2377
  annotations: annotationsFor("test_byok_key", TDD_TOOL_CATALOG),
2317
- inputSchema: {
2378
+ inputSchema: z.object({
2318
2379
  projectId: z.string().describe("Project id"),
2319
2380
  keyId: z.string().uuid().describe("Pooled BYOK key id")
2320
- }
2381
+ })
2321
2382
  },
2322
2383
  async ({ keyId }) => {
2323
2384
  const data = await apiCall(`/v1/admin/byok/keys/${encodeURIComponent(keyId)}/test`, {
@@ -2332,10 +2393,10 @@ Be specific. No generic advice.`
2332
2393
  title: titleOf("remove_byok_key", TDD_TOOL_CATALOG),
2333
2394
  description: descOf("remove_byok_key", TDD_TOOL_CATALOG),
2334
2395
  annotations: annotationsFor("remove_byok_key", TDD_TOOL_CATALOG),
2335
- inputSchema: {
2396
+ inputSchema: z.object({
2336
2397
  projectId: z.string().describe("Project id"),
2337
2398
  keyId: z.string().uuid().describe("Pooled BYOK key UUID from list_byok_keys")
2338
- }
2399
+ })
2339
2400
  },
2340
2401
  async ({ keyId }) => {
2341
2402
  const data = await apiCall(`/v1/admin/byok/keys/${encodeURIComponent(keyId)}`, {
@@ -2350,9 +2411,9 @@ Be specific. No generic advice.`
2350
2411
  title: titleOf("list_pending_review_stories", TDD_TOOL_CATALOG),
2351
2412
  description: descOf("list_pending_review_stories", TDD_TOOL_CATALOG),
2352
2413
  annotations: annotationsFor("list_pending_review_stories", TDD_TOOL_CATALOG),
2353
- inputSchema: {
2414
+ inputSchema: z.object({
2354
2415
  projectId: z.string().describe("Project id")
2355
- }
2416
+ })
2356
2417
  },
2357
2418
  async ({ projectId: projectId2 }) => {
2358
2419
  const data = await apiCall(
@@ -2367,11 +2428,11 @@ Be specific. No generic advice.`
2367
2428
  title: titleOf("approve_qa_story", TDD_TOOL_CATALOG),
2368
2429
  description: descOf("approve_qa_story", TDD_TOOL_CATALOG),
2369
2430
  annotations: annotationsFor("approve_qa_story", TDD_TOOL_CATALOG),
2370
- inputSchema: {
2431
+ inputSchema: z.object({
2371
2432
  projectId: z.string().describe("Project id"),
2372
2433
  qaStoryId: z.string().describe("QA story id to approve or reject"),
2373
2434
  status: z.enum(["approved", "rejected"]).describe("New approval status")
2374
- }
2435
+ })
2375
2436
  },
2376
2437
  async ({ projectId: projectId2, qaStoryId, status }) => {
2377
2438
  const data = await apiCall(
@@ -2387,11 +2448,11 @@ Be specific. No generic advice.`
2387
2448
  title: titleOf("reply_to_reporter", TDD_TOOL_CATALOG),
2388
2449
  description: descOf("reply_to_reporter", TDD_TOOL_CATALOG),
2389
2450
  annotations: annotationsFor("reply_to_reporter", TDD_TOOL_CATALOG),
2390
- inputSchema: {
2451
+ inputSchema: z.object({
2391
2452
  reportId: z.string().describe("Report id to reply to"),
2392
2453
  message: z.string().min(1).max(1e4).describe("Message text to send to the reporter"),
2393
2454
  authorName: z.string().optional().describe('Display name for the admin sender (default: "Mushi Admin")')
2394
- }
2455
+ })
2395
2456
  },
2396
2457
  async ({ reportId, message, authorName }) => {
2397
2458
  const data = await apiCall(`/v1/sync/reports/${reportId}/reply`, {
@@ -2407,10 +2468,10 @@ Be specific. No generic advice.`
2407
2468
  title: titleOf("list_qa_story_runs", TDD_TOOL_CATALOG),
2408
2469
  description: descOf("list_qa_story_runs", TDD_TOOL_CATALOG),
2409
2470
  annotations: annotationsFor("list_qa_story_runs", TDD_TOOL_CATALOG),
2410
- inputSchema: {
2471
+ inputSchema: z.object({
2411
2472
  storyId: z.string().describe("QA story id (uuid)"),
2412
2473
  limit: z.number().int().min(1).max(50).optional().default(10).describe("Max runs to return (default 10)")
2413
- }
2474
+ })
2414
2475
  },
2415
2476
  async ({ storyId, limit }) => {
2416
2477
  const data = await apiCall(
@@ -2425,10 +2486,10 @@ Be specific. No generic advice.`
2425
2486
  title: titleOf("get_qa_story_run", TDD_TOOL_CATALOG),
2426
2487
  description: descOf("get_qa_story_run", TDD_TOOL_CATALOG),
2427
2488
  annotations: annotationsFor("get_qa_story_run", TDD_TOOL_CATALOG),
2428
- inputSchema: {
2489
+ inputSchema: z.object({
2429
2490
  storyId: z.string().describe("QA story id (uuid)"),
2430
2491
  runId: z.string().describe("Run id (uuid) to fetch detail for")
2431
- }
2492
+ })
2432
2493
  },
2433
2494
  async ({ storyId, runId }) => {
2434
2495
  const data = await apiCall(
@@ -2451,9 +2512,9 @@ Be specific. No generic advice.`
2451
2512
  title: titleOf("test_notification_channel", TDD_TOOL_CATALOG),
2452
2513
  description: descOf("test_notification_channel", TDD_TOOL_CATALOG),
2453
2514
  annotations: annotationsFor("test_notification_channel", TDD_TOOL_CATALOG),
2454
- inputSchema: {
2515
+ inputSchema: z.object({
2455
2516
  kind: z.enum(["slack", "discord"]).describe("Notification channel kind to test")
2456
- }
2517
+ })
2457
2518
  },
2458
2519
  async ({ kind }) => {
2459
2520
  const data = await apiCall(
@@ -2469,9 +2530,9 @@ Be specific. No generic advice.`
2469
2530
  title: titleOf("run_fullstack_audit"),
2470
2531
  description: descOf("run_fullstack_audit"),
2471
2532
  annotations: annotationsFor("run_fullstack_audit"),
2472
- inputSchema: {
2533
+ inputSchema: z.object({
2473
2534
  project_id: z.string().optional().describe("Project ID to audit. Defaults to the configured project.")
2474
- }
2535
+ })
2475
2536
  },
2476
2537
  async ({ project_id }) => {
2477
2538
  const pid = project_id ?? config.projectId;
@@ -2489,10 +2550,10 @@ Be specific. No generic advice.`
2489
2550
  title: titleOf("get_backend_health"),
2490
2551
  description: descOf("get_backend_health"),
2491
2552
  annotations: annotationsFor("get_backend_health"),
2492
- inputSchema: {
2553
+ inputSchema: z.object({
2493
2554
  project_id: z.string().optional().describe("Project ID. Defaults to the configured project."),
2494
2555
  include_logs: z.boolean().optional().describe("Whether to include recent backend error logs (default: true).")
2495
- }
2556
+ })
2496
2557
  },
2497
2558
  async ({ project_id, include_logs = true }) => {
2498
2559
  const pid = project_id ?? config.projectId;
@@ -2516,9 +2577,9 @@ Be specific. No generic advice.`
2516
2577
  title: titleOf("get_usage"),
2517
2578
  description: descOf("get_usage"),
2518
2579
  annotations: annotationsFor("get_usage"),
2519
- inputSchema: {
2580
+ inputSchema: z.object({
2520
2581
  project_id: z.string().optional().describe("Project ID. Defaults to the configured project.")
2521
- }
2582
+ })
2522
2583
  },
2523
2584
  async ({ project_id }) => {
2524
2585
  const pid = project_id ?? config.projectId;
@@ -2533,12 +2594,12 @@ Be specific. No generic advice.`
2533
2594
  title: titleOf("list_skills"),
2534
2595
  description: descOf("list_skills"),
2535
2596
  annotations: annotationsFor("list_skills"),
2536
- inputSchema: {
2597
+ inputSchema: z.object({
2537
2598
  category: z.string().optional().describe("Filter by category: workflow, debug, test, audit, enhance, \u2026"),
2538
2599
  search: z.string().optional().describe("Free-text search across slug, title, description"),
2539
2600
  page: z.number().optional().describe("Page number (default 1)"),
2540
2601
  limit: z.number().optional().describe("Max results per page (default 200, max 200)")
2541
- }
2602
+ })
2542
2603
  },
2543
2604
  async (args) => {
2544
2605
  const qs = new URLSearchParams();
@@ -2557,9 +2618,9 @@ Be specific. No generic advice.`
2557
2618
  title: titleOf("get_skill"),
2558
2619
  description: descOf("get_skill"),
2559
2620
  annotations: annotationsFor("get_skill"),
2560
- inputSchema: {
2621
+ inputSchema: z.object({
2561
2622
  slug: z.string().describe('Skill slug, e.g. "workflow-fix-and-ship"')
2562
- }
2623
+ })
2563
2624
  },
2564
2625
  async (args) => {
2565
2626
  const data = await apiCall(`/v1/admin/skills/${args.slug}`);
@@ -2572,14 +2633,14 @@ Be specific. No generic advice.`
2572
2633
  title: titleOf("start_skill_pipeline"),
2573
2634
  description: descOf("start_skill_pipeline"),
2574
2635
  annotations: annotationsFor("start_skill_pipeline"),
2575
- inputSchema: {
2636
+ inputSchema: z.object({
2576
2637
  root_skill_slug: z.string().describe('Root skill slug to run, e.g. "workflow-fix-and-ship"'),
2577
2638
  report_id: z.string().optional().describe("Report UUID to attach the pipeline to"),
2578
2639
  mode: z.enum(["handoff", "cloud"]).optional().describe(
2579
2640
  "handoff (default): get context packet for local agent. cloud: auto-dispatch via Cursor Cloud."
2580
2641
  ),
2581
2642
  project_id: z.string().optional().describe("Project UUID. Falls back to the configured project.")
2582
- }
2643
+ })
2583
2644
  },
2584
2645
  async (args) => {
2585
2646
  const resolvedProjectId = args.project_id ?? projectId;
@@ -2597,9 +2658,9 @@ Be specific. No generic advice.`
2597
2658
  title: titleOf("get_pipeline_run"),
2598
2659
  description: descOf("get_pipeline_run"),
2599
2660
  annotations: annotationsFor("get_pipeline_run"),
2600
- inputSchema: {
2661
+ inputSchema: z.object({
2601
2662
  run_id: z.string().describe("Pipeline run UUID")
2602
- }
2663
+ })
2603
2664
  },
2604
2665
  async (args) => {
2605
2666
  const data = await apiCall(`/v1/admin/skills/pipelines/${args.run_id}`);
@@ -2612,14 +2673,14 @@ Be specific. No generic advice.`
2612
2673
  title: titleOf("checkin_pipeline_step"),
2613
2674
  description: descOf("checkin_pipeline_step"),
2614
2675
  annotations: annotationsFor("checkin_pipeline_step"),
2615
- inputSchema: {
2676
+ inputSchema: z.object({
2616
2677
  run_id: z.string().describe("Pipeline run UUID"),
2617
2678
  step_index: z.number().describe("Step index (0-based)"),
2618
2679
  status: z.enum(["running", "passed", "failed", "skipped"]).describe("Step status"),
2619
2680
  notes: z.string().optional().describe("Optional notes or output summary"),
2620
2681
  pr_url: z.string().optional().describe("PR URL opened during this step"),
2621
2682
  agent_ref: z.string().optional().describe("Cursor agentId or external agent reference")
2622
- }
2683
+ })
2623
2684
  },
2624
2685
  async (args) => {
2625
2686
  const { run_id, step_index, ...body } = args;
@@ -2636,13 +2697,13 @@ Be specific. No generic advice.`
2636
2697
  title: titleOf("ask_codebase", CODEBASE_TOOL_CATALOG),
2637
2698
  description: descOf("ask_codebase", CODEBASE_TOOL_CATALOG),
2638
2699
  annotations: annotationsFor("ask_codebase", CODEBASE_TOOL_CATALOG),
2639
- inputSchema: {
2700
+ inputSchema: z.object({
2640
2701
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
2641
2702
  question: z.string().describe("Plain-English question about the repo"),
2642
2703
  thread_id: z.string().optional().describe("Optional thread UUID to continue a conversation"),
2643
2704
  file_path: z.string().optional().describe("Optional file path focus"),
2644
2705
  symbol_name: z.string().optional().describe("Optional symbol name focus")
2645
- }
2706
+ })
2646
2707
  },
2647
2708
  async (args) => {
2648
2709
  const pid = args.project_id ?? projectId;
@@ -2670,12 +2731,12 @@ Be specific. No generic advice.`
2670
2731
  title: titleOf("get_file_summary", CODEBASE_TOOL_CATALOG),
2671
2732
  description: descOf("get_file_summary", CODEBASE_TOOL_CATALOG),
2672
2733
  annotations: annotationsFor("get_file_summary", CODEBASE_TOOL_CATALOG),
2673
- inputSchema: {
2734
+ inputSchema: z.object({
2674
2735
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
2675
2736
  file_path: z.string().describe("Indexed file path"),
2676
2737
  symbol_name: z.string().optional().describe("Optional symbol name within the file"),
2677
2738
  force: z.boolean().optional().describe("Bypass cache and regenerate")
2678
- }
2739
+ })
2679
2740
  },
2680
2741
  async (args) => {
2681
2742
  const pid = args.project_id ?? projectId;
@@ -2693,10 +2754,10 @@ Be specific. No generic advice.`
2693
2754
  title: titleOf("get_codebase_tour", CODEBASE_TOOL_CATALOG),
2694
2755
  description: descOf("get_codebase_tour", CODEBASE_TOOL_CATALOG),
2695
2756
  annotations: annotationsFor("get_codebase_tour", CODEBASE_TOOL_CATALOG),
2696
- inputSchema: {
2757
+ inputSchema: z.object({
2697
2758
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
2698
2759
  force: z.boolean().optional().describe("Bypass cache and regenerate")
2699
- }
2760
+ })
2700
2761
  },
2701
2762
  async (args) => {
2702
2763
  const pid = args.project_id ?? projectId;
@@ -2712,17 +2773,17 @@ Be specific. No generic advice.`
2712
2773
  title: titleOf("search_codebase", CODEBASE_TOOL_CATALOG),
2713
2774
  description: descOf("search_codebase", CODEBASE_TOOL_CATALOG),
2714
2775
  annotations: annotationsFor("search_codebase", CODEBASE_TOOL_CATALOG),
2715
- inputSchema: {
2776
+ inputSchema: z.object({
2716
2777
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
2717
2778
  query: z.string().describe("Plain-English search query against indexed source files"),
2718
2779
  k: z.number().int().min(1).max(20).optional().describe("Max hits to return (default 8, max 20)"),
2719
2780
  scope_prefix: z.string().optional().describe("Optional repo subdirectory prefix to limit search scope")
2720
- },
2721
- outputSchema: {
2781
+ }),
2782
+ outputSchema: z.object({
2722
2783
  results: z.array(z.unknown()).describe("Ranked file/symbol hits with paths, line ranges, and similarity"),
2723
2784
  query: z.string().describe("Normalized query string that was searched"),
2724
2785
  mode: z.string().optional().describe("semantic (embeddings) or name (path/symbol match)")
2725
- }
2786
+ })
2726
2787
  },
2727
2788
  async (args) => {
2728
2789
  const pid = args.project_id ?? projectId;
@@ -2747,11 +2808,11 @@ Be specific. No generic advice.`
2747
2808
  title: titleOf("get_codebase_domains", CODEBASE_TOOL_CATALOG),
2748
2809
  description: descOf("get_codebase_domains", CODEBASE_TOOL_CATALOG),
2749
2810
  annotations: annotationsFor("get_codebase_domains", CODEBASE_TOOL_CATALOG),
2750
- inputSchema: {
2811
+ inputSchema: z.object({
2751
2812
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
2752
2813
  force: z.boolean().optional().describe("Bypass cache and regenerate"),
2753
2814
  scope_prefix: z.string().optional().describe("Optional subdirectory scope prefix")
2754
- }
2815
+ })
2755
2816
  },
2756
2817
  async (args) => {
2757
2818
  const pid = args.project_id ?? projectId;
@@ -2770,20 +2831,20 @@ Be specific. No generic advice.`
2770
2831
  title: titleOf("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
2771
2832
  description: descOf("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
2772
2833
  annotations: annotationsFor("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
2773
- inputSchema: {
2834
+ inputSchema: z.object({
2774
2835
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
2775
2836
  paths: z.array(z.string()).optional().describe("Changed repo-relative file paths to analyze"),
2776
2837
  source: z.enum(["last_push", "compare", "fix"]).optional().describe("Auto-resolve changed paths instead of manual paths"),
2777
2838
  compare: z.string().optional().describe("GitHub compare range base...head when source=compare"),
2778
2839
  fix_id: z.string().optional().describe("Fix attempt UUID \u2014 uses that PR's changed files when source=fix")
2779
- },
2780
- outputSchema: {
2840
+ }),
2841
+ outputSchema: z.object({
2781
2842
  changed_paths: z.array(z.string()).describe("Resolved set of changed paths analyzed"),
2782
2843
  source: z.string().describe("How changed_paths were resolved (manual, last_push, compare, fix, \u2026)"),
2783
2844
  affected_file_paths: z.array(z.string()).describe("Indexed files that import or depend on changed paths"),
2784
2845
  affected_node_ids: z.array(z.string()).describe("Codebase graph node ids impacted by the change"),
2785
2846
  meta: z.unknown().nullable().optional().describe("Extra resolution metadata when auto-sourced")
2786
- }
2847
+ })
2787
2848
  },
2788
2849
  async (args) => {
2789
2850
  const pid = args.project_id ?? projectId;
@@ -2810,9 +2871,9 @@ Be specific. No generic advice.`
2810
2871
  title: titleOf("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
2811
2872
  description: descOf("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
2812
2873
  annotations: annotationsFor("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
2813
- inputSchema: {
2874
+ inputSchema: z.object({
2814
2875
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)")
2815
- }
2876
+ })
2816
2877
  },
2817
2878
  async (args) => {
2818
2879
  const pid = args.project_id ?? projectId;
@@ -2826,11 +2887,11 @@ Be specific. No generic advice.`
2826
2887
  {
2827
2888
  title: titleOf("use_mushi"),
2828
2889
  description: descOf("use_mushi"),
2829
- inputSchema: {
2890
+ inputSchema: z.object({
2830
2891
  intent: z.string().optional().describe(
2831
2892
  'What you are trying to accomplish with Mushi, e.g. "fix the top bug", "check project health", "run QA tests", "set up Mushi". Leave blank for a general orientation.'
2832
2893
  )
2833
- },
2894
+ }),
2834
2895
  annotations: annotationsFor("use_mushi")
2835
2896
  },
2836
2897
  async (args) => {