@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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
5
5
  import { createRequire } from "module";
6
6
  import { readFileSync } from "fs";
7
7
  import { join } from "path";
@@ -215,6 +215,14 @@ var TOOL_CATALOG = [
215
215
  hints: { readOnly: true, idempotent: true, openWorld: true },
216
216
  useCase: "Why is Mushi setup broken \u2014 one call, clear next step."
217
217
  },
218
+ {
219
+ name: "check_sdk_version",
220
+ title: "Check SDK freshness",
221
+ description: "Compare a published @mushi-mushi/* package version against the catalog (GET /v1/sdk/latest-version). Returns { package, current, latest, outdated } and, when outdated, suggestedActions (Sentry-style, max 1) pointing at search_mushi_docs plus the mushi-sdk-upgrade skill. Read-only. Use when Dependabot or mushi upgrade --check reports a drift, or before dispatching a fix that assumes a current SDK. Does not bump the pin \u2014 that stays a human/Dependabot change.",
222
+ scope: "mcp:read",
223
+ hints: { readOnly: true, idempotent: true, openWorld: true },
224
+ useCase: "Is this project on the current Mushi SDK?"
225
+ },
218
226
  {
219
227
  name: "search_mushi_docs",
220
228
  title: "Search Mushi documentation",
@@ -653,6 +661,7 @@ var USE_MUSHI_INTENTS = {
653
661
  label: "Set up Mushi",
654
662
  tools: [
655
663
  "diagnose_setup",
664
+ "check_sdk_version",
656
665
  "activation_status",
657
666
  "get_backend_health",
658
667
  "list_byok_keys",
@@ -803,6 +812,7 @@ var TOOL_FEATURE_MAP = {
803
812
  inventory_current: "admin",
804
813
  // setup
805
814
  diagnose_setup: "setup",
815
+ check_sdk_version: "setup",
806
816
  activation_status: "setup",
807
817
  project_integration_health: "setup",
808
818
  setup_repo_for_mushi: "setup",
@@ -912,7 +922,7 @@ function toolMatchesFeatures(toolName, filter) {
912
922
  }
913
923
 
914
924
  // src/server.ts
915
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
925
+ import { McpServer } from "@modelcontextprotocol/server";
916
926
  import { z } from "zod";
917
927
  import { createLogger } from "@mushi-mushi/core";
918
928
 
@@ -1060,7 +1070,7 @@ function wrapUntrustedJson(value, role) {
1060
1070
 
1061
1071
  // src/server.ts
1062
1072
  installStdoutGuard();
1063
- var NO_ARG_INPUT = {};
1073
+ var NO_ARG_INPUT = z.object({});
1064
1074
  var DEFAULT_API_TIMEOUT_MS = 15e3;
1065
1075
  var MIN_API_TIMEOUT_MS = 250;
1066
1076
  var MAX_API_TIMEOUT_MS = 3e5;
@@ -1334,7 +1344,7 @@ function createMushiServer(config) {
1334
1344
  title: titleOf("get_recent_reports"),
1335
1345
  description: descOf("get_recent_reports"),
1336
1346
  annotations: annotationsFor("get_recent_reports"),
1337
- inputSchema: {
1347
+ inputSchema: z.object({
1338
1348
  status: z.string().optional().describe("Filter by status: new, classified, grouped, fixing, fixed, dismissed"),
1339
1349
  category: z.string().optional().describe("Filter by category: bug, slow, visual, confusing, other"),
1340
1350
  severity: z.string().optional().describe("Filter by severity: critical, high, medium, low"),
@@ -1342,11 +1352,11 @@ function createMushiServer(config) {
1342
1352
  project_id: z.string().optional().describe(
1343
1353
  "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."
1344
1354
  )
1345
- },
1346
- outputSchema: {
1355
+ }),
1356
+ outputSchema: z.object({
1347
1357
  reports: z.array(z.unknown()),
1348
1358
  total: z.number()
1349
- }
1359
+ })
1350
1360
  },
1351
1361
  async (args) => {
1352
1362
  const params = new URLSearchParams();
@@ -1371,11 +1381,11 @@ function createMushiServer(config) {
1371
1381
  title: titleOf("get_report_detail"),
1372
1382
  description: descOf("get_report_detail"),
1373
1383
  annotations: annotationsFor("get_report_detail"),
1374
- inputSchema: {
1384
+ inputSchema: z.object({
1375
1385
  reportId: z.string().describe("The report UUID"),
1376
1386
  project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
1377
- },
1378
- outputSchema: { report: z.unknown() }
1387
+ }),
1388
+ outputSchema: z.object({ report: z.unknown() })
1379
1389
  },
1380
1390
  async (args) => {
1381
1391
  const { headers } = await projectScopeHeaders(args.project_id);
@@ -1398,7 +1408,7 @@ function createMushiServer(config) {
1398
1408
  title: titleOf("get_report_timeline"),
1399
1409
  description: descOf("get_report_timeline"),
1400
1410
  annotations: annotationsFor("get_report_timeline"),
1401
- inputSchema: { reportId: z.string().describe("The report UUID") }
1411
+ inputSchema: z.object({ reportId: z.string().describe("The report UUID") })
1402
1412
  },
1403
1413
  async (args) => jsonText(await apiCall(`/v1/sync/reports/${args.reportId}/timeline`))
1404
1414
  );
@@ -1418,14 +1428,14 @@ function createMushiServer(config) {
1418
1428
  title: titleOf("search_reports"),
1419
1429
  description: descOf("search_reports"),
1420
1430
  annotations: annotationsFor("search_reports"),
1421
- inputSchema: {
1431
+ inputSchema: z.object({
1422
1432
  query: z.string().describe("Natural-language search text or component path"),
1423
1433
  limit: z.number().optional().describe("Max results (default 10, max 50)"),
1424
1434
  threshold: z.number().optional().describe("Similarity threshold 0..1, default 0.2")
1425
- },
1426
- outputSchema: {
1435
+ }),
1436
+ outputSchema: z.object({
1427
1437
  results: z.array(z.unknown())
1428
- }
1438
+ })
1429
1439
  },
1430
1440
  async (args) => {
1431
1441
  const data = await apiCall("/v1/admin/reports/similarity", {
@@ -1446,13 +1456,13 @@ function createMushiServer(config) {
1446
1456
  title: titleOf("get_similar_bugs"),
1447
1457
  description: descOf("get_similar_bugs"),
1448
1458
  annotations: annotationsFor("get_similar_bugs"),
1449
- inputSchema: {
1459
+ inputSchema: z.object({
1450
1460
  query: z.string().describe("Component name, page path, or bug description"),
1451
1461
  limit: z.number().optional().describe("Max results (default 5, max 20)")
1452
- },
1453
- outputSchema: {
1462
+ }),
1463
+ outputSchema: z.object({
1454
1464
  results: z.array(z.unknown())
1455
- }
1465
+ })
1456
1466
  },
1457
1467
  async (args) => {
1458
1468
  const data = await apiCall("/v1/admin/reports/similarity", {
@@ -1473,18 +1483,18 @@ function createMushiServer(config) {
1473
1483
  title: titleOf("get_fix_context"),
1474
1484
  description: descOf("get_fix_context"),
1475
1485
  annotations: annotationsFor("get_fix_context"),
1476
- inputSchema: {
1486
+ inputSchema: z.object({
1477
1487
  reportId: z.string().describe("The report UUID to fix"),
1478
1488
  project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
1479
- },
1480
- outputSchema: {
1489
+ }),
1490
+ outputSchema: z.object({
1481
1491
  report: z.unknown(),
1482
1492
  fixPrompt: z.unknown(),
1483
1493
  reproductionSteps: z.unknown(),
1484
1494
  component: z.unknown(),
1485
1495
  rootCause: z.unknown(),
1486
1496
  bugOntologyTags: z.unknown()
1487
- }
1497
+ })
1488
1498
  },
1489
1499
  async (args) => {
1490
1500
  const { headers } = await projectScopeHeaders(args.project_id);
@@ -1513,10 +1523,10 @@ function createMushiServer(config) {
1513
1523
  title: titleOf("get_fix_timeline"),
1514
1524
  description: descOf("get_fix_timeline"),
1515
1525
  annotations: annotationsFor("get_fix_timeline"),
1516
- inputSchema: {
1526
+ inputSchema: z.object({
1517
1527
  fixId: z.string().describe("fix_attempt UUID"),
1518
1528
  project_id: z.string().optional().describe("Project UUID \u2014 required for org-scoped keys with multiple projects.")
1519
- }
1529
+ })
1520
1530
  },
1521
1531
  async (args) => {
1522
1532
  const { headers } = await projectScopeHeaders(args.project_id);
@@ -1529,7 +1539,7 @@ function createMushiServer(config) {
1529
1539
  title: titleOf("get_blast_radius"),
1530
1540
  description: descOf("get_blast_radius"),
1531
1541
  annotations: annotationsFor("get_blast_radius"),
1532
- inputSchema: { nodeId: z.string().describe("Graph node UUID") }
1542
+ inputSchema: z.object({ nodeId: z.string().describe("Graph node UUID") })
1533
1543
  },
1534
1544
  async (args) => jsonText(await apiCall(`/v1/admin/graph/blast-radius/${args.nodeId}`))
1535
1545
  );
@@ -1539,14 +1549,14 @@ function createMushiServer(config) {
1539
1549
  title: titleOf("get_knowledge_graph"),
1540
1550
  description: descOf("get_knowledge_graph"),
1541
1551
  annotations: annotationsFor("get_knowledge_graph"),
1542
- inputSchema: {
1552
+ inputSchema: z.object({
1543
1553
  seed: z.string().describe("Starting graph node id or human-readable label to traverse from"),
1544
1554
  depth: z.number().optional().describe("BFS hops outward from seed \u2014 clamped to 4 in the handler (default 2)")
1545
- },
1546
- outputSchema: {
1555
+ }),
1556
+ outputSchema: z.object({
1547
1557
  nodes: z.array(z.unknown()).describe("Graph nodes within the depth budget"),
1548
1558
  edges: z.array(z.unknown()).describe("Edges connecting the returned nodes")
1549
- }
1559
+ })
1550
1560
  },
1551
1561
  async (args) => {
1552
1562
  const params = new URLSearchParams({
@@ -1565,16 +1575,16 @@ function createMushiServer(config) {
1565
1575
  title: titleOf("get_graph_neighborhood"),
1566
1576
  description: descOf("get_graph_neighborhood"),
1567
1577
  annotations: annotationsFor("get_graph_neighborhood"),
1568
- inputSchema: {
1578
+ inputSchema: z.object({
1569
1579
  seed: z.string().describe(
1570
1580
  "Starting graph node id or label to expand around (e.g. an inventory Action id or component name)"
1571
1581
  ),
1572
1582
  depth: z.number().int().min(1).max(4).optional().describe("BFS hops to traverse outward. Default 2; clamped to a max of 4.")
1573
- },
1574
- outputSchema: {
1583
+ }),
1584
+ outputSchema: z.object({
1575
1585
  nodes: z.array(z.unknown()).describe("Graph nodes within the depth budget"),
1576
1586
  edges: z.array(z.unknown()).describe("Edges connecting the returned nodes")
1577
- }
1587
+ })
1578
1588
  },
1579
1589
  async (args) => {
1580
1590
  const params = new URLSearchParams({
@@ -1593,12 +1603,12 @@ function createMushiServer(config) {
1593
1603
  title: titleOf("get_graph_node"),
1594
1604
  description: descOf("get_graph_node"),
1595
1605
  annotations: annotationsFor("get_graph_node"),
1596
- inputSchema: {
1606
+ inputSchema: z.object({
1597
1607
  nodeId: z.string().describe("The graph_nodes.id of the node to fetch (UUID).")
1598
- },
1599
- outputSchema: {
1608
+ }),
1609
+ outputSchema: z.object({
1600
1610
  node: z.unknown().describe("Single graph_nodes row including metadata (Action nodes carry v2 status)")
1601
- }
1611
+ })
1602
1612
  },
1603
1613
  async (args) => {
1604
1614
  const data = await apiCall(`/v1/admin/graph/node/${args.nodeId}`);
@@ -1611,9 +1621,9 @@ function createMushiServer(config) {
1611
1621
  title: titleOf("get_inventory"),
1612
1622
  description: descOf("get_inventory"),
1613
1623
  annotations: annotationsFor("get_inventory"),
1614
- inputSchema: {
1624
+ inputSchema: z.object({
1615
1625
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to the server-configured project when omitted")
1616
- }
1626
+ })
1617
1627
  },
1618
1628
  async (args) => {
1619
1629
  const pid = args.projectId ?? projectId;
@@ -1628,11 +1638,11 @@ function createMushiServer(config) {
1628
1638
  title: titleOf("diff_inventory"),
1629
1639
  description: descOf("diff_inventory"),
1630
1640
  annotations: annotationsFor("diff_inventory"),
1631
- inputSchema: {
1641
+ inputSchema: z.object({
1632
1642
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project"),
1633
1643
  fromSha: z.string().describe("Older commit SHA (the baseline to diff from)"),
1634
1644
  toSha: z.string().describe("Newer commit SHA (the candidate to diff to)")
1635
- }
1645
+ })
1636
1646
  },
1637
1647
  async (args) => {
1638
1648
  const pid = args.projectId ?? projectId;
@@ -1648,15 +1658,15 @@ function createMushiServer(config) {
1648
1658
  title: titleOf("list_gate_findings"),
1649
1659
  description: descOf("list_gate_findings"),
1650
1660
  annotations: annotationsFor("list_gate_findings"),
1651
- inputSchema: {
1661
+ inputSchema: z.object({
1652
1662
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project"),
1653
1663
  gate: z.string().optional().describe("Filter to one gate id: dead-handler | mock-leak | crawl | status-claim"),
1654
1664
  severity: z.string().optional().describe("Minimum severity to include: low | medium | high | critical")
1655
- },
1656
- outputSchema: {
1665
+ }),
1666
+ outputSchema: z.object({
1657
1667
  runs: z.array(z.unknown()).describe("Recent gate_runs rows, newest first"),
1658
1668
  findings: z.array(z.unknown()).describe("gate_findings rows for those runs")
1659
- }
1669
+ })
1660
1670
  },
1661
1671
  async (args) => {
1662
1672
  const pid = args.projectId ?? projectId;
@@ -1682,18 +1692,20 @@ function createMushiServer(config) {
1682
1692
  title: titleOf("suggest_fix"),
1683
1693
  description: descOf("suggest_fix"),
1684
1694
  annotations: annotationsFor("suggest_fix"),
1685
- inputSchema: {
1695
+ inputSchema: z.object({
1686
1696
  reportId: z.string().describe("Report UUID to read the Stage-2 suggested-fix slice for")
1687
- },
1688
- outputSchema: {
1697
+ }),
1698
+ outputSchema: z.object({
1689
1699
  reportId: z.string().describe("The report this slice was read from"),
1690
1700
  rootCause: z.unknown().describe("Stage-2 root-cause hint, or null if not yet classified"),
1691
1701
  suggestedFix: z.unknown().describe("Stage-2 suggested fix, or null if not yet classified"),
1692
1702
  reproductionSteps: z.unknown().describe("Reproduction steps recorded on the report (array; [] if none)"),
1693
1703
  summary: z.unknown().describe("One-line report summary, or null"),
1694
1704
  component: z.unknown().describe("Component/page the bug was attributed to, or null"),
1695
- note: z.string().optional().describe("Present when no Stage-2 analysis exists \u2014 explains why and what to call instead")
1696
- }
1705
+ note: z.string().optional().describe(
1706
+ "Present when no Stage-2 analysis exists \u2014 explains why and what to call instead"
1707
+ )
1708
+ })
1697
1709
  },
1698
1710
  async (args) => {
1699
1711
  const report = await apiCall(`/v1/admin/reports/${args.reportId}`);
@@ -1725,11 +1737,11 @@ function createMushiServer(config) {
1725
1737
  title: titleOf("run_nl_query"),
1726
1738
  description: descOf("run_nl_query"),
1727
1739
  annotations: annotationsFor("run_nl_query"),
1728
- inputSchema: {
1740
+ inputSchema: z.object({
1729
1741
  question: z.string().describe(
1730
1742
  'Question in plain English, e.g. "Which components had the most critical bugs this week?"'
1731
1743
  )
1732
- }
1744
+ })
1733
1745
  },
1734
1746
  async (args) => {
1735
1747
  const data = await apiCall("/v1/admin/query", {
@@ -1745,14 +1757,14 @@ function createMushiServer(config) {
1745
1757
  title: titleOf("diagnose_setup"),
1746
1758
  description: descOf("diagnose_setup"),
1747
1759
  annotations: annotationsFor("diagnose_setup"),
1748
- inputSchema: {
1760
+ inputSchema: z.object({
1749
1761
  mode: z.enum(["full", "ingest", "dispatch"]).optional().describe(
1750
1762
  "full (default) = ingest + dispatch; ingest = SDK pipeline only; dispatch = fix preflight only."
1751
1763
  ),
1752
1764
  project_id: z.string().optional().describe("Project UUID for dispatch checks (defaults to configured project)."),
1753
1765
  projectId: z.string().optional().describe("Alias for project_id.")
1754
- },
1755
- outputSchema: {
1766
+ }),
1767
+ outputSchema: z.object({
1756
1768
  mode: z.string(),
1757
1769
  ready: z.boolean(),
1758
1770
  summary: z.string(),
@@ -1760,7 +1772,7 @@ function createMushiServer(config) {
1760
1772
  ingest: z.unknown().optional(),
1761
1773
  dispatch: z.unknown().optional(),
1762
1774
  connection: z.unknown().optional()
1763
- }
1775
+ })
1764
1776
  },
1765
1777
  async (args) => {
1766
1778
  const mode = args.mode ?? "full";
@@ -1899,17 +1911,71 @@ function createMushiServer(config) {
1899
1911
  });
1900
1912
  }
1901
1913
  );
1914
+ server.registerTool(
1915
+ "check_sdk_version",
1916
+ {
1917
+ title: titleOf("check_sdk_version"),
1918
+ description: descOf("check_sdk_version"),
1919
+ annotations: annotationsFor("check_sdk_version"),
1920
+ inputSchema: z.object({
1921
+ package: z.string().optional().describe(
1922
+ "npm package name (default @mushi-mushi/web). Examples: @mushi-mushi/web, @mushi-mushi/node, @mushi-mushi/core."
1923
+ ),
1924
+ current: z.string().optional().describe("Installed version from package.json, if known (e.g. 1.27.0).")
1925
+ }),
1926
+ outputSchema: z.object({
1927
+ package: z.string(),
1928
+ latest: z.string().optional(),
1929
+ current: z.string().optional(),
1930
+ outdated: z.boolean().optional(),
1931
+ suggestedActions: z.array(
1932
+ z.object({
1933
+ type: z.literal("tool_call"),
1934
+ toolName: z.string(),
1935
+ arguments: z.record(z.string(), z.unknown()),
1936
+ reason: z.string()
1937
+ })
1938
+ ).optional()
1939
+ })
1940
+ },
1941
+ async (args) => {
1942
+ const pkg = args.package?.trim() || "@mushi-mushi/web";
1943
+ const query = new URLSearchParams({ package: pkg }).toString();
1944
+ const data = await apiCall(
1945
+ `/v1/sdk/latest-version?${query}`
1946
+ );
1947
+ const latest = data.latest ?? data.version;
1948
+ const current = args.current;
1949
+ const outdated = current && latest ? current !== latest : void 0;
1950
+ return jsonResult({
1951
+ package: data.package ?? pkg,
1952
+ latest,
1953
+ current,
1954
+ outdated,
1955
+ ...outdated ? {
1956
+ suggestedActions: [
1957
+ {
1958
+ type: "tool_call",
1959
+ toolName: "search_mushi_docs",
1960
+ arguments: { query: `${pkg} upgrade mushi-sdk-upgrade` },
1961
+ reason: "Read current upgrade notes, then apply the mushi-sdk-upgrade skill. This tool does not bump the pin."
1962
+ }
1963
+ ]
1964
+ } : {}
1965
+ });
1966
+ }
1967
+ );
1902
1968
  server.registerTool(
1903
1969
  "search_mushi_docs",
1904
1970
  {
1905
1971
  title: titleOf("search_mushi_docs"),
1906
1972
  description: descOf("search_mushi_docs"),
1907
1973
  annotations: annotationsFor("search_mushi_docs"),
1908
- inputSchema: {
1974
+ inputSchema: z.object({
1909
1975
  query: z.string().describe("Keywords to search official Mushi docs (guides, MCP, inventory, QA)."),
1910
1976
  limit: z.number().int().min(1).max(20).optional().describe("Max results (default 8).")
1911
- },
1912
- outputSchema: {
1977
+ }),
1978
+ outputSchema: z.object({
1913
1979
  query: z.string(),
1914
1980
  results: z.array(
1915
1981
  z.object({
@@ -1919,7 +1985,7 @@ function createMushiServer(config) {
1919
1985
  score: z.number()
1920
1986
  })
1921
1987
  )
1922
- }
1988
+ })
1923
1989
  },
1924
1990
  async (args) => {
1925
1991
  const query = args.query ?? "";
@@ -1940,11 +2006,11 @@ function createMushiServer(config) {
1940
2006
  description: descOf("list_projects"),
1941
2007
  annotations: annotationsFor("list_projects"),
1942
2008
  inputSchema: NO_ARG_INPUT,
1943
- outputSchema: {
2009
+ outputSchema: z.object({
1944
2010
  projects: z.array(z.unknown()),
1945
2011
  total: z.number().optional(),
1946
2012
  _multi_project_hint: z.string().optional()
1947
- }
2013
+ })
1948
2014
  },
1949
2015
  async () => {
1950
2016
  const data = await apiCall("/v1/admin/mcp/projects");
@@ -1961,7 +2027,7 @@ function createMushiServer(config) {
1961
2027
  description: descOf("get_account_overview"),
1962
2028
  annotations: annotationsFor("get_account_overview"),
1963
2029
  inputSchema: NO_ARG_INPUT,
1964
- outputSchema: {
2030
+ outputSchema: z.object({
1965
2031
  projects: z.array(z.unknown()),
1966
2032
  total: z.number(),
1967
2033
  active_project_id: z.string().nullable(),
@@ -1969,7 +2035,7 @@ function createMushiServer(config) {
1969
2035
  resourceCount: z.number().optional(),
1970
2036
  promptCount: z.number().optional(),
1971
2037
  multi_project_hint: z.string()
1972
- }
2038
+ })
1973
2039
  },
1974
2040
  async () => {
1975
2041
  const data = await apiCall("/v1/admin/mcp/account-overview");
@@ -1991,9 +2057,9 @@ function createMushiServer(config) {
1991
2057
  title: titleOf("get_project_context"),
1992
2058
  description: descOf("get_project_context"),
1993
2059
  annotations: annotationsFor("get_project_context"),
1994
- inputSchema: {
2060
+ inputSchema: z.object({
1995
2061
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project.")
1996
- }
2062
+ })
1997
2063
  },
1998
2064
  async (args) => {
1999
2065
  const pid = args.project_id ?? projectId;
@@ -2015,13 +2081,13 @@ function createMushiServer(config) {
2015
2081
  title: titleOf("get_pipeline_logs"),
2016
2082
  description: descOf("get_pipeline_logs"),
2017
2083
  annotations: annotationsFor("get_pipeline_logs"),
2018
- inputSchema: {
2084
+ inputSchema: z.object({
2019
2085
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
2020
2086
  service: z.enum(["fix-worker", "qa-story-runner", "pipeline", "all"]).optional().describe("Pipeline service to filter (default: all)."),
2021
2087
  since: z.string().optional().describe("ISO-8601 timestamp \u2014 return only entries after this time."),
2022
2088
  limit: z.number().optional().describe("Max entries to return (default 50, max 200)."),
2023
2089
  level: z.enum(["info", "warn", "error", "fatal"]).optional().describe("Minimum severity level (default: warn).")
2024
- }
2090
+ })
2025
2091
  },
2026
2092
  async (args) => {
2027
2093
  const pid = args.project_id ?? projectId;
@@ -2041,9 +2107,9 @@ function createMushiServer(config) {
2041
2107
  title: titleOf("get_report_evidence"),
2042
2108
  description: descOf("get_report_evidence"),
2043
2109
  annotations: annotationsFor("get_report_evidence"),
2044
- inputSchema: {
2110
+ inputSchema: z.object({
2045
2111
  report_id: z.string().describe("Report UUID.")
2046
- }
2112
+ })
2047
2113
  },
2048
2114
  async (args) => {
2049
2115
  const [reportRes, timelineRes] = await Promise.allSettled([
@@ -2091,12 +2157,12 @@ function createMushiServer(config) {
2091
2157
  title: titleOf("triage_issue"),
2092
2158
  description: descOf("triage_issue"),
2093
2159
  annotations: annotationsFor("triage_issue"),
2094
- inputSchema: {
2160
+ inputSchema: z.object({
2095
2161
  report_id: z.string().uuid().describe("Report UUID to triage."),
2096
2162
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project."),
2097
2163
  include_logs: z.boolean().optional().describe("Include recent pipeline logs in triage packet (default: true).")
2098
- },
2099
- outputSchema: {
2164
+ }),
2165
+ outputSchema: z.object({
2100
2166
  report_id: z.string(),
2101
2167
  severity: z.unknown(),
2102
2168
  category: z.unknown(),
@@ -2109,7 +2175,7 @@ function createMushiServer(config) {
2109
2175
  recent_logs: z.unknown().nullable(),
2110
2176
  recommended_actions: z.array(z.unknown()),
2111
2177
  triage_summary: z.string()
2112
- }
2178
+ })
2113
2179
  },
2114
2180
  async (args) => {
2115
2181
  const { projectId: pid, headers } = await projectScopeHeaders(args.project_id);
@@ -2233,7 +2299,7 @@ function createMushiServer(config) {
2233
2299
  title: titleOf("submit_fix_result"),
2234
2300
  description: descOf("submit_fix_result"),
2235
2301
  annotations: annotationsFor("submit_fix_result"),
2236
- inputSchema: {
2302
+ inputSchema: z.object({
2237
2303
  reportId: z.string().describe("Report UUID to attach this external fix attempt to"),
2238
2304
  branch: z.string().describe("Git branch name where the fix was implemented"),
2239
2305
  prUrl: z.string().optional().describe("GitHub pull request URL, if a PR was opened"),
@@ -2243,19 +2309,14 @@ function createMushiServer(config) {
2243
2309
  idempotencyKey: z.string().uuid().optional().describe(
2244
2310
  "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."
2245
2311
  )
2246
- },
2247
- outputSchema: {
2312
+ }),
2313
+ outputSchema: z.object({
2248
2314
  ok: z.boolean().describe("True when the fix_attempt row was created and marked completed"),
2249
2315
  fixId: z.string().describe("UUID of the new fix_attempt row")
2250
- }
2316
+ })
2251
2317
  },
2252
2318
  async (args) => {
2253
- const idemKey = args.idempotencyKey ?? await stableUuidFrom(
2254
- IDEMPOTENCY_NAMESPACE,
2255
- args.reportId,
2256
- args.branch,
2257
- args.prUrl ?? ""
2258
- );
2319
+ const idemKey = args.idempotencyKey ?? await stableUuidFrom(IDEMPOTENCY_NAMESPACE, args.reportId, args.branch, args.prUrl ?? "");
2259
2320
  const created = await apiCall("/v1/admin/fixes", {
2260
2321
  method: "POST",
2261
2322
  headers: { "Idempotency-Key": idemKey },
@@ -2291,10 +2352,10 @@ function createMushiServer(config) {
2291
2352
  title: titleOf("triage_next_steps"),
2292
2353
  description: descOf("triage_next_steps"),
2293
2354
  annotations: annotationsFor("triage_next_steps"),
2294
- inputSchema: {
2355
+ inputSchema: z.object({
2295
2356
  project_id: z.string().uuid().optional().describe("Project UUID (defaults to the configured project).")
2296
- },
2297
- outputSchema: {
2357
+ }),
2358
+ outputSchema: z.object({
2298
2359
  steps: z.array(
2299
2360
  z.object({
2300
2361
  priority: z.number(),
@@ -2305,7 +2366,7 @@ function createMushiServer(config) {
2305
2366
  })
2306
2367
  ),
2307
2368
  summary: z.string()
2308
- }
2369
+ })
2309
2370
  },
2310
2371
  async (args) => {
2311
2372
  const pid = await resolveProjectId(args.project_id);
@@ -2340,7 +2401,9 @@ function createMushiServer(config) {
2340
2401
  args: { report_id: r.id }
2341
2402
  });
2342
2403
  }
2343
- const openUserReports = reports.filter((r) => (r.status === "new" || r.status === "classified") && !isCron(r) && !isBlocked(r)).sort((a, b) => {
2404
+ const openUserReports = reports.filter(
2405
+ (r) => (r.status === "new" || r.status === "classified") && !isCron(r) && !isBlocked(r)
2406
+ ).sort((a, b) => {
2344
2407
  const rank = { critical: 0, high: 1, medium: 2, low: 3 };
2345
2408
  return (rank[String(a.severity)] ?? 4) - (rank[String(b.severity)] ?? 4);
2346
2409
  });
@@ -2373,7 +2436,7 @@ function createMushiServer(config) {
2373
2436
  title: titleOf("dispatch_fix"),
2374
2437
  description: descOf("dispatch_fix"),
2375
2438
  annotations: annotationsFor("dispatch_fix"),
2376
- inputSchema: {
2439
+ inputSchema: z.object({
2377
2440
  reportId: z.string().uuid().describe("Report UUID to fix"),
2378
2441
  agent: z.enum(["claude_code", "codex", "rest_worker", "mcp"]).optional().describe("Override the agent adapter"),
2379
2442
  idempotencyKey: z.string().uuid().optional().describe(
@@ -2382,19 +2445,19 @@ function createMushiServer(config) {
2382
2445
  inventoryActionNodeId: z.string().uuid().optional().describe(
2383
2446
  "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."
2384
2447
  )
2385
- },
2386
- outputSchema: {
2448
+ }),
2449
+ outputSchema: z.object({
2387
2450
  fixId: z.string(),
2388
2451
  status: z.string()
2389
- }
2452
+ })
2390
2453
  },
2391
- async (args, extra) => {
2392
- if (extra?.sendNotification && extra?._meta?.progressToken) {
2454
+ async (args, ctx) => {
2455
+ if (ctx?.mcpReq.notify && ctx?.mcpReq._meta?.progressToken) {
2393
2456
  try {
2394
- await extra.sendNotification({
2457
+ await ctx.mcpReq.notify({
2395
2458
  method: "notifications/progress",
2396
2459
  params: {
2397
- progressToken: extra._meta.progressToken,
2460
+ progressToken: ctx.mcpReq._meta.progressToken,
2398
2461
  progress: 0,
2399
2462
  total: 100,
2400
2463
  message: "Dispatching Mushi fix orchestrator\u2026"
@@ -2427,13 +2490,13 @@ function createMushiServer(config) {
2427
2490
  title: titleOf("trigger_judge"),
2428
2491
  description: descOf("trigger_judge"),
2429
2492
  annotations: annotationsFor("trigger_judge"),
2430
- inputSchema: {
2493
+ inputSchema: z.object({
2431
2494
  limit: z.number().optional().describe("Max reports to judge in this batch (default 25, max 100)"),
2432
2495
  projectId: z.string().optional().describe("Restrict to one project when the API key owns multiple")
2433
- },
2434
- outputSchema: {
2496
+ }),
2497
+ outputSchema: z.object({
2435
2498
  dispatched: z.number().describe("Number of judge-batch jobs dispatched (one per accessible project)")
2436
- }
2499
+ })
2437
2500
  },
2438
2501
  async (args) => {
2439
2502
  const data = await apiCall("/v1/admin/judge/run", {
@@ -2452,10 +2515,10 @@ function createMushiServer(config) {
2452
2515
  title: titleOf("test_gen_from_report"),
2453
2516
  description: descOf("test_gen_from_report"),
2454
2517
  annotations: annotationsFor("test_gen_from_report"),
2455
- inputSchema: {
2518
+ inputSchema: z.object({
2456
2519
  reportId: z.string().describe("Report UUID to turn into a Playwright PR"),
2457
2520
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
2458
- }
2521
+ })
2459
2522
  },
2460
2523
  async (args) => {
2461
2524
  const pid = args.projectId ?? projectId;
@@ -2481,16 +2544,16 @@ function createMushiServer(config) {
2481
2544
  title: titleOf("merge_fix"),
2482
2545
  description: descOf("merge_fix"),
2483
2546
  annotations: annotationsFor("merge_fix"),
2484
- inputSchema: {
2547
+ inputSchema: z.object({
2485
2548
  fixId: z.string().describe("Fix attempt UUID whose GitHub PR should be squash-merged"),
2486
2549
  mergeMethod: z.enum(["squash", "merge", "rebase"]).optional().describe("GitHub merge method (default squash)")
2487
- },
2488
- outputSchema: {
2550
+ }),
2551
+ outputSchema: z.object({
2489
2552
  merged: z.boolean().optional().describe("True when GitHub accepted the merge in this call"),
2490
2553
  alreadyMerged: z.boolean().optional().describe("True when the PR was already merged (idempotent no-op)"),
2491
2554
  reportId: z.string().describe("Report UUID linked to this fix attempt"),
2492
2555
  reportStatus: z.string().describe("Report workflow status after merge bookkeeping")
2493
- }
2556
+ })
2494
2557
  },
2495
2558
  async (args) => {
2496
2559
  const data = await apiCall(`/v1/admin/fixes/${args.fixId}/merge`, {
@@ -2506,14 +2569,14 @@ function createMushiServer(config) {
2506
2569
  title: titleOf("refresh_ci"),
2507
2570
  description: descOf("refresh_ci"),
2508
2571
  annotations: annotationsFor("refresh_ci"),
2509
- inputSchema: {
2572
+ inputSchema: z.object({
2510
2573
  fixId: z.string().describe("Fix attempt UUID whose PR check-runs should be re-polled")
2511
- },
2512
- outputSchema: {
2574
+ }),
2575
+ outputSchema: z.object({
2513
2576
  check_run_status: z.string().nullable().describe("GitHub check run status after refresh"),
2514
2577
  check_run_conclusion: z.string().nullable().describe("success | failure | neutral | null while pending"),
2515
2578
  check_run_updated_at: z.string().nullable().describe("ISO timestamp when CI status was last persisted")
2516
- }
2579
+ })
2517
2580
  },
2518
2581
  async (args) => {
2519
2582
  const data = await apiCall(`/v1/admin/fixes/${args.fixId}/refresh-ci`, { method: "POST" });
@@ -2526,13 +2589,13 @@ function createMushiServer(config) {
2526
2589
  title: titleOf("reopen_report"),
2527
2590
  description: descOf("reopen_report"),
2528
2591
  annotations: annotationsFor("reopen_report"),
2529
- inputSchema: {
2592
+ inputSchema: z.object({
2530
2593
  reportId: z.string().describe("Report UUID to move back to reopened status"),
2531
2594
  note: z.string().optional().describe("Operator note recorded on the reopen transition")
2532
- },
2533
- outputSchema: {
2595
+ }),
2596
+ outputSchema: z.object({
2534
2597
  report: z.unknown().describe("Updated report row with status=reopened")
2535
- }
2598
+ })
2536
2599
  },
2537
2600
  async (args) => {
2538
2601
  const report = await apiCall(`/v1/sync/reports/${args.reportId}`, {
@@ -2548,7 +2611,7 @@ function createMushiServer(config) {
2548
2611
  title: titleOf("transition_status"),
2549
2612
  description: descOf("transition_status"),
2550
2613
  annotations: annotationsFor("transition_status"),
2551
- inputSchema: {
2614
+ inputSchema: z.object({
2552
2615
  reportId: z.string().describe("Report UUID"),
2553
2616
  status: z.enum([
2554
2617
  "pending",
@@ -2567,7 +2630,7 @@ function createMushiServer(config) {
2567
2630
  "dismissed"
2568
2631
  ]).describe("Target status (resolved is stored as fixed)"),
2569
2632
  reason: z.string().optional().describe("Reason for the transition (audit trail)")
2570
- }
2633
+ })
2571
2634
  },
2572
2635
  async (args) => {
2573
2636
  const data = await apiCall(`/v1/admin/reports/${args.reportId}`, {
@@ -2583,9 +2646,9 @@ function createMushiServer(config) {
2583
2646
  title: titleOf("setup_repo_for_mushi"),
2584
2647
  description: descOf("setup_repo_for_mushi"),
2585
2648
  annotations: annotationsFor("setup_repo_for_mushi"),
2586
- inputSchema: {
2649
+ inputSchema: z.object({
2587
2650
  projectId: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
2588
- }
2651
+ })
2589
2652
  },
2590
2653
  async (args) => {
2591
2654
  const pid = args.projectId ?? projectId;
@@ -2608,12 +2671,12 @@ function createMushiServer(config) {
2608
2671
  title: titleOf("query_lessons"),
2609
2672
  description: descOf("query_lessons"),
2610
2673
  annotations: annotationsFor("query_lessons"),
2611
- inputSchema: {
2674
+ inputSchema: z.object({
2612
2675
  diff_text: z.string().describe("The PR diff, code snippet, or description of the change being made."),
2613
2676
  max_tokens: z.number().optional().describe("Maximum tokens for returned lessons context (default 3000, max 8000)."),
2614
2677
  top_k: z.number().optional().describe("Max number of lessons to return (default 15, max 50)."),
2615
2678
  project_id: z.string().optional().describe("Project UUID. Defaults to configured project.")
2616
- }
2679
+ })
2617
2680
  },
2618
2681
  async (args) => {
2619
2682
  const pid = args.project_id ?? projectId;
@@ -2635,14 +2698,14 @@ function createMushiServer(config) {
2635
2698
  title: titleOf("list_lessons"),
2636
2699
  description: descOf("list_lessons"),
2637
2700
  annotations: annotationsFor("list_lessons"),
2638
- inputSchema: {
2701
+ inputSchema: z.object({
2639
2702
  severity: z.enum(["info", "warn", "critical"]).optional().describe("Filter to one severity level"),
2640
2703
  limit: z.number().optional().describe("Max lessons to return (default 50, max 200)"),
2641
2704
  project_id: z.string().optional().describe("Project UUID \u2014 defaults to configured project")
2642
- },
2643
- outputSchema: {
2705
+ }),
2706
+ outputSchema: z.object({
2644
2707
  lessons: z.array(z.unknown()).describe("Promoted lesson rows ordered by frequency")
2645
- }
2708
+ })
2646
2709
  },
2647
2710
  async (args) => {
2648
2711
  const pid = args.project_id ?? projectId;
@@ -2654,7 +2717,7 @@ function createMushiServer(config) {
2654
2717
  return wrappedJsonResult({ lessons }, "lesson text");
2655
2718
  }
2656
2719
  );
2657
- server.resource(
2720
+ server.registerResource(
2658
2721
  "project_stats",
2659
2722
  "project://stats",
2660
2723
  { description: "Report counts, category breakdown, severity distribution" },
@@ -2668,7 +2731,7 @@ function createMushiServer(config) {
2668
2731
  ]
2669
2732
  })
2670
2733
  );
2671
- server.resource(
2734
+ server.registerResource(
2672
2735
  "project_settings",
2673
2736
  "project://settings",
2674
2737
  {
@@ -2684,7 +2747,7 @@ function createMushiServer(config) {
2684
2747
  ]
2685
2748
  })
2686
2749
  );
2687
- server.resource(
2750
+ server.registerResource(
2688
2751
  "project_dashboard",
2689
2752
  "project://dashboard",
2690
2753
  {
@@ -2706,10 +2769,10 @@ function createMushiServer(config) {
2706
2769
  title: titleOf("list_top_contributors"),
2707
2770
  description: descOf("list_top_contributors"),
2708
2771
  annotations: annotationsFor("list_top_contributors"),
2709
- inputSchema: {
2772
+ inputSchema: z.object({
2710
2773
  limit: z.number().int().min(1).max(100).optional().default(10).describe("Max rows to return (default 10, max 100)"),
2711
2774
  range: z.enum(["30d", "90d", "all"]).optional().default("30d").describe("Time window for points calculation")
2712
- }
2775
+ })
2713
2776
  },
2714
2777
  async ({ limit, range }) => ({
2715
2778
  content: [
@@ -2730,11 +2793,11 @@ function createMushiServer(config) {
2730
2793
  title: titleOf("award_bonus_points"),
2731
2794
  description: descOf("award_bonus_points"),
2732
2795
  annotations: annotationsFor("award_bonus_points"),
2733
- inputSchema: {
2796
+ inputSchema: z.object({
2734
2797
  external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
2735
2798
  points: z.number().int().min(1).max(5e4).describe("Bonus points to award (max 50,000 per call)"),
2736
2799
  reason: z.string().max(200).describe("Human-readable reason, logged to end_user_activity")
2737
- }
2800
+ })
2738
2801
  },
2739
2802
  async ({ external_user_id, points, reason }) => ({
2740
2803
  content: [
@@ -2759,11 +2822,11 @@ function createMushiServer(config) {
2759
2822
  title: titleOf("set_tier"),
2760
2823
  description: descOf("set_tier"),
2761
2824
  annotations: annotationsFor("set_tier"),
2762
- inputSchema: {
2825
+ inputSchema: z.object({
2763
2826
  external_user_id: z.string().describe("The host-app user id as passed to Mushi.identify()"),
2764
2827
  tier_slug: z.string().describe('Tier slug to assign, e.g. "champion", "contributor", "explorer"'),
2765
2828
  reason: z.string().max(200).optional().describe("Optional reason for manual override")
2766
- }
2829
+ })
2767
2830
  },
2768
2831
  async ({ external_user_id, tier_slug, reason }) => ({
2769
2832
  content: [
@@ -2782,7 +2845,7 @@ function createMushiServer(config) {
2782
2845
  ]
2783
2846
  })
2784
2847
  );
2785
- server.resource(
2848
+ server.registerResource(
2786
2849
  "privacy_status",
2787
2850
  "privacy://status",
2788
2851
  {
@@ -2798,7 +2861,7 @@ function createMushiServer(config) {
2798
2861
  ]
2799
2862
  })
2800
2863
  );
2801
- server.resource(
2864
+ server.registerResource(
2802
2865
  "evolution_history",
2803
2866
  "evolution://history",
2804
2867
  {
@@ -2820,7 +2883,7 @@ function createMushiServer(config) {
2820
2883
  ]
2821
2884
  })
2822
2885
  );
2823
- server.resource(
2886
+ server.registerResource(
2824
2887
  "project_integration_health",
2825
2888
  "project://integration-health",
2826
2889
  {
@@ -2836,7 +2899,7 @@ function createMushiServer(config) {
2836
2899
  ]
2837
2900
  })
2838
2901
  );
2839
- server.resource(
2902
+ server.registerResource(
2840
2903
  "inventory_current",
2841
2904
  "inventory://current",
2842
2905
  {
@@ -2861,9 +2924,9 @@ function createMushiServer(config) {
2861
2924
  title: titleOf("activation_status"),
2862
2925
  description: descOf("activation_status"),
2863
2926
  annotations: annotationsFor("activation_status"),
2864
- inputSchema: {
2927
+ inputSchema: z.object({
2865
2928
  project_id: z.string().optional().describe("Project UUID (defaults to the configured project).")
2866
- }
2929
+ })
2867
2930
  },
2868
2931
  async (args) => {
2869
2932
  const pid = args.project_id ?? projectId;
@@ -2871,7 +2934,7 @@ function createMushiServer(config) {
2871
2934
  return jsonResult(await apiCall(`/v1/admin/activation${qs}`));
2872
2935
  }
2873
2936
  );
2874
- server.resource(
2937
+ server.registerResource(
2875
2938
  "activation_status",
2876
2939
  "mushi://activation",
2877
2940
  {
@@ -2890,10 +2953,12 @@ function createMushiServer(config) {
2890
2953
  };
2891
2954
  }
2892
2955
  );
2893
- server.prompt(
2956
+ server.registerPrompt(
2894
2957
  "summarize_report_for_fix",
2895
- "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.",
2896
- { reportId: z.string().describe("The report UUID to summarize") },
2958
+ {
2959
+ 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.",
2960
+ argsSchema: z.object({ reportId: z.string().describe("The report UUID to summarize") })
2961
+ },
2897
2962
  ({ reportId }) => ({
2898
2963
  messages: [
2899
2964
  {
@@ -2918,10 +2983,12 @@ Lead with the fix. Skip the preamble.`
2918
2983
  ]
2919
2984
  })
2920
2985
  );
2921
- server.prompt(
2986
+ server.registerPrompt(
2922
2987
  "explain_judge_result",
2923
- "Turn raw Sonnet-as-Judge scores into ship / iterate / dismiss guidance. Use after a fix attempt has been judged.",
2924
- { fixId: z.string().describe("The fix_attempt UUID to explain") },
2988
+ {
2989
+ description: "Turn raw Sonnet-as-Judge scores into ship / iterate / dismiss guidance. Use after a fix attempt has been judged.",
2990
+ argsSchema: z.object({ fixId: z.string().describe("The fix_attempt UUID to explain") })
2991
+ },
2925
2992
  ({ fixId }) => ({
2926
2993
  messages: [
2927
2994
  {
@@ -2942,10 +3009,12 @@ No preamble, no score-by-score recap \u2014 the numbers are in the tool output.`
2942
3009
  ]
2943
3010
  })
2944
3011
  );
2945
- server.prompt(
3012
+ server.registerPrompt(
2946
3013
  "triage_next_steps",
2947
- 'Answer "what should I focus on right now?" \u2014 five-item markdown list drawn from the dashboard + recent classified queue.',
2948
- {},
3014
+ {
3015
+ description: 'Answer "what should I focus on right now?" \u2014 five-item markdown list drawn from the dashboard + recent classified queue.',
3016
+ argsSchema: z.object({})
3017
+ },
2949
3018
  () => ({
2950
3019
  messages: [
2951
3020
  {
@@ -2965,10 +3034,12 @@ Prefer items that are bottlenecks or critical severity. Skip filler.`
2965
3034
  ]
2966
3035
  })
2967
3036
  );
2968
- server.prompt(
3037
+ server.registerPrompt(
2969
3038
  "mushi_setup",
2970
- "Diagnose why Mushi setup is stuck and return the single next command or console step to unblock it.",
2971
- {},
3039
+ {
3040
+ description: "Diagnose why Mushi setup is stuck and return the single next command or console step to unblock it.",
3041
+ argsSchema: z.object({})
3042
+ },
2972
3043
  () => ({
2973
3044
  messages: [
2974
3045
  {
@@ -2998,13 +3069,13 @@ Be specific. No generic advice.`
2998
3069
  title: titleOf("map_user_stories", TDD_TOOL_CATALOG),
2999
3070
  description: descOf("map_user_stories", TDD_TOOL_CATALOG),
3000
3071
  annotations: annotationsFor("map_user_stories", TDD_TOOL_CATALOG),
3001
- inputSchema: {
3072
+ inputSchema: z.object({
3002
3073
  projectId: z.string().describe("Project id to map stories for"),
3003
3074
  baseUrl: z.string().url().describe("Live app URL to crawl"),
3004
3075
  maxPages: z.number().int().min(1).max(50).optional().describe("Max pages to crawl (default 20)"),
3005
3076
  provider: z.enum(["firecrawl", "browserbase"]).optional().describe("Crawl provider (default: firecrawl)"),
3006
3077
  cursorCloudRefine: z.boolean().optional().describe("Dispatch Cursor Cloud agent to refine and open a PR")
3007
- }
3078
+ })
3008
3079
  },
3009
3080
  async ({ projectId: projectId2, baseUrl, maxPages, provider, cursorCloudRefine }) => {
3010
3081
  if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
@@ -3029,9 +3100,9 @@ Be specific. No generic advice.`
3029
3100
  title: titleOf("get_map_run_status", TDD_TOOL_CATALOG),
3030
3101
  description: descOf("get_map_run_status", TDD_TOOL_CATALOG),
3031
3102
  annotations: annotationsFor("get_map_run_status", TDD_TOOL_CATALOG),
3032
- inputSchema: {
3103
+ inputSchema: z.object({
3033
3104
  projectId: z.string().describe("Project id")
3034
- }
3105
+ })
3035
3106
  },
3036
3107
  async ({ projectId: projectId2 }) => {
3037
3108
  const data = await apiCall(`/v1/admin/inventory/${projectId2}/map-runs`);
@@ -3044,7 +3115,7 @@ Be specific. No generic advice.`
3044
3115
  title: titleOf("generate_tdd_from_story", TDD_TOOL_CATALOG),
3045
3116
  description: descOf("generate_tdd_from_story", TDD_TOOL_CATALOG),
3046
3117
  annotations: annotationsFor("generate_tdd_from_story", TDD_TOOL_CATALOG),
3047
- inputSchema: {
3118
+ inputSchema: z.object({
3048
3119
  projectId: z.string().describe("Project UUID that owns the inventory story"),
3049
3120
  storyNodeId: z.string().describe(
3050
3121
  "User story node id/slug from accepted inventory (from map_user_stories or inventory.yaml)"
@@ -3052,13 +3123,13 @@ Be specific. No generic advice.`
3052
3123
  automationMode: z.enum(["auto", "review", "approve"]).optional().describe("Approval gate for the generated qa_story (default review \u2192 pending_review)"),
3053
3124
  baseUrl: z.string().url().optional().describe("Override the live app URL the Playwright test should target"),
3054
3125
  openPr: z.boolean().optional().describe("Open a draft GitHub PR with the generated spec (default true)")
3055
- },
3056
- outputSchema: {
3126
+ }),
3127
+ outputSchema: z.object({
3057
3128
  qaStoryId: z.string().describe("UUID of the inserted qa_stories row"),
3058
3129
  prUrl: z.string().nullable().describe("GitHub PR URL when openPr=true and GitHub is connected"),
3059
3130
  approvalStatus: z.string().describe("pending_review | approved \u2014 follows automationMode and project defaults"),
3060
3131
  needsHumanReview: z.boolean().describe("True when the story requires operator approval before scheduled runs")
3061
- }
3132
+ })
3062
3133
  },
3063
3134
  async ({ projectId: projectId2, storyNodeId, automationMode, baseUrl, openPr }) => {
3064
3135
  if (!projectId2) throw new MushiApiError(400, "MISSING_PROJECT", "projectId is required");
@@ -3079,9 +3150,9 @@ Be specific. No generic advice.`
3079
3150
  title: titleOf("improve_qa_story", TDD_TOOL_CATALOG),
3080
3151
  description: descOf("improve_qa_story", TDD_TOOL_CATALOG),
3081
3152
  annotations: annotationsFor("improve_qa_story", TDD_TOOL_CATALOG),
3082
- inputSchema: {
3153
+ inputSchema: z.object({
3083
3154
  projectId: z.string().optional().describe("Project id (omit to run across all projects)")
3084
- }
3155
+ })
3085
3156
  },
3086
3157
  async ({ projectId: projectId2 }) => {
3087
3158
  const data = await apiCall("/v1/admin/pdca/improve-qa-stories", {
@@ -3097,10 +3168,10 @@ Be specific. No generic advice.`
3097
3168
  title: titleOf("run_qa_story", TDD_TOOL_CATALOG),
3098
3169
  description: descOf("run_qa_story", TDD_TOOL_CATALOG),
3099
3170
  annotations: annotationsFor("run_qa_story", TDD_TOOL_CATALOG),
3100
- inputSchema: {
3171
+ inputSchema: z.object({
3101
3172
  projectId: z.string().describe("Project id"),
3102
3173
  qaStoryId: z.string().describe("qa_story id to run")
3103
- }
3174
+ })
3104
3175
  },
3105
3176
  async ({ projectId: projectId2, qaStoryId }) => {
3106
3177
  const data = await apiCall(
@@ -3116,9 +3187,9 @@ Be specific. No generic advice.`
3116
3187
  title: titleOf("list_byok_keys", TDD_TOOL_CATALOG),
3117
3188
  description: descOf("list_byok_keys", TDD_TOOL_CATALOG),
3118
3189
  annotations: annotationsFor("list_byok_keys", TDD_TOOL_CATALOG),
3119
- inputSchema: {
3190
+ inputSchema: z.object({
3120
3191
  projectId: z.string().describe("Project id")
3121
- }
3192
+ })
3122
3193
  },
3123
3194
  async ({ projectId: projectId2 }) => {
3124
3195
  const data = await apiCall(
@@ -3133,14 +3204,14 @@ Be specific. No generic advice.`
3133
3204
  title: titleOf("add_byok_key", TDD_TOOL_CATALOG),
3134
3205
  description: descOf("add_byok_key", TDD_TOOL_CATALOG),
3135
3206
  annotations: annotationsFor("add_byok_key", TDD_TOOL_CATALOG),
3136
- inputSchema: {
3207
+ inputSchema: z.object({
3137
3208
  projectId: z.string().describe("Project id"),
3138
3209
  provider: z.enum(["anthropic", "openai", "firecrawl", "browserbase", "cursor"]).describe("Provider slug"),
3139
3210
  key: z.string().min(8).max(4096).describe("The API key value to add"),
3140
3211
  label: z.string().optional().describe("Human-readable label for this key"),
3141
3212
  priority: z.number().int().min(0).max(1e4).optional().describe("Priority for ordering (lower = higher priority)"),
3142
3213
  baseUrl: z.string().url().max(2048).optional().describe("Allow-listed OpenAI-compatible HTTPS base URL")
3143
- }
3214
+ })
3144
3215
  },
3145
3216
  async ({ projectId: projectId2, provider, key, label, priority, baseUrl }) => {
3146
3217
  const data = await apiCall("/v1/admin/byok/keys", {
@@ -3163,10 +3234,10 @@ Be specific. No generic advice.`
3163
3234
  title: titleOf("test_byok_key", TDD_TOOL_CATALOG),
3164
3235
  description: descOf("test_byok_key", TDD_TOOL_CATALOG),
3165
3236
  annotations: annotationsFor("test_byok_key", TDD_TOOL_CATALOG),
3166
- inputSchema: {
3237
+ inputSchema: z.object({
3167
3238
  projectId: z.string().describe("Project id"),
3168
3239
  keyId: z.string().uuid().describe("Pooled BYOK key id")
3169
- }
3240
+ })
3170
3241
  },
3171
3242
  async ({ keyId }) => {
3172
3243
  const data = await apiCall(`/v1/admin/byok/keys/${encodeURIComponent(keyId)}/test`, {
@@ -3181,10 +3252,10 @@ Be specific. No generic advice.`
3181
3252
  title: titleOf("remove_byok_key", TDD_TOOL_CATALOG),
3182
3253
  description: descOf("remove_byok_key", TDD_TOOL_CATALOG),
3183
3254
  annotations: annotationsFor("remove_byok_key", TDD_TOOL_CATALOG),
3184
- inputSchema: {
3255
+ inputSchema: z.object({
3185
3256
  projectId: z.string().describe("Project id"),
3186
3257
  keyId: z.string().uuid().describe("Pooled BYOK key UUID from list_byok_keys")
3187
- }
3258
+ })
3188
3259
  },
3189
3260
  async ({ keyId }) => {
3190
3261
  const data = await apiCall(`/v1/admin/byok/keys/${encodeURIComponent(keyId)}`, {
@@ -3199,9 +3270,9 @@ Be specific. No generic advice.`
3199
3270
  title: titleOf("list_pending_review_stories", TDD_TOOL_CATALOG),
3200
3271
  description: descOf("list_pending_review_stories", TDD_TOOL_CATALOG),
3201
3272
  annotations: annotationsFor("list_pending_review_stories", TDD_TOOL_CATALOG),
3202
- inputSchema: {
3273
+ inputSchema: z.object({
3203
3274
  projectId: z.string().describe("Project id")
3204
- }
3275
+ })
3205
3276
  },
3206
3277
  async ({ projectId: projectId2 }) => {
3207
3278
  const data = await apiCall(
@@ -3216,11 +3287,11 @@ Be specific. No generic advice.`
3216
3287
  title: titleOf("approve_qa_story", TDD_TOOL_CATALOG),
3217
3288
  description: descOf("approve_qa_story", TDD_TOOL_CATALOG),
3218
3289
  annotations: annotationsFor("approve_qa_story", TDD_TOOL_CATALOG),
3219
- inputSchema: {
3290
+ inputSchema: z.object({
3220
3291
  projectId: z.string().describe("Project id"),
3221
3292
  qaStoryId: z.string().describe("QA story id to approve or reject"),
3222
3293
  status: z.enum(["approved", "rejected"]).describe("New approval status")
3223
- }
3294
+ })
3224
3295
  },
3225
3296
  async ({ projectId: projectId2, qaStoryId, status }) => {
3226
3297
  const data = await apiCall(
@@ -3236,11 +3307,11 @@ Be specific. No generic advice.`
3236
3307
  title: titleOf("reply_to_reporter", TDD_TOOL_CATALOG),
3237
3308
  description: descOf("reply_to_reporter", TDD_TOOL_CATALOG),
3238
3309
  annotations: annotationsFor("reply_to_reporter", TDD_TOOL_CATALOG),
3239
- inputSchema: {
3310
+ inputSchema: z.object({
3240
3311
  reportId: z.string().describe("Report id to reply to"),
3241
3312
  message: z.string().min(1).max(1e4).describe("Message text to send to the reporter"),
3242
3313
  authorName: z.string().optional().describe('Display name for the admin sender (default: "Mushi Admin")')
3243
- }
3314
+ })
3244
3315
  },
3245
3316
  async ({ reportId, message, authorName }) => {
3246
3317
  const data = await apiCall(`/v1/sync/reports/${reportId}/reply`, {
@@ -3256,10 +3327,10 @@ Be specific. No generic advice.`
3256
3327
  title: titleOf("list_qa_story_runs", TDD_TOOL_CATALOG),
3257
3328
  description: descOf("list_qa_story_runs", TDD_TOOL_CATALOG),
3258
3329
  annotations: annotationsFor("list_qa_story_runs", TDD_TOOL_CATALOG),
3259
- inputSchema: {
3330
+ inputSchema: z.object({
3260
3331
  storyId: z.string().describe("QA story id (uuid)"),
3261
3332
  limit: z.number().int().min(1).max(50).optional().default(10).describe("Max runs to return (default 10)")
3262
- }
3333
+ })
3263
3334
  },
3264
3335
  async ({ storyId, limit }) => {
3265
3336
  const data = await apiCall(
@@ -3274,10 +3345,10 @@ Be specific. No generic advice.`
3274
3345
  title: titleOf("get_qa_story_run", TDD_TOOL_CATALOG),
3275
3346
  description: descOf("get_qa_story_run", TDD_TOOL_CATALOG),
3276
3347
  annotations: annotationsFor("get_qa_story_run", TDD_TOOL_CATALOG),
3277
- inputSchema: {
3348
+ inputSchema: z.object({
3278
3349
  storyId: z.string().describe("QA story id (uuid)"),
3279
3350
  runId: z.string().describe("Run id (uuid) to fetch detail for")
3280
- }
3351
+ })
3281
3352
  },
3282
3353
  async ({ storyId, runId }) => {
3283
3354
  const data = await apiCall(
@@ -3300,9 +3371,9 @@ Be specific. No generic advice.`
3300
3371
  title: titleOf("test_notification_channel", TDD_TOOL_CATALOG),
3301
3372
  description: descOf("test_notification_channel", TDD_TOOL_CATALOG),
3302
3373
  annotations: annotationsFor("test_notification_channel", TDD_TOOL_CATALOG),
3303
- inputSchema: {
3374
+ inputSchema: z.object({
3304
3375
  kind: z.enum(["slack", "discord"]).describe("Notification channel kind to test")
3305
- }
3376
+ })
3306
3377
  },
3307
3378
  async ({ kind }) => {
3308
3379
  const data = await apiCall(
@@ -3318,9 +3389,9 @@ Be specific. No generic advice.`
3318
3389
  title: titleOf("run_fullstack_audit"),
3319
3390
  description: descOf("run_fullstack_audit"),
3320
3391
  annotations: annotationsFor("run_fullstack_audit"),
3321
- inputSchema: {
3392
+ inputSchema: z.object({
3322
3393
  project_id: z.string().optional().describe("Project ID to audit. Defaults to the configured project.")
3323
- }
3394
+ })
3324
3395
  },
3325
3396
  async ({ project_id }) => {
3326
3397
  const pid = project_id ?? config.projectId;
@@ -3338,10 +3409,10 @@ Be specific. No generic advice.`
3338
3409
  title: titleOf("get_backend_health"),
3339
3410
  description: descOf("get_backend_health"),
3340
3411
  annotations: annotationsFor("get_backend_health"),
3341
- inputSchema: {
3412
+ inputSchema: z.object({
3342
3413
  project_id: z.string().optional().describe("Project ID. Defaults to the configured project."),
3343
3414
  include_logs: z.boolean().optional().describe("Whether to include recent backend error logs (default: true).")
3344
- }
3415
+ })
3345
3416
  },
3346
3417
  async ({ project_id, include_logs = true }) => {
3347
3418
  const pid = project_id ?? config.projectId;
@@ -3365,9 +3436,9 @@ Be specific. No generic advice.`
3365
3436
  title: titleOf("get_usage"),
3366
3437
  description: descOf("get_usage"),
3367
3438
  annotations: annotationsFor("get_usage"),
3368
- inputSchema: {
3439
+ inputSchema: z.object({
3369
3440
  project_id: z.string().optional().describe("Project ID. Defaults to the configured project.")
3370
- }
3441
+ })
3371
3442
  },
3372
3443
  async ({ project_id }) => {
3373
3444
  const pid = project_id ?? config.projectId;
@@ -3382,12 +3453,12 @@ Be specific. No generic advice.`
3382
3453
  title: titleOf("list_skills"),
3383
3454
  description: descOf("list_skills"),
3384
3455
  annotations: annotationsFor("list_skills"),
3385
- inputSchema: {
3456
+ inputSchema: z.object({
3386
3457
  category: z.string().optional().describe("Filter by category: workflow, debug, test, audit, enhance, \u2026"),
3387
3458
  search: z.string().optional().describe("Free-text search across slug, title, description"),
3388
3459
  page: z.number().optional().describe("Page number (default 1)"),
3389
3460
  limit: z.number().optional().describe("Max results per page (default 200, max 200)")
3390
- }
3461
+ })
3391
3462
  },
3392
3463
  async (args) => {
3393
3464
  const qs = new URLSearchParams();
@@ -3406,9 +3477,9 @@ Be specific. No generic advice.`
3406
3477
  title: titleOf("get_skill"),
3407
3478
  description: descOf("get_skill"),
3408
3479
  annotations: annotationsFor("get_skill"),
3409
- inputSchema: {
3480
+ inputSchema: z.object({
3410
3481
  slug: z.string().describe('Skill slug, e.g. "workflow-fix-and-ship"')
3411
- }
3482
+ })
3412
3483
  },
3413
3484
  async (args) => {
3414
3485
  const data = await apiCall(`/v1/admin/skills/${args.slug}`);
@@ -3421,14 +3492,14 @@ Be specific. No generic advice.`
3421
3492
  title: titleOf("start_skill_pipeline"),
3422
3493
  description: descOf("start_skill_pipeline"),
3423
3494
  annotations: annotationsFor("start_skill_pipeline"),
3424
- inputSchema: {
3495
+ inputSchema: z.object({
3425
3496
  root_skill_slug: z.string().describe('Root skill slug to run, e.g. "workflow-fix-and-ship"'),
3426
3497
  report_id: z.string().optional().describe("Report UUID to attach the pipeline to"),
3427
3498
  mode: z.enum(["handoff", "cloud"]).optional().describe(
3428
3499
  "handoff (default): get context packet for local agent. cloud: auto-dispatch via Cursor Cloud."
3429
3500
  ),
3430
3501
  project_id: z.string().optional().describe("Project UUID. Falls back to the configured project.")
3431
- }
3502
+ })
3432
3503
  },
3433
3504
  async (args) => {
3434
3505
  const resolvedProjectId = args.project_id ?? projectId;
@@ -3446,9 +3517,9 @@ Be specific. No generic advice.`
3446
3517
  title: titleOf("get_pipeline_run"),
3447
3518
  description: descOf("get_pipeline_run"),
3448
3519
  annotations: annotationsFor("get_pipeline_run"),
3449
- inputSchema: {
3520
+ inputSchema: z.object({
3450
3521
  run_id: z.string().describe("Pipeline run UUID")
3451
- }
3522
+ })
3452
3523
  },
3453
3524
  async (args) => {
3454
3525
  const data = await apiCall(`/v1/admin/skills/pipelines/${args.run_id}`);
@@ -3461,14 +3532,14 @@ Be specific. No generic advice.`
3461
3532
  title: titleOf("checkin_pipeline_step"),
3462
3533
  description: descOf("checkin_pipeline_step"),
3463
3534
  annotations: annotationsFor("checkin_pipeline_step"),
3464
- inputSchema: {
3535
+ inputSchema: z.object({
3465
3536
  run_id: z.string().describe("Pipeline run UUID"),
3466
3537
  step_index: z.number().describe("Step index (0-based)"),
3467
3538
  status: z.enum(["running", "passed", "failed", "skipped"]).describe("Step status"),
3468
3539
  notes: z.string().optional().describe("Optional notes or output summary"),
3469
3540
  pr_url: z.string().optional().describe("PR URL opened during this step"),
3470
3541
  agent_ref: z.string().optional().describe("Cursor agentId or external agent reference")
3471
- }
3542
+ })
3472
3543
  },
3473
3544
  async (args) => {
3474
3545
  const { run_id, step_index, ...body } = args;
@@ -3485,13 +3556,13 @@ Be specific. No generic advice.`
3485
3556
  title: titleOf("ask_codebase", CODEBASE_TOOL_CATALOG),
3486
3557
  description: descOf("ask_codebase", CODEBASE_TOOL_CATALOG),
3487
3558
  annotations: annotationsFor("ask_codebase", CODEBASE_TOOL_CATALOG),
3488
- inputSchema: {
3559
+ inputSchema: z.object({
3489
3560
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
3490
3561
  question: z.string().describe("Plain-English question about the repo"),
3491
3562
  thread_id: z.string().optional().describe("Optional thread UUID to continue a conversation"),
3492
3563
  file_path: z.string().optional().describe("Optional file path focus"),
3493
3564
  symbol_name: z.string().optional().describe("Optional symbol name focus")
3494
- }
3565
+ })
3495
3566
  },
3496
3567
  async (args) => {
3497
3568
  const pid = args.project_id ?? projectId;
@@ -3519,12 +3590,12 @@ Be specific. No generic advice.`
3519
3590
  title: titleOf("get_file_summary", CODEBASE_TOOL_CATALOG),
3520
3591
  description: descOf("get_file_summary", CODEBASE_TOOL_CATALOG),
3521
3592
  annotations: annotationsFor("get_file_summary", CODEBASE_TOOL_CATALOG),
3522
- inputSchema: {
3593
+ inputSchema: z.object({
3523
3594
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
3524
3595
  file_path: z.string().describe("Indexed file path"),
3525
3596
  symbol_name: z.string().optional().describe("Optional symbol name within the file"),
3526
3597
  force: z.boolean().optional().describe("Bypass cache and regenerate")
3527
- }
3598
+ })
3528
3599
  },
3529
3600
  async (args) => {
3530
3601
  const pid = args.project_id ?? projectId;
@@ -3542,10 +3613,10 @@ Be specific. No generic advice.`
3542
3613
  title: titleOf("get_codebase_tour", CODEBASE_TOOL_CATALOG),
3543
3614
  description: descOf("get_codebase_tour", CODEBASE_TOOL_CATALOG),
3544
3615
  annotations: annotationsFor("get_codebase_tour", CODEBASE_TOOL_CATALOG),
3545
- inputSchema: {
3616
+ inputSchema: z.object({
3546
3617
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
3547
3618
  force: z.boolean().optional().describe("Bypass cache and regenerate")
3548
- }
3619
+ })
3549
3620
  },
3550
3621
  async (args) => {
3551
3622
  const pid = args.project_id ?? projectId;
@@ -3561,17 +3632,17 @@ Be specific. No generic advice.`
3561
3632
  title: titleOf("search_codebase", CODEBASE_TOOL_CATALOG),
3562
3633
  description: descOf("search_codebase", CODEBASE_TOOL_CATALOG),
3563
3634
  annotations: annotationsFor("search_codebase", CODEBASE_TOOL_CATALOG),
3564
- inputSchema: {
3635
+ inputSchema: z.object({
3565
3636
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
3566
3637
  query: z.string().describe("Plain-English search query against indexed source files"),
3567
3638
  k: z.number().int().min(1).max(20).optional().describe("Max hits to return (default 8, max 20)"),
3568
3639
  scope_prefix: z.string().optional().describe("Optional repo subdirectory prefix to limit search scope")
3569
- },
3570
- outputSchema: {
3640
+ }),
3641
+ outputSchema: z.object({
3571
3642
  results: z.array(z.unknown()).describe("Ranked file/symbol hits with paths, line ranges, and similarity"),
3572
3643
  query: z.string().describe("Normalized query string that was searched"),
3573
3644
  mode: z.string().optional().describe("semantic (embeddings) or name (path/symbol match)")
3574
- }
3645
+ })
3575
3646
  },
3576
3647
  async (args) => {
3577
3648
  const pid = args.project_id ?? projectId;
@@ -3596,11 +3667,11 @@ Be specific. No generic advice.`
3596
3667
  title: titleOf("get_codebase_domains", CODEBASE_TOOL_CATALOG),
3597
3668
  description: descOf("get_codebase_domains", CODEBASE_TOOL_CATALOG),
3598
3669
  annotations: annotationsFor("get_codebase_domains", CODEBASE_TOOL_CATALOG),
3599
- inputSchema: {
3670
+ inputSchema: z.object({
3600
3671
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
3601
3672
  force: z.boolean().optional().describe("Bypass cache and regenerate"),
3602
3673
  scope_prefix: z.string().optional().describe("Optional subdirectory scope prefix")
3603
- }
3674
+ })
3604
3675
  },
3605
3676
  async (args) => {
3606
3677
  const pid = args.project_id ?? projectId;
@@ -3619,20 +3690,20 @@ Be specific. No generic advice.`
3619
3690
  title: titleOf("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
3620
3691
  description: descOf("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
3621
3692
  annotations: annotationsFor("analyze_codebase_impact", CODEBASE_TOOL_CATALOG),
3622
- inputSchema: {
3693
+ inputSchema: z.object({
3623
3694
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)"),
3624
3695
  paths: z.array(z.string()).optional().describe("Changed repo-relative file paths to analyze"),
3625
3696
  source: z.enum(["last_push", "compare", "fix"]).optional().describe("Auto-resolve changed paths instead of manual paths"),
3626
3697
  compare: z.string().optional().describe("GitHub compare range base...head when source=compare"),
3627
3698
  fix_id: z.string().optional().describe("Fix attempt UUID \u2014 uses that PR's changed files when source=fix")
3628
- },
3629
- outputSchema: {
3699
+ }),
3700
+ outputSchema: z.object({
3630
3701
  changed_paths: z.array(z.string()).describe("Resolved set of changed paths analyzed"),
3631
3702
  source: z.string().describe("How changed_paths were resolved (manual, last_push, compare, fix, \u2026)"),
3632
3703
  affected_file_paths: z.array(z.string()).describe("Indexed files that import or depend on changed paths"),
3633
3704
  affected_node_ids: z.array(z.string()).describe("Codebase graph node ids impacted by the change"),
3634
3705
  meta: z.unknown().nullable().optional().describe("Extra resolution metadata when auto-sourced")
3635
- }
3706
+ })
3636
3707
  },
3637
3708
  async (args) => {
3638
3709
  const pid = args.project_id ?? projectId;
@@ -3659,9 +3730,9 @@ Be specific. No generic advice.`
3659
3730
  title: titleOf("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
3660
3731
  description: descOf("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
3661
3732
  annotations: annotationsFor("analyze_wiki_knowledge", CODEBASE_TOOL_CATALOG),
3662
- inputSchema: {
3733
+ inputSchema: z.object({
3663
3734
  project_id: z.string().optional().describe("Project UUID (defaults to configured project)")
3664
- }
3735
+ })
3665
3736
  },
3666
3737
  async (args) => {
3667
3738
  const pid = args.project_id ?? projectId;
@@ -3675,11 +3746,11 @@ Be specific. No generic advice.`
3675
3746
  {
3676
3747
  title: titleOf("use_mushi"),
3677
3748
  description: descOf("use_mushi"),
3678
- inputSchema: {
3749
+ inputSchema: z.object({
3679
3750
  intent: z.string().optional().describe(
3680
3751
  '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.'
3681
3752
  )
3682
- },
3753
+ }),
3683
3754
  annotations: annotationsFor("use_mushi")
3684
3755
  },
3685
3756
  async (args) => {
@@ -3895,23 +3966,31 @@ async function main() {
3895
3966
  hasProjectId: !!PROJECT_ID,
3896
3967
  scopes: SCOPES.join(",")
3897
3968
  });
3898
- const server = createMushiServer({
3899
- version: VERSION,
3900
- apiEndpoint: API_ENDPOINT,
3901
- apiKey: API_KEY,
3902
- projectId: PROJECT_ID || void 0,
3903
- scopes: SCOPES,
3904
- features: FEATURES
3905
- });
3906
- const transport = new StdioServerTransport();
3907
- await server.connect(transport);
3969
+ let server;
3970
+ const handle = serveStdio(
3971
+ (ctx) => {
3972
+ server = createMushiServer({
3973
+ version: VERSION,
3974
+ apiEndpoint: API_ENDPOINT,
3975
+ apiKey: API_KEY,
3976
+ projectId: PROJECT_ID || void 0,
3977
+ scopes: SCOPES,
3978
+ features: FEATURES
3979
+ });
3980
+ log.debug("[mushi-mcp] server instance created", { era: ctx.era });
3981
+ return server;
3982
+ },
3983
+ {
3984
+ onerror: (err) => log.error("stdio transport error", { err: String(err) })
3985
+ }
3986
+ );
3908
3987
  let shuttingDown = false;
3909
3988
  let pollTimer;
3910
3989
  const shutdown = (exitCode) => {
3911
3990
  if (shuttingDown) return;
3912
3991
  shuttingDown = true;
3913
3992
  if (pollTimer) clearInterval(pollTimer);
3914
- void transport.close().finally(() => process.exit(exitCode));
3993
+ void handle.close().finally(() => process.exit(exitCode));
3915
3994
  };
3916
3995
  setActiveShutdown(shutdown);
3917
3996
  process.stdin.on("end", () => shutdown(0));
@@ -3943,7 +4022,7 @@ async function main() {
3943
4022
  const data = await res.json();
3944
4023
  const updatedAt = data?.data?.updatedAt ?? null;
3945
4024
  if (updatedAt && updatedAt !== lastInventoryAt) {
3946
- if (lastInventoryAt !== null) {
4025
+ if (lastInventoryAt !== null && server) {
3947
4026
  await server.server.sendResourceUpdated({ uri: "inventory://current" });
3948
4027
  log.info("inventory://current updated \u2014 notified subscribers", { updatedAt });
3949
4028
  }