@horizon36596/zenith-mcp 0.1.1

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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +18 -0
  3. package/README.md +129 -0
  4. package/dist/editIo.d.ts +15 -0
  5. package/dist/editIo.d.ts.map +1 -0
  6. package/dist/editIo.js +33 -0
  7. package/dist/editIo.js.map +1 -0
  8. package/dist/findings.d.ts +35 -0
  9. package/dist/findings.d.ts.map +1 -0
  10. package/dist/findings.js +76 -0
  11. package/dist/findings.js.map +1 -0
  12. package/dist/index.d.ts +11 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +21 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/project.d.ts +46 -0
  17. package/dist/project.d.ts.map +1 -0
  18. package/dist/project.js +106 -0
  19. package/dist/project.js.map +1 -0
  20. package/dist/resources.d.ts +3 -0
  21. package/dist/resources.d.ts.map +1 -0
  22. package/dist/resources.js +37 -0
  23. package/dist/resources.js.map +1 -0
  24. package/dist/result.d.ts +14 -0
  25. package/dist/result.d.ts.map +1 -0
  26. package/dist/result.js +27 -0
  27. package/dist/result.js.map +1 -0
  28. package/dist/schemas.d.ts +576 -0
  29. package/dist/schemas.d.ts.map +1 -0
  30. package/dist/schemas.js +197 -0
  31. package/dist/schemas.js.map +1 -0
  32. package/dist/server.d.ts +13 -0
  33. package/dist/server.d.ts.map +1 -0
  34. package/dist/server.js +27 -0
  35. package/dist/server.js.map +1 -0
  36. package/dist/spawnCli.d.ts +28 -0
  37. package/dist/spawnCli.d.ts.map +1 -0
  38. package/dist/spawnCli.js +57 -0
  39. package/dist/spawnCli.js.map +1 -0
  40. package/dist/tools/analysis.d.ts +4 -0
  41. package/dist/tools/analysis.d.ts.map +1 -0
  42. package/dist/tools/analysis.js +128 -0
  43. package/dist/tools/analysis.js.map +1 -0
  44. package/dist/tools/auto.d.ts +4 -0
  45. package/dist/tools/auto.d.ts.map +1 -0
  46. package/dist/tools/auto.js +43 -0
  47. package/dist/tools/auto.js.map +1 -0
  48. package/dist/tools/edit.d.ts +3 -0
  49. package/dist/tools/edit.d.ts.map +1 -0
  50. package/dist/tools/edit.js +346 -0
  51. package/dist/tools/edit.js.map +1 -0
  52. package/dist/tools/project.d.ts +4 -0
  53. package/dist/tools/project.d.ts.map +1 -0
  54. package/dist/tools/project.js +58 -0
  55. package/dist/tools/project.js.map +1 -0
  56. package/dist/version.d.ts +3 -0
  57. package/dist/version.d.ts.map +1 -0
  58. package/dist/version.js +4 -0
  59. package/dist/version.js.map +1 -0
  60. package/package.json +67 -0
  61. package/spec/checks.md +313 -0
  62. package/spec/file-format.md +672 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Horizon (FTC 36596)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,18 @@
