Skip to content

MCP draft/write — Phase A1: draft engine + page authoring tools (WIP)#100

Merged
kennethphough merged 6 commits into
masterfrom
feature/mcp-draft-write
Jun 1, 2026
Merged

MCP draft/write — Phase A1: draft engine + page authoring tools (WIP)#100
kennethphough merged 6 commits into
masterfrom
feature/mcp-draft-write

Conversation

@kennethphough

Copy link
Copy Markdown
Member

WIP draft PR to validate via CI. Phase A1 of the MCP draft/write feature: surface-generic draft engine + the four page authoring tools (write_page_part, list_drafts, read_draft, discard_draft). commit_draft + tests land in follow-up commits before un-drafting.

🤖 Generated with Claude Code

kennethphough and others added 6 commits June 1, 2026 02:21
Surface-generic draft engine for the MCP draft/write feature. A draft is a
pending, non-live version (draft=1, is_current=0); the live content and the
is_current version are untouched until commit.

- KytePageVersion: add `draft` + `draft_source` columns (+ additive migration
  4.10.0_page_version_draft_flag.sql, migration-first / safe on old code).
- src/Mcp/Service/DraftService.php: surface-generic engine (driven by a
  surface descriptor so functions/scripts plug in later). Owns write/read/
  list/discard over the *Version + *VersionContent tables, reusing the same
  sha256 content-hash + bz2 + reference_count conventions as
  KytePageController so drafts dedup against existing version content.
- src/Mcp/Tools/DraftTools.php: write_page_part [draft], list_drafts [read],
  read_draft [read], discard_draft [draft].

WIP: commit_draft (publish promotion) and tests land next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- commit_draft [commit scope]: publishes a draft to live and promotes it to
  the current version. Publishes through the REAL KytePageController pipeline
  (live KytePageData write + S3 + CloudFront), so output is identical to a
  human Publish.
- KytePageController::publishPage made public static (it used no $this);
  added publishFromContent() which expands the page via getObject, overlays
  draft content, writes KytePageData, and publishes.
- ModelController: optional `internal` construction flag that skips the
  session authenticate() check, so a trusted server-side caller (MCP commit)
  can use the controller with an account context but no HTTP session.
- DraftService::commitDraft constructs the controller in internal mode,
  publishes, then demotes the prior current version and flips the draft to
  is_current. markCurrentNotCurrent helper.
- tests/DraftServiceTest.php: authoring loop (write/accumulate/read/list/
  discard), live-version-untouched, cross-account denial. Commit excluded
  (AWS publish; validated on dev).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
KytePageController::publishPage now returns the S3 page-write result (the
stream wrapper returns false on a failed upload rather than throwing);
existing void callers ignore it harmlessly. publishFromContent publishes
FIRST and bails on a false result BEFORE mutating live KytePageData, so a
failed commit leaves the page untouched instead of half-applied.
DraftService::commitDraft wraps the publish in try/catch and only promotes
the draft (is_current=1, draft=0) on success — on failure it returns
committed:false + error and the draft is left intact for retry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sions

Add `draft` + `draft_source` to KyteFunctionVersion and KyteScriptVersion
(same pattern as KytePageVersion) so the draft engine can extend to
controller functions and standalone scripts. Replaces the page-only
migration with 4.10.0_version_draft_flags.sql covering all three version
tables. Additive / migration-first / inert on older code.

Engine + commit dispatch + tools for functions/scripts land next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… engine

- DraftService: functionSurface() + scriptSurface() descriptors,
  surfaceByLabel()/allSurfaces(); generalized parentInApplication
  (page/script via site→app, function via controller→app) and commitDraft
  via a per-surface publishForSurface():
    * page  → KytePageController::publishFromContent (S3 + CF)
    * script→ KyteScriptController::publishFromContent (S3 + CF)
    * function → write Function.code + ControllerController::generateCodeBase
      (DB-only, no S3)
- KyteScriptController::publishFromContent (publish-first, captures S3 result,
  invalidates CF; skips the heavier include_all all-pages regeneration for now).
- DraftTools restructured: write_page_part / write_function_code /
  write_script_content; read_draft/discard_draft/commit_draft take a `surface`
  arg; list_drafts spans all surfaces (each row tagged with surface).
- tests: function + script authoring loops (write/read/discard, live untouched).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gelog

MCP requests carry an account but no KyteUser, so the draft version insert
passed created_by=null — fine for KytePageVersion (nullable) but
KyteFunctionVersion.created_by is NOT NULL, which broke function/script
drafts. Default created_by to 0 (system) when there's no user. Also adds the
v4.10.0 CHANGELOG entry for the MCP draft/write feature.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kennethphough
kennethphough marked this pull request as ready for review June 1, 2026 08:55
@kennethphough
kennethphough merged commit 4616606 into master Jun 1, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant