Skip to content

feat(api): post/surface wire vocabulary — /api/posts routes + publish_post MCP tools#140

Merged
benvinegar merged 2 commits into
mainfrom
feat/wire-post-surface-vocab
Jun 25, 2026
Merged

feat(api): post/surface wire vocabulary — /api/posts routes + publish_post MCP tools#140
benvinegar merged 2 commits into
mainfrom
feat/wire-post-surface-vocab

Conversation

@benvinegar

Copy link
Copy Markdown
Member

Steps A3 (HTTP routes) + A4 (MCP) of the workspace ▸ session ▸ post ▸ surface rename. #135 renamed the data model + store (Post, Surface block, getPost/createPost/listPosts); this brings the wire layer in line, while keeping every old route, tool, param, and response field working as a deprecated alias. Fully additive — nothing removed (removal is a later telemetry-gated cleanup wave).

A3 — routes (server/app.ts)

New canonical paths, each reusing the existing handler (no duplicated logic — renderSurfacePage/sessionSurfacePage were factored out and registered on both old and new paths):

  • GET/POST/PUT/DELETE /api/posts[/:id], GET /p/:id, GET /session/:id/p/:postId, GET /api/sessions/:id/posts.
  • Publish/revise accept surfaces (preferred) or legacy parts; the rich-render handler reads ?surface=N or legacy ?part=N.
  • isPublicReadAllowed gains /p/ and /api/posts/. All old paths (/s/:id, /api/surfaces, /api/snippets, /session/:id/s/:surfaceId, /api/sessions/:id/surfaces) remain.

A4 — MCP (server/mcpSpec.ts, server/mcpHttp.ts, mcp/server.ts)

  • New tools publish_post / update_post / list_posts on both HTTP and stdio, delegating to the same handlers; they advertise surfaces and emit /p/<id> URLs (old tools keep /s/<id>).
  • HTTP cases accept args.surfaces ?? args.parts; reply_to_user now takes postId (preferred) with surfaceId as a deprecated alias.
  • Old tools kept and marked "Deprecated alias of …"; instructions + descriptions + schemas rewritten to post/surface/workspace vocabulary (per-kind names html/markdown/… unchanged).

Out of scope (intentional)

guide/*.md (A6) and bin/sideshow.js (A7) untouched — the CLI keeps working via the retained legacy routes. Model/store/types were already done in #135.

Verification

  • npm run typecheck (3 tsc projects) · ✅ npm test 255/255 (+11 new) · ✅ npm run lint (oxlint) · ✅ npm run format:check · ✅ npm run build
  • Changeset: sideshow minor.
  • New tests assert new routes ≡ old, surfaces and legacy parts bodies, the three new MCP tools, and reply_to_user via postId and legacy surfaceId.
  • Reviewed by a fresh-context reviewer (no back-compat regressions; the one gap it found — reply_to_user advertising postId — is fixed in this PR with a test).

🤖 Generated with Claude Code

benvinegar and others added 2 commits June 25, 2026 12:18
…_post MCP tools

Steps A3 (HTTP routes) + A4 (MCP) of the workspace/post/surface rename. A prior
PR renamed the data model + store (Post, Surface block, getPost/createPost/…);
this updates the WIRE layer to match, while keeping every old route, tool, param,
and response field working as a deprecated alias. Fully additive — nothing removed.

A3 — routes (server/app.ts), all reusing the existing handlers:
- New: GET/POST/PUT/DELETE /api/posts[/:id], GET /p/:id,
  GET /session/:id/p/:postId, GET /api/sessions/:id/posts.
- Publish/revise accept `surfaces` (preferred) or legacy `parts`; the rich-page
  handler reads ?surface=N or legacy ?part=N. isPublicReadAllowed gains /p/ and
  /api/posts/. Old /s/, /api/surfaces, /api/snippets, /session/:id/s/ all kept.

A4 — MCP (server/mcpSpec.ts, server/mcpHttp.ts, mcp/server.ts):
- New tools publish_post / update_post / list_posts on HTTP + stdio, delegating
  to the same handlers; they advertise `surfaces` and emit /p/<id> URLs.
- reply_to_user now takes `postId` (preferred) with `surfaceId` as a deprecated
  alias.
- Old tools kept, descriptions prefixed "Deprecated alias of …". Instructions +
  descriptions + schemas rewritten to post/surface/workspace vocabulary.

guide/*.md (A6) and bin/sideshow.js (A7) intentionally untouched; the CLI keeps
working via the retained legacy routes.

Tests: +11 in test/api.test.ts (new routes ≡ old, surfaces+parts bodies, new MCP
tools, reply_to_user postId + legacy surfaceId). 255/255 pass, typecheck/lint/
format clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ocab, neutral id desc

From a Sonnet code-review pass on the post/surface wire change:

- PUT /api/posts/:id (and /api/surfaces/:id): an explicit `surfaces: null` was
  silently treated as a title-only update (200) because `surfaces ?? parts`
  coalesced null→undefined and skipped validation. Gate on field *presence* so
  null is a 400, matching POST and the legacy `parts` path. (+regression test)
- mcpHttp publish: the empty-blocks error now uses the tool's own vocabulary —
  "a surface needs at least one part" for the deprecated publish_surface/
  publish_snippet, "a post needs at least one surface" for publish_post.
- mcpSpec: neutral `id` description ("Post id returned by a publish call") so the
  deprecated update_surface tool no longer points agents at publish_post.
- Tests: cross-matrix coverage (/s/:id?surface=, /p/:id?part=) and reply_to_user
  with neither postId nor surfaceId (clean error).

256/256 tests, typecheck/lint/format clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@benvinegar benvinegar merged commit 14c48dd into main Jun 25, 2026
9 checks passed
@benvinegar benvinegar deleted the feat/wire-post-surface-vocab branch June 25, 2026 17:08
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