1
+ Zenith
2
+ Copyright (c) 2026 Horizon (FTC 36596)
3
+
4
+ Zenith is licensed under the MIT License; see LICENSE.
5
+
6
+ Third-party assets
7
+
8
+ - BIOBUZZ field images (apps/web/public/fields/biobuzz/*.webp) by Team Juice 16236, from the r/FTC
9
+ post "BIOBUZZ custom field images (MeepMeep compatible)". Used with the credit the author asks for.
10
+
11
+ Bundled fonts
12
+
13
+ The following fonts are bundled in apps/web/public/fonts/. Each is licensed under the SIL Open Font
14
+ License, Version 1.1. The full licence text ships next to each font file.
15
+
16
+ - Jost, licensed under the SIL Open Font License 1.1 (jost-OFL.txt).
17
+ - IBM Plex Sans, licensed under the SIL Open Font License 1.1 (ibm-plex-sans-OFL.txt).
18
+ - JetBrains Mono, licensed under the SIL Open Font License 1.1 (jetbrains-mono-OFL.txt).
package/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # @horizon36596/zenith-mcp
2
+
3
+ The MCP server for [Zenith](https://libraries.horizon36596.org/zenith/), the FTC autonomous planner
4
+ by Horizon (FTC 36596). It gives an agent the same verbs as the `zenith` CLI, plus structural edit
5
+ tools, over stdio, so the agent can write and fix an auto without hand-editing JSON.
6
+
7
+ Add it to a robot repository's `.mcp.json`:
8
+
9
+ ```json
10
+ {
11
+ "mcpServers": {
12
+ "zenith": {
13
+ "command": "npx",
14
+ "args": ["-y", "@horizon36596/zenith-mcp"]
15
+ }
16
+ }
17
+ }
18
+ ```
19
+
20
+ Requires Node 22 or newer. The PNG render and sim tools run the `zenith` CLI from
21
+ `@horizon36596/zenith-cli`, which installs with this package. Documentation:
22
+ https://libraries.horizon36596.org/zenith/. Licence: MIT.
23
+
24
+ ## Package reference
25
+
26
+ The Zenith MCP server: the same verbs `packages/cli` has, plus structural edit tools, exposed over
27
+ stdio so an agent (Claude Code, or any other MCP-capable client) can author and edit an auto end
28
+ to end without hand-editing JSON. The usage guide, written for the agent side, is the docs site's
29
+ Agents and MCP page (https://libraries.horizon36596.org/zenith/agents-mcp/); this file is the package's own reference.
30
+
31
+ ### Running it
32
+
33
+ ```
34
+ npx -y @horizon36596/zenith-mcp
35
+ ```
36
+
37
+ or, inside this workspace, `node packages/mcp/dist/index.js` after `pnpm build`, or
38
+ `pnpm exec tsx packages/mcp/src/index.ts` straight from source. It speaks the MCP stdio protocol
39
+ and expects to be launched by a client (an `.mcp.json` entry, an SDK `StdioClientTransport`), not
40
+ run interactively. The Agents and MCP page's Setup section has the `.mcp.json` snippet.
41
+
42
+ The server finds its project by walking up from its own working directory looking for
43
+ `zenith.json`, the same way `zenith validate` does; run it from inside the robot repo, or pass
44
+ `project` (`dir` on `zenith.project.open`) on each call.
45
+
46
+ ### Tools
47
+
48
+ | tool | does |
49
+ |---|---|
50
+ | `zenith.project.open` | Robot/field/waypoints summary and the autos list. |
51
+ | `zenith.auto.read` | Reads and schema-validates one auto file. |
52
+ | `zenith.validate` | Schema + feasibility findings for one auto. |
53
+ | `zenith.estimate` | Per-step timing table and total. |
54
+ | `zenith.render` | Renders to SVG or PNG. |
55
+ | `zenith.diff` | Structural diff between two autos. |
56
+ | `zenith.sim` | Runs the robot repo's simulator via the CLI. |
57
+ | `zenith.registry` | `robot.json`'s `commands`/`conditions` in full. |
58
+ | `zenith.waypoints` | `waypoints.json`'s named poses in full. |
59
+ | `zenith.edit.*` | One tool per structural edit primitive in `@horizon36596/zenith-core`'s `edit/` — see the Agents and MCP page. |
60
+
61
+ Every tool takes structured JSON input (a precise `zod` schema per tool — `src/schemas.ts`) and
62
+ returns structured JSON text. Every tool above is implemented and exercised end to end
63
+ (`src/integration.test.ts`, `test/flowB.test.ts`); a call still comes back as
64
+ a tool error (`isError: true`) rather than crashing the server when the input is bad or an edit is
65
+ rejected.
66
+
67
+ Every tool that calls `check`/`ledger` (`zenith.validate`, `zenith.render`, `zenith.edit.*`) resolves
68
+ season rules through `@horizon36596/zenith-seasons` and includes a `seasonWarnings` array (full sentences,
69
+ usually empty) in its result — see the Agents and MCP page's Seasons section.
70
+
71
+ ### Resources
72
+
73
+ - `zenith://spec/file-format` — the File format page (https://libraries.horizon36596.org/zenith/file-format/).
74
+ - `zenith://spec/checks` — the Checks and findings page (https://libraries.horizon36596.org/zenith/checks-and-findings/), every finding code.
75
+
76
+ `spec/*.md` in this package are byte-for-byte copies of `site/docs/file-format.md` and
77
+ `site/docs/checks-and-findings.md` (not symlinks, for a clean publish and a clean Windows checkout), so
78
+ they ship with the published package. `src/specCopies.test.ts` fails when they drift; re-copy them
79
+ whenever those pages change.
80
+
81
+ ### Layout
82
+
83
+ ```
84
+ src/
85
+ project.ts find zenith.json, load the robot/field/waypoints it points at, resolve an
86
+ "auto" argument (name, relative path, or absolute path) to a file
87
+ findings.ts resolve -> plan -> check for a parsed Auto, robot/field override included
88
+ editIo.ts read-current / write-canonical-and-findings, the common tail of every
89
+ zenith.edit.* tool
90
+ schemas.ts zod (v4, the MCP SDK's peer version) input schemas mirroring
91
+ @horizon36596/zenith-schema's (zod 3) auto.ts shapes structurally
92
+ spawnCli.ts spawns the zenith CLI's own built entry point (resolved via `require.resolve`,
93
+ not `npx`, so it works offline against this workspace's build) for the
94
+ render --png and sim tools, so this server's own stdout (the MCP transport)
95
+ never receives the CLI's output directly
96
+ result.ts jsonResult / errorResult / guarded (turn a thrown error into a tool error)
97
+ resources.ts the two zenith://spec/* resources
98
+ specCopies.test.ts checks spec/*.md still match the docs pages they copy
99
+ tools/ one module per tool group: project, auto, analysis, edit
100
+ server.ts createServer(): wires every tool and resource onto an McpServer
101
+ index.ts the stdio binary entry point; also re-exports createServer for embedding
102
+ ```
103
+
104
+ `@horizon36596/zenith-mcp` may depend on `@horizon36596/zenith-core`, `@horizon36596/zenith-schema`, `@horizon36596/zenith-seasons` and Node.
105
+ It never touches `apps/web` or `@horizon36596/zenith-github`, and it reaches a
106
+ season's rules only through `@horizon36596/zenith-seasons`'s registry, never by depending on a `season-*`
107
+ package directly.
108
+
109
+ Note on `zod`: this package's own tool-input schemas (`src/schemas.ts`) are written against `zod`
110
+ 4 (the MCP SDK's peer dependency) and are a structural, not nominal, mirror of `@horizon36596/zenith-schema`'s
111
+ `zod` 3 definitions. A value that passes the MCP-facing schema still re-validates fully inside the
112
+ `@horizon36596/zenith-core` edit primitive it reaches (`parseAuto`, via `finish()`), so the two schemas are a
113
+ convenience for precise tool descriptions and early rejection, not the sole source of truth.
114
+
115
+ ### Testing
116
+
117
+ `pnpm exec vitest run --project mcp` (or `pnpm test` for the whole workspace).
118
+ `src/integration.test.ts` spawns the real server over stdio with the MCP SDK's own client — not
119
+ `createServer()` called in-process — against the example project, so it exercises the actual stdio
120
+ framing an agent host uses: it lists tools and resources, validates an example auto, and
121
+ runs `zenith.edit.setPose` on a temporary copy, asserting the returned findings shape and that the
122
+ file it wrote round-trips through `canonicalize` byte for byte.
123
+
124
+ `test/flowB.test.ts` drives the agent flow (an agent authoring a new auto from nothing)
125
+ against a small clean fixture, also over real stdio: `newAutoFromTemplate`, a path step added
126
+ without a heading (so there is a real `HEADING_MISSING` error to fix), `setHeadingMode`, a
127
+ `shootAll` command step, `validate`, an edit loop until the auto has zero errors, a PNG render (the
128
+ one test in this repository that exercises `spawnCli.ts`'s child-process path end to end) and an
129
+ `estimate` — asserting zero errors at the end and that the file on disk is canonical.
@@ -0,0 +1,15 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import type { Auto } from "@horizon36596/zenith-schema";
3
+ import { type Project } from "./project.js";
4
+ /** Reads and schema-validates the auto a `zenith.edit.*` tool is about to change. */
5
+ export declare function loadCurrent(project: Project, autoRef: string): {
6
+ path: string;
7
+ auto: Auto;
8
+ };
9
+ /**
10
+ * The common tail of every `zenith.edit.*` tool: write the edit's result back in canonical form,
11
+ * then run the same validate pipeline `zenith.validate` does, so the agent sees the effect of its
12
+ * edit immediately instead of having to call `zenith.validate` separately.
13
+ */
14
+ export declare function writeEditResult(project: Project, path: string, updated: Auto, extra?: Record<string, unknown>): CallToolResult;
15
+ //# sourceMappingURL=editIo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editIo.d.ts","sourceRoot":"","sources":["../src/editIo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAExD,OAAO,EAA8D,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AAGxG,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAG3F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,EACb,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAClC,cAAc,CAgBhB"}
package/dist/editIo.js ADDED
@@ -0,0 +1,33 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { canonicalize, loadAuto } from "@horizon36596/zenith-core";
3
+ import { errorCount, findingsForAuto, helpFor, warningCount } from "./findings.js";
4
+ import { confinePath, readJsonFile, relativeToRoot, resolveAutoPath } from "./project.js";
5
+ import { jsonResult } from "./result.js";
6
+ /** Reads and schema-validates the auto a `zenith.edit.*` tool is about to change. */
7
+ export function loadCurrent(project, autoRef) {
8
+ const path = resolveAutoPath(project, autoRef);
9
+ return { path, auto: loadAuto(readJsonFile(path)) };
10
+ }
11
+ /**
12
+ * The common tail of every `zenith.edit.*` tool: write the edit's result back in canonical form,
13
+ * then run the same validate pipeline `zenith.validate` does, so the agent sees the effect of its
14
+ * edit immediately instead of having to call `zenith.validate` separately.
15
+ */
16
+ export function writeEditResult(project, path, updated, extra = {}) {
17
+ // `path` normally already came from `resolveAutoPath` (itself confined), but this is the actual
18
+ // write call, so it is confined again here too: the one place every `zenith.edit.*` primitive's
19
+ // output reaches disk is the one place that must refuse to write outside the project on its own.
20
+ writeFileSync(confinePath(project.root, path), canonicalize("auto", updated), "utf8");
21
+ const { findings, seasonWarnings } = findingsForAuto(updated, project);
22
+ return jsonResult({
23
+ written: relativeToRoot(project, path),
24
+ auto: updated,
25
+ findings,
26
+ errors: errorCount(findings),
27
+ warnings: warningCount(findings),
28
+ seasonWarnings,
29
+ help: helpFor(findings),
30
+ ...extra,
31
+ });
32
+ }
33
+ //# sourceMappingURL=editIo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editIo.js","sourceRoot":"","sources":["../src/editIo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAEnE,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAC;AACxG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,qFAAqF;AACrF,MAAM,UAAU,WAAW,CAAC,OAAgB,EAAE,OAAe;IAC3D,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAgB,EAChB,IAAY,EACZ,OAAa,EACb,QAAiC,EAAE;IAEnC,gGAAgG;IAChG,gGAAgG;IAChG,iGAAiG;IACjG,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IACtF,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvE,OAAO,UAAU,CAAC;QAChB,OAAO,EAAE,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC;QACtC,IAAI,EAAE,OAAO;QACb,QAAQ;QACR,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC;QAC5B,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC;QAChC,cAAc;QACd,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC;QACvB,GAAG,KAAK;KACT,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { type Finding, type FindingCode, type FindingHelp, type Plan } from "@horizon36596/zenith-core";
2
+ import type { Auto, Field, Robot } from "@horizon36596/zenith-schema";
3
+ import { type Project } from "./project.js";
4
+ export interface AutoFindings {
5
+ findings: Finding[];
6
+ /** Full sentences from `@horizon36596/zenith-seasons` when the field names no plugin this build carries. */
7
+ seasonWarnings: string[];
8
+ }
9
+ /** Everything `zenith.estimate` and `zenith.render` need, planned once against the robot and field
10
+ * the auto actually resolves to (its own `robot`/`field` override, when it has one). */
11
+ export interface LoadedAuto {
12
+ plan: Plan;
13
+ robot: Robot;
14
+ field: Field;
15
+ findings: Finding[];
16
+ seasonWarnings: string[];
17
+ }
18
+ /**
19
+ * Resolves `auto.robot`/`auto.field` (falling back to the project's), plans, and runs `check()` —
20
+ * the one shared load every MCP tool that needs a `Plan` should go through, so `zenith.estimate`
21
+ * and `zenith.render` can no longer quietly plan against the project's robot/field while
22
+ * `zenith.validate` (via `findingsForAuto` below) honours the auto's own override.
23
+ */
24
+ export declare function loadAutoAndPlan(auto: Auto, project: Project): LoadedAuto;
25
+ /** Schema plus feasibility findings for a parsed `Auto`, against a project's robot and field. */
26
+ export declare function findingsForAuto(auto: Auto, project: Project): AutoFindings;
27
+ export declare const errorCount: (findings: readonly Finding[]) => number;
28
+ export declare const warningCount: (findings: readonly Finding[]) => number;
29
+ /**
30
+ * What each finding code present means and how to fix it (core's `FINDING_HELP`), keyed by code,
31
+ * so an agent explaining a finding to a person uses the same words the editor and
32
+ * `zenith validate --explain` do.
33
+ */
34
+ export declare function helpFor(findings: readonly Finding[]): Partial<Record<FindingCode, FindingHelp>>;
35
+ //# sourceMappingURL=findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../src/findings.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,IAAI,EACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAEtE,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AA6B1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,4GAA4G;IAC5G,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;wFACwF;AACxF,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAWD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,UAAU,CAgBxE;AAED,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY,CAG1E;AAED,eAAO,MAAM,UAAU,GAAI,UAAU,SAAS,OAAO,EAAE,KAAG,MACS,CAAC;AAEpE,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,OAAO,EAAE,KAAG,MACS,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAI/F"}
@@ -0,0 +1,76 @@
1
+ import { check, estimate, FINDING_HELP, loadField, loadRobot, plan, resolve, } from "@horizon36596/zenith-core";
2
+ import { resolveSeason } from "@horizon36596/zenith-seasons";
3
+ import { isAbsolute, join } from "node:path";
4
+ import { readJsonFile } from "./project.js";
5
+ /**
6
+ * An auto may name its own robot or field file (`auto.robot`/`auto.field`); when it does, that
7
+ * file wins over `zenith.json`'s, and a broken override becomes a SCHEMA finding rather than a
8
+ * thrown error, the same as `zenith validate` (`packages/cli/src/commands/validate.ts`).
9
+ */
10
+ function override(project, path, load, fallback, findings) {
11
+ if (path === undefined)
12
+ return fallback;
13
+ const full = isAbsolute(path) ? path : join(project.root, path);
14
+ try {
15
+ return load(readJsonFile(full));
16
+ }
17
+ catch (error) {
18
+ findings.push({
19
+ severity: "error",
20
+ stepId: "(file)",
21
+ code: "SCHEMA",
22
+ message: `Cannot use ${path}: ${error.message.split("\n")[0] ?? ""}`,
23
+ });
24
+ return fallback;
25
+ }
26
+ }
27
+ /**
28
+ * `auto.name` ends up in filesystem paths and (via `zenith sim`, `packages/cli/src/commands/sim.ts`)
29
+ * a shell command; the CLI enforces this at load time, and every MCP tool that plans an auto goes
30
+ * through `loadAutoAndPlan` below, so the same charset is enforced here too
31
+ * — as an error finding rather than a thrown error, so
32
+ * an agent editing the file sees exactly why, the same as any other SCHEMA problem.
33
+ */
34
+ const AUTO_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
35
+ /**
36
+ * Resolves `auto.robot`/`auto.field` (falling back to the project's), plans, and runs `check()` —
37
+ * the one shared load every MCP tool that needs a `Plan` should go through, so `zenith.estimate`
38
+ * and `zenith.render` can no longer quietly plan against the project's robot/field while
39
+ * `zenith.validate` (via `findingsForAuto` below) honours the auto's own override.
40
+ */
41
+ export function loadAutoAndPlan(auto, project) {
42
+ const findings = [];
43
+ if (!AUTO_NAME_PATTERN.test(auto.name)) {
44
+ findings.push({
45
+ severity: "error",
46
+ stepId: "(file)",
47
+ code: "SCHEMA",
48
+ message: `"name" must match ${AUTO_NAME_PATTERN.source} (letters, digits, ".", "_", "-" only); got ${JSON.stringify(auto.name)}.`,
49
+ });
50
+ }
51
+ const robot = override(project, auto.robot, loadRobot, project.robot, findings);
52
+ const field = override(project, auto.field, loadField, project.field, findings);
53
+ const planned = plan(resolve(auto, project.waypoints), robot, field);
54
+ const season = resolveSeason(field);
55
+ findings.push(...check(planned, estimate(planned, robot), robot, field, season.rules));
56
+ return { plan: planned, robot, field, findings, seasonWarnings: [...season.warnings] };
57
+ }
58
+ /** Schema plus feasibility findings for a parsed `Auto`, against a project's robot and field. */
59
+ export function findingsForAuto(auto, project) {
60
+ const { findings, seasonWarnings } = loadAutoAndPlan(auto, project);
61
+ return { findings, seasonWarnings };
62
+ }
63
+ export const errorCount = (findings) => findings.filter((finding) => finding.severity === "error").length;
64
+ export const warningCount = (findings) => findings.filter((finding) => finding.severity === "warning").length;
65
+ /**
66
+ * What each finding code present means and how to fix it (core's `FINDING_HELP`), keyed by code,
67
+ * so an agent explaining a finding to a person uses the same words the editor and
68
+ * `zenith validate --explain` do.
69
+ */
70
+ export function helpFor(findings) {
71
+ const help = {};
72
+ for (const finding of findings)
73
+ help[finding.code] = FINDING_HELP[finding.code];
74
+ return help;
75
+ }
76
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../src/findings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { createServer, SERVER_NAME, SERVER_VERSION } from "./server.js";
3
+ export { createServer, SERVER_NAME, SERVER_VERSION };
4
+ /**
5
+ * The `@horizon36596/zenith-mcp` binary: runs the server over stdio, the transport Claude Code's
6
+ * `.mcp.json` and every other MCP host speak (site/docs/agents-mcp.md). `createServer()` is
7
+ * exported separately so a test, or a host that wants to embed the server in-process, does not have
8
+ * to spawn a subprocess.
9
+ */
10
+ export declare function main(): Promise<void>;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAErD;;;;;GAKG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAI1C"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { pathToFileURL } from "node:url";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { createServer, SERVER_NAME, SERVER_VERSION } from "./server.js";
5
+ export { createServer, SERVER_NAME, SERVER_VERSION };
6
+ /**
7
+ * The `@horizon36596/zenith-mcp` binary: runs the server over stdio, the transport Claude Code's
8
+ * `.mcp.json` and every other MCP host speak (site/docs/agents-mcp.md). `createServer()` is
9
+ * exported separately so a test, or a host that wants to embed the server in-process, does not have
10
+ * to spawn a subprocess.
11
+ */
12
+ export async function main() {
13
+ const server = createServer();
14
+ const transport = new StdioServerTransport();
15
+ await server.connect(transport);
16
+ }
17
+ const entry = process.argv[1];
18
+ if (entry !== undefined && import.meta.url === pathToFileURL(entry).href) {
19
+ await main();
20
+ }
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC9B,IAAI,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACzE,MAAM,IAAI,EAAE,CAAC;AACf,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { Field, Link, Robot, Waypoints } from "@horizon36596/zenith-schema";
2
+ export declare const LINK_FILE = "zenith.json";
3
+ export declare class ProjectError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ /**
7
+ * Resolves `target` (relative to `root`, or absolute) and asserts it lands inside `root`. Every
8
+ * path an MCP tool derives from its own input — a `name`, an `out`, an `auto` reference — must go
9
+ * through this before it is read or written: an agent's tool call is untrusted input in exactly
10
+ * the way a hand-typed CLI argument is not. Throws
11
+ * `ProjectError` rather than silently clamping the path, so the refusal is visible to the caller.
12
+ */
13
+ export declare function confinePath(root: string, target: string): string;
14
+ export interface Project {
15
+ /** The directory holding `zenith.json`. Every path in the link file is relative to it. */
16
+ root: string;
17
+ link: Link;
18
+ robot: Robot;
19
+ field: Field;
20
+ waypoints: Waypoints | undefined;
21
+ }
22
+ /** Walks up from a directory looking for `zenith.json`, the way `git` looks for `.git`. */
23
+ export declare function findProjectRoot(from: string): string | null;
24
+ export declare function readJsonFile(path: string): unknown;
25
+ /**
26
+ * Loads `zenith.json` and the robot, field and waypoints files it points at, starting the search
27
+ * for the link file at `dir` (an explicit `project` argument, when a tool call gives one) or at
28
+ * the server process's own working directory otherwise — an MCP server is normally started from
29
+ * inside the robot repo (site/docs/agents-mcp.md), the same way `zenith validate` finds a project by
30
+ * walking up from the shell's current directory.
31
+ */
32
+ export declare function resolveProject(dir?: string): Project;
33
+ /** A path relative to `project.root`, with forward slashes so it reads the same on every OS. */
34
+ export declare function relativeToRoot(project: Project, absolute: string): string;
35
+ /**
36
+ * Resolves the `auto` argument every tool takes to an absolute path. It accepts three forms: an
37
+ * absolute path, a path (containing a slash or ending `.auto.json`) relative to the project root,
38
+ * or a bare name — the common case, matching `zenith new <name>` — resolved against the project's
39
+ * `autosDir`. Every form is confined to the project root (finding 2): a tool call is untrusted
40
+ * input, so `"../../../../outside/.gitconfig"` or an absolute path outside the project is
41
+ * refused here rather than followed, for a read as much as a write.
42
+ */
43
+ export declare function resolveAutoPath(project: Project, auto: string): string;
44
+ /** Every `*.auto.json` file name (without the extension) in the project's `autosDir`. */
45
+ export declare function listAutoNames(project: Project): string[];
46
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAEjF,eAAO,MAAM,SAAS,gBAAgB,CAAC;AAEvC,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOhE;AAED,MAAM,WAAW,OAAO;IACtB,0FAA0F;IAC1F,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;CAClC;AAED,2FAA2F;AAC3F,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ3D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYlD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAsBpD;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtE;AAED,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAOxD"}
@@ -0,0 +1,106 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, relative, resolve as resolvePath } from "node:path";
3
+ import { loadField, loadLink, loadRobot, loadWaypoints } from "@horizon36596/zenith-core";
4
+ export const LINK_FILE = "zenith.json";
5
+ export class ProjectError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "ProjectError";
9
+ }
10
+ }
11
+ /**
12
+ * Resolves `target` (relative to `root`, or absolute) and asserts it lands inside `root`. Every
13
+ * path an MCP tool derives from its own input — a `name`, an `out`, an `auto` reference — must go
14
+ * through this before it is read or written: an agent's tool call is untrusted input in exactly
15
+ * the way a hand-typed CLI argument is not. Throws
16
+ * `ProjectError` rather than silently clamping the path, so the refusal is visible to the caller.
17
+ */
18
+ export function confinePath(root, target) {
19
+ const resolved = isAbsolute(target) ? resolvePath(target) : resolvePath(root, target);
20
+ const rel = relative(root, resolved);
21
+ if (rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute(rel)) {
22
+ throw new ProjectError(`"${target}" resolves outside the project root (${root}); refusing to use it.`);
23
+ }
24
+ return resolved;
25
+ }
26
+ /** Walks up from a directory looking for `zenith.json`, the way `git` looks for `.git`. */
27
+ export function findProjectRoot(from) {
28
+ let current = resolvePath(from);
29
+ for (;;) {
30
+ if (existsSync(join(current, LINK_FILE)))
31
+ return current;
32
+ const parent = dirname(current);
33
+ if (parent === current)
34
+ return null;
35
+ current = parent;
36
+ }
37
+ }
38
+ export function readJsonFile(path) {
39
+ let text;
40
+ try {
41
+ text = readFileSync(path, "utf8");
42
+ }
43
+ catch {
44
+ throw new ProjectError(`Cannot read ${path}.`);
45
+ }
46
+ try {
47
+ return JSON.parse(text);
48
+ }
49
+ catch (error) {
50
+ throw new ProjectError(`${path} is not valid JSON: ${error.message}`);
51
+ }
52
+ }
53
+ /**
54
+ * Loads `zenith.json` and the robot, field and waypoints files it points at, starting the search
55
+ * for the link file at `dir` (an explicit `project` argument, when a tool call gives one) or at
56
+ * the server process's own working directory otherwise — an MCP server is normally started from
57
+ * inside the robot repo (site/docs/agents-mcp.md), the same way `zenith validate` finds a project by
58
+ * walking up from the shell's current directory.
59
+ */
60
+ export function resolveProject(dir) {
61
+ const from = dir === undefined ? process.cwd() : resolvePath(dir);
62
+ const root = findProjectRoot(from);
63
+ if (root === null) {
64
+ throw new ProjectError(`No ${LINK_FILE} in ${from} or any directory above it. Pass "project" with the robot repo's ` +
65
+ "directory, or run the MCP server from inside that repo.");
66
+ }
67
+ const linkPath = join(root, LINK_FILE);
68
+ const link = loadLink(readJsonFile(linkPath));
69
+ const inRoot = (relativePath) => isAbsolute(relativePath) ? relativePath : join(root, relativePath);
70
+ return {
71
+ root,
72
+ link,
73
+ robot: loadRobot(readJsonFile(inRoot(link.robot))),
74
+ field: loadField(readJsonFile(inRoot(link.field))),
75
+ waypoints: link.waypoints === undefined ? undefined : loadWaypoints(readJsonFile(inRoot(link.waypoints))),
76
+ };
77
+ }
78
+ /** A path relative to `project.root`, with forward slashes so it reads the same on every OS. */
79
+ export function relativeToRoot(project, absolute) {
80
+ return relative(project.root, absolute).split("\\").join("/");
81
+ }
82
+ /**
83
+ * Resolves the `auto` argument every tool takes to an absolute path. It accepts three forms: an
84
+ * absolute path, a path (containing a slash or ending `.auto.json`) relative to the project root,
85
+ * or a bare name — the common case, matching `zenith new <name>` — resolved against the project's
86
+ * `autosDir`. Every form is confined to the project root (finding 2): a tool call is untrusted
87
+ * input, so `"../../../../outside/.gitconfig"` or an absolute path outside the project is
88
+ * refused here rather than followed, for a read as much as a write.
89
+ */
90
+ export function resolveAutoPath(project, auto) {
91
+ if (isAbsolute(auto) || auto.includes("/") || auto.includes("\\") || auto.endsWith(".auto.json")) {
92
+ return confinePath(project.root, auto);
93
+ }
94
+ return confinePath(project.root, join(project.link.autosDir, `${auto}.auto.json`));
95
+ }
96
+ /** Every `*.auto.json` file name (without the extension) in the project's `autosDir`. */
97
+ export function listAutoNames(project) {
98
+ const dir = join(project.root, project.link.autosDir);
99
+ if (!existsSync(dir))
100
+ return [];
101
+ return readdirSync(dir)
102
+ .filter((entry) => entry.endsWith(".auto.json"))
103
+ .map((entry) => entry.slice(0, -".auto.json".length))
104
+ .sort();
105
+ }
106
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerResources(server: McpServer): void;
3
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmBzE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8BzD"}
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ /**
5
+ * `zenith://spec/file-format` and `zenith://spec/checks`: the two reference pages an agent needs to
6
+ * know the file format's rules and the check codes, served as MCP resources so a client can read them
7
+ * without the docs site or a clone of this repository.
8
+ *
9
+ * `packages/mcp/spec/file-format.md` and `packages/mcp/spec/checks.md` are byte-for-byte copies of the
10
+ * public docs pages `site/docs/file-format.md` and `site/docs/checks-and-findings.md`, kept next to
11
+ * the package's own source (not symlinked, for a clean Windows checkout and a clean npm publish) so
12
+ * they ship with the published `@horizon36596/zenith-mcp` package. `specCopies.test.ts` fails when
13
+ * they drift; re-copy them when the pages change.
14
+ */
15
+ const specDir = join(dirname(fileURLToPath(import.meta.url)), "../spec");
16
+ function readSpec(file) {
17
+ return readFileSync(join(specDir, file), "utf8");
18
+ }
19
+ export function registerResources(server) {
20
+ server.registerResource("file-format", "zenith://spec/file-format", {
21
+ title: "Zenith file format",
22
+ description: "The file format reference: the five file kinds, canonical form, and every field of " +
23
+ "*.auto.json — step kinds, segments, heading modes, markers.",
24
+ mimeType: "text/markdown",
25
+ }, (uri) => ({
26
+ contents: [{ uri: uri.href, mimeType: "text/markdown", text: readSpec("file-format.md") }],
27
+ }));
28
+ server.registerResource("checks", "zenith://spec/checks", {
29
+ title: "Zenith checks and findings",
30
+ description: "The checks and findings reference: every finding code zenith.validate can raise, with " +
31
+ "what causes it and how to fix it.",
32
+ mimeType: "text/markdown",
33
+ }, (uri) => ({
34
+ contents: [{ uri: uri.href, mimeType: "text/markdown", text: readSpec("checks.md") }],
35
+ }));
36
+ }
37
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC;;;;;;;;;;GAUG;AACH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAEzE,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,gBAAgB,CACrB,aAAa,EACb,2BAA2B,EAC3B;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,qFAAqF;YACrF,6DAA6D;QAC/D,QAAQ,EAAE,eAAe;KAC1B,EACD,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACR,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;KAC3F,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,gBAAgB,CACrB,QAAQ,EACR,sBAAsB,EACtB;QACE,KAAK,EAAE,4BAA4B;QACnC,WAAW,EACT,wFAAwF;YACxF,mCAAmC;QACrC,QAAQ,EAAE,eAAe;KAC1B,EACD,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACR,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;KACtF,CAAC,CACH,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ /** Every tool returns structured JSON text, pretty-printed so a human reading a transcript can too. */
3
+ export declare function jsonResult(data: unknown): CallToolResult;
4
+ /**
5
+ * Turns a thrown error into an MCP tool error result instead of letting it crash the server. The
6
+ * known error types this project throws (`ProjectError`, core's `EditError` and
7
+ * `NotImplementedError`, schema's `SchemaError`) all carry a message written for whoever is
8
+ * holding the file; anything else is reported by its message too, since a stack trace is not
9
+ * useful to an agent reading a tool result.
10
+ */
11
+ export declare function errorResult(error: unknown): CallToolResult;
12
+ /** Wraps a tool handler so anything it throws becomes a tool error result instead of crashing the server. */
13
+ export declare function guarded<Args extends unknown[]>(handler: (...args: Args) => Promise<CallToolResult> | CallToolResult): (...args: Args) => Promise<CallToolResult>;
14
+ //# sourceMappingURL=result.d.ts.map