feat(mcp): harden the MCP server — renderability, verb narrowing, real version#37
Merged
Merged
Conversation
The 'dry-run compileTimeline and turn a CompileError into {exportable,
blockers}' dance was copy-pasted in the CLI timeline show and the clip ui
exportable route. Lift it into one exported checkExportable in compile.ts so
a third caller (the MCP server) reuses it and every surface reports
renderability identically. Pure refactor — no behaviour change.
Split set_transform out of the verb union into its own schema and expose EditableVerbSchema — the eight verbs whose results the compiler can render. CompositionVerbSchema is unchanged (the full set incl. set_transform, for the CLI/op layer). This lets the UI and MCP accept only renderable edits without re-listing the verbs. No behaviour change on its own.
Three pre-release-audit follow-ups for the MCP server:
- timeline_show/edit now report {exportable, blockers} (via the shared
checkExportable), so an agent learns a doc cannot render up front instead
of only at timeline_export. summarizeComposition gains a typed return.
- timeline_edit validates against EditableVerbSchema, so set_transform — which
the compiler refuses — is no longer accepted (mirrors the ClipInspector UI).
- the server version is read from package.json (walking up so it resolves from
both src under vitest and the bundled dist/cli.js) instead of a hardcoded
string that would go stale at the next release. A test asserts the handshake
version matches package.json.
Verified the bundled path with a real node dist/cli.js mcp stdio handshake.
This was referenced Jun 19, 2026
|
🎉 This PR is included in version 0.3.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Harden the MCP server (3 v0.3.0 audit follow-ups)
Implements the three non-blocking follow-ups the v0.3.0 pre-release audit flagged for the new MCP server. All are read-or-validate hardening — no change to the editing/render semantics.
1 —
timeline_show/timeline_editreport renderabilitytimeline_showreturned only rev/duration/canvas/clips — unlike the CLItimeline showand the UI's/api/timeline/exportable, which both report{exportable, blockers}. An MCP agent could author a gapped/unrenderable doc that showed clean and only discovered the problem attimeline_export. Bothshowandeditnow includeexportable+blockers.The dry-run-compile dance (
compileTimeline→ catchCompileError→{exportable, blockers}) was copy-pasted in the CLI and the UI route, so I lifted it into one exportedcheckExportableincompile.ts; all three surfaces now share it and answer identically. An empty doc still answers (exportable:false+ a blocker) —shownever throws.2 —
timeline_editno longer acceptsset_transformtimeline_editvalidated against the fullCompositionVerbSchema, which includesset_transform— a verb the compiler refuses (non-identity transforms failassertCompilable), and which the UI'sClipInspectoralready omits for exactly this reason. Split the verb union intoEditableVerbSchema(the eight renderable verbs) + the full schema (still used by the CLI/op layer), and pointed the MCP input schema at the editable set.set_transformis now rejected at the boundary instead of producing a doc that only fails at export.3 — Server version is read from
package.json, not hardcodedThe handshake version was a hardcoded string. It happened to be right for the current release, but the next bump would have announced the wrong version. It's now read from
package.jsonby walking up from the module — which resolves correctly from both the source (under vitest) and the bundleddist/cli.jsat runtime, where the depth topackage.jsondiffers (a fixed relative path would break one).Verified against the bundled artifact, not just source: a real
node dist/cli.js mcpstdio handshake reports the version straight frompackage.json, with clean stdout.Verification
pnpm type-check✓ · 545 tests ✓ (3 new MCP tests: version, exportability-in-show,set_transformrejection) ·pnpm lintexit 0 ·pnpm build✓ · bundled stdio smoke ✓.🤖 Generated with Claude Code