Skip to content

Release: MCP engine refactor (Phases 0-14e) + docs alignment (R1-R4)#47

Merged
ABB65 merged 68 commits intomainfrom
next-mcp
Apr 18, 2026
Merged

Release: MCP engine refactor (Phases 0-14e) + docs alignment (R1-R4)#47
ABB65 merged 68 commits intomainfrom
next-mcp

Conversation

@ABB65
Copy link
Copy Markdown
Member

@ABB65 ABB65 commented Apr 18, 2026

The next-mcp integration branch is ready to merge into main. After this merges, the Changesets action opens a "Version Packages" PR that publishes the releases below.

Release manifest

Package Current Bump New
@contentrain/mcp 1.2.0 minor 1.3.0
@contentrain/types 0.4.x minor 0.5.0
contentrain 0.4.3 minor 0.5.0
@contentrain/rules 0.3.x minor 0.4.0
@contentrain/skills 0.3.x minor 0.4.0
@contentrain/query 5.1.4 patch 5.1.5

14 changesets pending. Verified via pnpm release:status + pnpm release:check.

Studio handoff pre-flight — satisfied

  • @contentrain/types ≥ 0.5.0 ✓ (handoff pre-req was ≥ 0.4.2)
  • @contentrain/mcp ≥ 1.3.0

What ships

MCP engine refactor (Phase 0-10) — already in next-mcp

Provider-agnostic plan/apply engine. RepoProvider contract in @contentrain/types. LocalProvider + GitHubProvider + GitLabProvider, stdio + Streamable HTTP transports. Capability gates, branch migration to cr/*, validator unification, conformance harness.

Phase 13 — serve correctness + secure-by-default auth

Drift fixes in contentrain serve, sync-warning cache, capability badge, branch health banner, secure-by-default HTTP MCP auth (hard error on non-localhost bind without --authToken).

Phase 14a — MCP boundary hardening + CLI commands

LocalProvider implements full RepoProvider. ToolProvider = RepoProvider. WorkflowMode / SyncResult / MergeResult.sync? consolidated in @contentrain/types. New CLI commands: merge, describe, describe-format, scaffold.

Phase 14b — serve backend

/api/describe-format, /api/preview/merge routes. meta:changed + file-watch:error WS events. Defensive Zod on /api/normalize/plan/reject. File-watcher error surfaced instead of silently swallowed.

Phase 14c — doctor extraction

@contentrain/mcp/core/doctor + contentrain_doctor MCP tool + /api/doctor route. CLI doctor --json with non-zero exit on failure.

Phase 14d — Serve UI integration

/doctor + /format pages. Merge preview panel on BranchDetail. Watcher-down banner. SEO metadata toast. Dictionary-first UI strings.

Phase 14e — cross-cutting CLI flags

--json on diff/generate, --watch on validate, global --debug + CONTENTRAIN_DEBUG env var.

Phase R1 — rules/skills parity

Added contentrain_merge + contentrain_doctor to public MCP_TOOLS catalog (15 → 17). Migrated legacy contentrain/{op}/... branch prefixes to cr/* across essentials / prompts / skills. Cross-package parity tests lock the surfaces in lockstep with @contentrain/mcp.

Phase R2 — package READMEs

All 6 package READMEs rewritten against actual src/ exports + package.json exports + MCP tool registry. 3 parallel audit agents.

Phase R3 — production docs site

22 docs pages under docs/ audited by 5 parallel agents, then rewritten. VitePress build verified. Every claim cross-checked against source.

Phase R3b — root guidance

README.md / CLAUDE.md / AGENTS.md aligned with current tool count + optional-peer-deps decision.

Phase R4 — release manifest

Release pre-flight verification + manifest.

Verification

  • pnpm release:check → passed
  • pnpm release:status → 14 changesets, 5 minor + 1 patch
  • pnpm -r typecheck → 0 errors across 8 packages
  • pnpm lint → 0 warnings on 419 files
  • npx vitepress build → success
  • R1 parity tests → rules 16/16, skills 85/85
  • MCP fast suite → 450 passed / 2 skipped / 30 files (14a baseline)
  • CLI unit suite → 38/38

Post-merge flow

  1. main receives the merge
  2. Changesets action opens a "Version Packages" PR
  3. Merging that PR publishes to npm + creates per-package tags
  4. Studio team can upgrade to @contentrain/mcp 1.3.0 + @contentrain/types 0.5.0

🤖 Generated with Claude Code

Contentrain and others added 30 commits March 29, 2026 20:42
…octor usage analysis, vocabulary activation

Three-tier content governance to prevent and detect quality issues in AI-generated content:

**Prevention (content_save):**
- Detect duplicate dictionary values at write time and return advisory warnings
- Cross-reference with vocabulary.json for canonical term consistency
- Advisories are soft warnings — save succeeds, agent decides action

**Validation (validator):**
- Intra-dictionary duplicate value detection (warning severity)
- Cross-dictionary duplicate value detection across models (notice severity)

**Analysis (doctor --usage):**
- Unused content keys: scan source files for unreferenced dictionary keys, collection IDs, document slugs
- Duplicate values: detect different keys mapping to identical strings
- Missing locale coverage: find keys present in default locale but absent in others

**Vocabulary activation:**
- Include vocabulary in describe_format, status, and describe (dictionary hint)
- Cross-reference vocabulary terms in content_save advisories

**Skills & rules:**
- Add Content Governance section to essential rules
- Add advisory review step and vocabulary guidance to content skill
- Add dictionary deduplication rules to quality references

Tests: 7 new tests covering advisory detection, validator duplicates, and integration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scaffolds the safety net for the upcoming MCP engine refactor:

- Contentrain CLA (Apache ICLA-derived with relicense grant, so the
  monorepo retains flexibility to ship parts under AGPL or proprietary
  ee/ licenses in Studio's orbit)
- cla-assistant-lite GitHub workflow — activates once the
  CLA_SIGNATURES_TOKEN repo secret is provisioned; until then PRs flow
  through without blocking
- CONTRIBUTING.md documents the sign-off flow
- Byte-identical conformance fixture suite under
  packages/mcp/tests/fixtures/conformance/ — locks the current write
  path output before refactor-induced refactoring starts. Each scenario
  ships setup/, scenario.json and expected/; the harness uses
  vi.setSystemTime + CONTENTRAIN_SOURCE=mcp-local for determinism
- Three seed scenarios cover the collection, singleton and dictionary
  write paths; scenarios.json tracks the 15-fixture plan
- .gitignore excludes /.internal symlink (private refactor docs live
  in studio/.internal/refactor/)

Refs: .internal/refactor/01-mcp-engine-plan.md phase 0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(phase-0): foundation — CLA + conformance harness
…ader

Adds the provider-agnostic plumbing the rest of the refactor will build on.
Pure additions; no existing source changed, no public surface touched.

- core/contracts/ defines the provider interfaces: RepoReader (read-only),
  RepoWriter (applyPlan), RepoProvider (combined), plus the supporting types
  FileChange, ProviderCapabilities, Branch, FileDiff, MergeResult, Commit,
  CommitAuthor, ApplyPlanInput. LOCAL_CAPABILITIES is pre-set for the
  LocalProvider slot we'll wire up in phase 3.
- core/serialization/ re-exports canonicalStringify, sortKeys,
  parseMarkdownFrontmatter and serializeMarkdownFrontmatter from
  @contentrain/types behind a single module, plus a typed parseCanonical
  helper. This is the import surface Studio will consume post-refactor.
- providers/local/reader.ts ships LocalReader — a node:fs-backed RepoReader.
  Plumbing only; nothing calls it yet. Phase 2 injects it into core ops.
- tests/serialization-parity.test.ts locks the byte-level serialization
  contract with 20 fixtures (empty, unicode TR/CJK/emoji, nested, null and
  undefined filtering, array order preservation, field-order override,
  escape chars, number/boolean/mixed, object-map sort) plus 2 invariants
  (trailing newline, 2-space indent).

Scope note: the original phase 1 plan also migrated core/*.ts reads onto
the RepoReader. During implementation it became clear phase 2 rewrites
those same functions anyway (plan/apply + FileChange[] return), so the
read migration was moved to phase 2 to avoid throwaway work. Plan doc
(.internal/refactor/01-mcp-engine-plan.md) updated.

Verification:
- pnpm vitest run conformance serialization-parity → 25/25 green
- oxlint + tsc --noEmit on all new files → clean

Refs: .internal/refactor/01-mcp-engine-plan.md phase 1,
      .internal/refactor/03-conformance-contract.md §2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(mcp): phase 1 — contracts, serialization, LocalReader
First op on the plan/apply pattern. planContentSave reads existing state
through a RepoReader and returns a FileChange[] + typed result + advisories
— no disk writes inside the core function. The tool handler applies the
plan to the worktree via a thin shim and hands off to the existing
transaction flow (tx.commit still writes context.json through its current
write-through path — that stays in Faz 3 where LocalProvider takes over).

- core/ops/types.ts: OpPlan<T> envelope and ContentSaveEntryResult
- core/ops/paths.ts: content-root-relative content/meta/document path
  helpers that mirror current resolveJsonFilePath/resolveMdFilePath logic
- core/ops/content-save.ts: planContentSave covers all four model kinds
  (singleton/collection/dictionary/document), preserves collision checks,
  dictionary-value advisories and vocabulary cross-ref advisories, and
  uses in-memory coalescing so N entries → single content file + single
  meta file per (path)
- core/ops/apply-to-worktree.ts: FileChange[] → worktree shim (writeFile /
  rm); replaced by LocalProvider.applyPlan in Faz 3
- core/ops/index.ts: barrel export
- tools/content.ts content_save: invokes planContentSave before opening a
  transaction, applies plan.changes inside tx.write, hands off the same
  context payload to tx.commit. Error handling preserved. writeContent
  import dropped from this handler (still available for delete path).

Verification:
- pnpm vitest run conformance serialization-parity → 25/25 green
- pnpm vitest run tools/content -t 'contentrain_content_save' → 10/10
  green (singleton, collection new, collection update, document,
  dictionary, unknown model, branch health, same-second concurrency,
  advisory when duplicate value, no advisory when unique)
- oxlint + tsc --noEmit → clean
- Public MCP tool surface unchanged (same response JSON shape)

Refs: .internal/refactor/01-mcp-engine-plan.md phase 2,
      .internal/refactor/00-principles.md §5.1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(mcp): phase 2.1 — plan/apply for content_save
Second op migrated to the plan/apply pattern. planContentDelete reads
existing state through a RepoReader, computes the file removals and
updates needed (entry removed from an object-map content file, meta
file updated or deleted when empty, etc.), and emits a FileChange[]
that the tool handler applies inside the existing tx.write callback.

- core/ops/content-delete.ts: planContentDelete covers all four kinds
  - Collection: removes the entry ID from each locale's content +
    meta files; deletes a meta file when the removal empties it; auto-
    discovers locales when no locale argument is supplied
  - Singleton: nulls the locale's content file + meta file (or the
    whole meta dir contents for non-i18n)
  - Dictionary: key-scoped removal rewrites the content file in place,
    locale-scoped removal nulls content + meta
  - Document: strategy-aware (file / suffix / directory / none),
    plus meta removal under .contentrain/meta/{id}/{slug}/
- tools/content.ts content_delete: builds the plan before opening the
  transaction, fails fast on validation errors (unknown keys for
  dictionary, missing id/slug), applies plan.changes inside tx.write,
  tx.commit unchanged. deleteContent import dropped.
- core/ops/index.ts: barrel exports planContentDelete and its types.

Verification:
- pnpm vitest run conformance serialization-parity → 25/25 green
- pnpm vitest run tools/content -t 'contentrain_content_delete' → 3/3
  green (collection entry from object-map, collection metadata across
  all locales, document slug directory)
- oxlint + tsc --noEmit → clean
- Public response JSON shape preserved (deleted, files_removed, git)

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

refactor(mcp): phase 2.2 — plan/apply for content_delete
Third and final op batch of phase 2. The public content and model tool
surface is now fully on plan/apply; legacy writeContent/writeModel/
deleteContent/deleteModel remain in core but are no longer imported by
the content or model tool handlers. Bulk, setup/init, and normalize
apply continue to use the legacy surface until their own phase lifts.

- core/ops/model-save.ts: planModelSave emits a single FileChange for
  .contentrain/models/{id}.json with canonical field order preserved.
  Detects create vs update by probing reader.readFile. Empty
  content/meta dir scaffolding is dropped — git does not track empty
  directories and write helpers mkdir -p on demand, so the legacy
  ensureDir calls were operationally inert.
- core/ops/model-delete.ts: planModelDelete enumerates the model
  definition file plus any content and meta files (up to two
  directory levels deep — the schema never nests more than that).
  Emits null FileChange per file. Returns the legacy three-entry
  files_removed summary (models/{id}.json, content/{domain}/{id}/,
  meta/{id}/) so the tool response shape is unchanged.
- tools/model.ts: model_save builds the plan before opening the
  transaction; model_delete loads the model for existence + reference
  checks, passes the loaded definition to planModelDelete, then
  applies plan.changes inside tx.write. writeModel/deleteModel imports
  dropped from this file.
- core/ops/index.ts: barrel exports extended.

Verification:
- pnpm vitest run conformance serialization-parity → 25/25 green
- pnpm vitest run tools/model → 8/8 green (create, update, field type
  validation, relation validation, uninitialized guard, delete,
  referenced-model block, nonexistent model)
- oxlint + tsc --noEmit → clean
- Public MCP tool response JSON shape preserved

Phase 2 wrap: content_save + content_delete + model_save + model_delete
are all plan/apply. Bulk tool, setup init/scaffold, and normalize apply
stay on the legacy write path and will migrate with phases 3, 4 and 6
respectively.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(mcp): phase 2.3 — plan/apply for model_save + model_delete (phase 2 wrap)
Introduces LocalProvider as the single entry point for local-filesystem
content operations. Tool handlers no longer juggle createTransaction /
tx.write / tx.commit / tx.complete / tx.cleanup by hand — they construct
a LocalProvider, call planXxx against it (provider implements
RepoReader), and drive the write through a single provider.applyPlan
call. Provider owns worktree lifecycle and cleanup internally.

Phase 3 stays behaviour-preserving: LocalProvider's applyPlan delegates
to the existing createTransaction pipeline, so worktree setup, content
branch guard, merge/update-ref, selective sync and error paths are bit-
for-bit identical. Phase 6 will fold transaction.ts directly into
providers/local/ and drop the wrap.

- providers/local/types.ts
  LocalApplyPlanInput (branch + changes + message + optional context +
  optional workflowOverride), LocalApplyResult (extends Commit with
  workflowAction + selective-sync details + warning), LocalContextUpdate,
  LocalSelectiveSyncResult.
- providers/local/provider.ts
  LocalProvider class. Implements RepoReader by delegating to LocalReader.
  applyPlan wraps createTransaction, emits a Commit-shaped result with
  author derived from env + defaults. Internal tx.cleanup in finally so
  tool handlers don't need to.
- providers/local/index.ts
  Export LocalProvider and the new types alongside LocalReader.
- tools/content.ts content_save, content_delete
  Swap LocalReader + createTransaction scaffolding for a single
  LocalProvider instance. Drop manual tx.cleanup calls. Response JSON
  shape preserved (git.action, git.commit, git.sync).
- tools/model.ts model_save, model_delete
  Same migration pattern. model_save uses provider for both the plan
  read and the apply; model_delete passes the already-loaded model
  straight to planModelDelete.

Verification:
- pnpm vitest run conformance serialization-parity → 25/25 green
- pnpm vitest run tools/content.test tools/model.test → 25/25 green
  (10 content_save + 2 advisory + 3 delete + 4 list + 8 model ~ 405s)
- oxlint + tsc --noEmit → clean
- Public MCP tool response JSON shape unchanged

What's still on the legacy transaction.ts path (deferred):
- bulk.ts (copy_locale / update_status / delete_entries)
- setup.ts (contentrain_init / contentrain_scaffold)
- normalize apply (core/apply-manager.ts)
- workflow.ts (submit / merge — these call git directly, not via tx)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(mcp): phase 3 — LocalProvider wraps the transaction flow
Reorganises validation into @contentrain/mcp/core/validator and adds a
Studio-signature per-entry validator. validateProject behaviour is
preserved (workflow tests pass), Studio can now swap its content-
validation.ts import to this module with zero signature change.

Structure:
- core/validator/ replaces the flat core/validator.ts
  - entry.ts      validateContent(data, fields, modelId, locale,
                  entryId?, ctx?) — union of MCP's per-entry checks
                  (secret detection, schema validation via @contentrain/
                  types validateFieldValue, unique constraint) and
                  Studio's extras (email/url heuristics, polymorphic
                  relation structure, nested object + array-of-object
                  recursion).
  - relation-integrity.ts  checkRelationIntegrity — async relation
                  existence check with host-provided loadContent.
                  Warning-severity (matches Studio's policy; MCP's
                  legacy error severity belongs to validateProject's
                  project-wide orchestrator, not per-entry).
  - schedule.ts   validateScheduleFields — extracted from the legacy
                  validator; project.ts now imports it instead of
                  inlining.
  - project.ts    legacy validator.ts moved verbatim (only path + the
                  schedule import changed). validateProject's behaviour
                  is bit-for-bit identical.
  - index.ts      barrel exporting validateContent, validateProject,
                  checkRelationIntegrity, validateScheduleFields, and
                  the ValidationContext / ValidateOptions / ValidateResult
                  types.
- tools/workflow.ts + tools/content.ts
  Import validateProject from core/validator/index.js instead of the
  flat file.
- package.json
  Build script rewires src/core/validator.ts → src/core/validator/index.ts.
  The ./core/validator subpath export now points to dist/core/validator/
  index.mjs so external consumers (Studio) get all of the above from a
  single import.

Tests:
- tests/core/validator/entry.test.ts — 20 fixtures covering the union
  of rules (valid entry, required field missing, type mismatch, secret
  detection, unique constraint with/without exclusion, email and URL
  heuristics, single + polymorphic relation structure, relations min/
  max + item type, array of strings + integers, nested object, array-
  of-object field path prefix, error context propagation).
- Existing tools/workflow.test.ts still passes (12/12 — validateProject
  exercised end-to-end).
- Conformance + serialization-parity still at 25/25 (no byte drift).

What this does NOT do:
- Refactor validateProject internals to call validateContent. The
  per-entry logic inside project.ts still mirrors the legacy validator;
  unifying it would change observable behaviour (would start emitting
  Studio-style email/url warnings during contentrain_validate) and is a
  phase 4.2 task once we have the fixture coverage to catch regressions.
- Delete Studio's content-validation.ts. That handoff is documented in
  .internal/refactor/02-studio-handoff.md phase S3.

Verification:
- pnpm vitest run conformance serialization-parity core/validator → 45/45
- pnpm vitest run tools/workflow → 12/12 (210s)
- oxlint + tsc --noEmit → clean
- Public MCP tool response JSON shape unchanged

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(mcp): phase 4 — unified validator module
…ules

Phase 4 promoted core/validator.ts to a directory (core/validator/
with index.ts + entry.ts + relation-integrity.ts + schedule.ts +
project.ts), which matches the published package surface — the
./core/validator export maps to dist/core/validator/index.{mjs,d.ts}.

The root tsconfig's path mapping still only pointed at the flat .ts
form, so downstream packages (CLI today, Studio later) could no longer
typecheck imports of @contentrain/mcp/core/validator during dev.
Accept both shapes:

  \"@contentrain/mcp/core/*\": [
    \"packages/mcp/src/core/*.ts\",
    \"packages/mcp/src/core/*/index.ts\"
  ]

TypeScript picks the first resolvable match, so flat modules keep
working and directory-based ones (validator today, ops/contracts/
serialization tomorrow if we re-export them under core/*) resolve
via their index.ts. One-line unblock — no source changes, no test
changes; pnpm typecheck passes across all 6 packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(tsconfig): resolve @contentrain/mcp/core/* to directory-based modules
…h ops)

First non-local RepoProvider. Implements the full RepoProvider interface
from phase 1 contracts on top of an Octokit-driven GitHub REST + Git
Data API client. No hard dependency — @octokit/rest is an optional peer
and the factory loads it via dynamic import.

Structure:
- providers/github/
  - client.ts         type-only alias for @octokit/rest Octokit
  - types.ts          RepoRef (owner/name/contentRoot) + GitHubAuth
                      (PAT today, App auth reserved for phase 5.2)
  - paths.ts          contentRoot-aware path resolver (forward-slash,
                      no leading slash — matches Git Data API conventions)
  - capabilities.ts   GITHUB_CAPABILITIES — no worktree, no source RW,
                      no AST; push + PR fallback + branch protection
                      detection ON. Normalize's capability-gate lands
                      in phase 6.
  - reader.ts         GitHubReader — readFile (base64 decode via
                      repos.getContent, falls back to git.getBlob for
                      files > 1 MB), listDirectory (empty on 404),
                      fileExists.
  - apply-plan.ts     applyPlanToGitHub — base SHA resolution
                      (existing branch HEAD or base branch), blob
                      creation per non-null FileChange, null-sha tree
                      entry for deletions, tree + commit + ref
                      create/update. One atomic Git Data commit.
  - branch-ops.ts     list / create / delete / diff / merge / isMerged
                      / getDefaultBranch — paginated where it matters,
                      normalised return types.
  - provider.ts       GitHubProvider class wiring the pieces together
                      behind RepoProvider.
  - factory.ts        createGitHubClient + createGitHubProvider —
                      dynamic import + PAT auth path; App-auth path
                      throws pointing at phase 5.2.
  - index.ts          barrel export.

Tests (tests/providers/github/, 16 total):
- reader.test.ts (11)   base64 decode, large-file blob fallback,
                        directory rejection, 404 → empty array,
                        contentRoot prefix, 500 propagation.
- apply-plan.test.ts (5) branch update vs. create, deletions (null-sha),
                        default branch fallback, contentRoot prefix.

Package surface:
- package.json adds @octokit/rest as optional peer dependency and dev
  dependency; adds providers/github/index.ts to the tsdown build
  entries; exposes ./providers/github as a subpath export (dist paths
  follow the repo's existing .d.ts/.mjs shape).
- Root tsconfig path mapping accepts both flat and directory-based
  providers/* modules so local typecheck + Studio consumption both
  resolve.

What phase 5.1 does NOT ship (by design):
- HTTP transport — the Node-side server that fronts MCP tool calls
  and hands them to GitHubProvider. Phase 5.2 adds it along with the
  CLI --http flag.
- GitHub App installation auth. Phase 5.2, same reason.
- Tool handler wiring for remote providers. Tool handlers still use
  LocalProvider exclusively; the HTTP transport will slot in a
  provider resolver.

Verification:
- pnpm vitest run conformance serialization-parity core/validator
  providers/github → 61/61 (no drift anywhere)
- pnpm build → dist/providers/github/ artefacts present
- pnpm typecheck → all 6 packages green
- oxlint on new code → 0 warnings

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor(mcp): phase 5.1 — GitHubProvider (reader + applyPlan + branch ops)
Ships an HTTP-mounted MCP endpoint alongside the stdio transport. The
same McpServer (every tool registered today) answers over both
transports — the only thing that changes is the wire. Phase 5.3 will
plug GitHubProvider into the routing so a single HTTP endpoint can
serve multiple repos; this PR keeps LocalProvider as the only backend
to avoid touching tool handlers before the parallel Studio handoff
lands.

MCP — packages/mcp:
- src/server/http/server.ts
  startHttpMcpServer({ projectRoot, port, host?, authToken?, path? })
  wraps @modelcontextprotocol/sdk's StreamableHTTPServerTransport in a
  node:http listener. Stateful session IDs (randomUUID) because the
  SDK's initialize flow relies on them. Optional Bearer-token guard
  that short-circuits with 401 before the transport sees the request.
  Mount path defaults to /mcp; anything else 404s. Handle exposes
  server + mcp + url + close() for clean teardown.
- src/server/http/index.ts
  barrel — exports startHttpMcpServer and the option/handle types.
- tests/server/http.test.ts (4 tests, ~3.7s)
  real end-to-end: spins the server on an ephemeral port, drives it
  with StreamableHTTPClientTransport from the SDK, asserts
  contentrain_describe_format returns a spec over HTTP, auth guard
  accepts matching Bearer + rejects missing, mount-path is enforced.
- package.json — adds ./server/http to exports and to the tsdown
  build entry list.

CLI — packages/cli/src/commands/serve.ts:
- New --mcp-http flag (env: CONTENTRAIN_MCP_HTTP=true). When set, the
  CLI starts the HTTP MCP server on the same --port/--host it already
  accepts, with an optional --auth-token (env: CONTENTRAIN_AUTH_TOKEN)
  Bearer guard. Mode precedence stays stdio > mcp-http > web-ui so
  IDE setups keep working unchanged.
- SIGINT/SIGTERM handlers close the server cleanly before exit.
- Warn when binding 0.0.0.0 without an auth token.

Root:
- tsconfig.json adds @contentrain/mcp/server/http path mapping so the
  CLI's static import resolves during typecheck.

Verification:
- pnpm vitest run conformance serialization-parity core/validator
  providers/github server/http → 65/65 green
- pnpm build → dist/server/http/ artefacts emitted
- pnpm typecheck → all 6 packages clean
- oxlint on new code + the CLI change → 0 warnings

What 5.2 does NOT ship (staged for 5.3):
- GitHubProvider routing via HTTP — tool handlers still bind to
  LocalProvider at construction time. Phase 5.3 adds a provider
  resolver on the server side so an HTTP request can select
  GitHubProvider for a given project.
- App-based GitHub auth (installation tokens). Pending phase 5.3 too.

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

refactor(mcp,cli): phase 5.2 — HTTP transport for MCP
tsdown emits declaration files as .d.mts (ESM type declarations) but
every subpath export in package.json claimed ./dist/…/*.d.ts. Local
typecheck kept passing because the root tsconfig paths map to source
.ts files, but external consumers installing @contentrain/mcp from
npm would hit a types-resolution failure — TypeScript looks at the
exports' "types" field first, the claimed .d.ts path does not exist
on disk, and downstream modules become any.

This bites Studio's planned phase S3 (drop content-validation.ts and
content-paths.ts, import from @contentrain/mcp/core/validator +
@contentrain/mcp/core/content-manager). Replacing the 19 .d.ts paths
with .d.mts matches what tsdown actually emits so the package is
consumable as shipped.

No source changes, no behaviour changes, no test changes.

Verification:
- pnpm vitest run conformance serialization-parity core/validator
  providers/github server/http → 65/65 green
- pnpm typecheck → all 6 packages clean
- grep '\\.d\\.ts"' packages/mcp/package.json → 0 hits (was 19)

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

fix(mcp): align subpath exports with tsdown's .d.mts output
… to validateContent

Completes the validator unification. validateProject's per-kind handlers
(collection, singleton, document) no longer carry inline secret /
field / unique / relation logic — they call validateContent for the
synchronous per-entry pass and checkRelationIntegrity for the async
relation-existence pass. validateDictionaryModel stays as-is (dictionaries
have no declared field schema). Project-wide orchestration (locale file
existence, canonical sort, entry parity, orphan meta/content, cross-
dictionary duplicate values) stays in project.ts — it is not per-entry
work.

This is the observable-behaviour change the Phase 4 commit deliberately
deferred: contentrain_validate now emits the Studio-side rules the unified
validateContent carries (email / URL heuristic warnings, polymorphic
relation structure errors, nested object + array-of-object recursion).
Every rule was already shipped in @contentrain/mcp/core/validator's
validateContent since Phase 4, so Studio's future handoff lands without
surprises.

Changes:
- core/validator/relation-integrity.ts
  - Adds `severity` option (default 'warning' for Studio compat, MCP's
    project validator passes 'error').
  - Adds `resolveTarget` option — a richer target resolver that can
    signal target-model absence ({ exists: false }) so callers emit a
    "target model not found" error, matching the legacy checkRelation
    behaviour MCP relies on.
  - Backwards compatible: Studio's existing call sites pass only
    loadContent and get warning-severity, no target-model lookup.
- core/validator/entry.ts
  - Unique check now uses String(value) comparison (matches legacy
    validator.ts uniqueness semantics — catches cross-type duplicates
    like 42 vs '42' that strict === would miss).
- core/validator/project.ts
  - New `buildProjectTargetResolver(projectRoot)` produces a ResolvedTarget
    for the current fs layout: collections return their object-map
    (empty {} when locale file missing so broken-ref detection still
    fires), documents return a slug-exists marker map, singletons and
    dictionaries return { content: null } so key enforcement is skipped.
  - New `scanUndeclaredFieldsForSecrets` preserves the legacy behaviour
    of flagging secrets in data fields that are not declared in
    model.fields — validateContent only sees declared fields.
  - validateCollectionModel / validateSingletonModel / validateDocumentModel
    each:
      1. run the undeclared-field secret scan,
      2. call validateContent(data, fields, id, locale, entryId?, ctx?) and
         merge errors,
      3. call checkRelationIntegrity with severity:'error' + resolveTarget
         and merge errors.
    Document handler recomposes the validateContent call without the
    synthetic `body` field (legacy skipped body there) and carries the
    slug through the merged errors.
  - Legacy inline checkRelation helper + inline secret/field/unique
    loops removed.
- core/validator/index.ts
  - Re-exports CheckRelationIntegrityOptions, LegacyLoadContent,
    ResolvedTarget so external consumers (Studio) can type their
    callbacks.

Tests:
- tests/tools/workflow.test.ts adds three validateProject-level cases
  locking the newly-delegated behaviour:
    * emits email heuristic warning (type:'email' field with invalid
      string)
    * emits url heuristic warning (type:'url' field with missing
      protocol)
    * flags nested object field errors ({ type:'object', fields }
      missing required sub-field)
  These lock the behaviour change in place — any future regression
  in the delegation shows up here.

Verification:
- pnpm vitest run tools/workflow → 15/15 green (12 existing + 3 new,
  ~273s). The fixed broken-relation test continues to emit 'error'
  severity because resolveTarget returns an empty-map content for
  collection targets with missing locale files, so the checker treats
  any ref as absent (legacy parity).
- pnpm vitest run conformance serialization-parity core/validator
  providers/github server/http → 65/65 green.
- pnpm typecheck → all 6 packages clean.
- oxlint on new + modified code → 0 warnings.

Studio handoff note:
- .internal/refactor/02-studio-handoff.md phase S3 can now replace
  Studio's server/utils/content-validation.ts with:
    import { validateContent, checkRelationIntegrity, ValidationContext }
      from '@contentrain/mcp/core/validator'
  …and get the identical rule set (plus MCP's project-wide
  orchestration if they later adopt validateProject).

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

refactor(mcp): phase 4.2 — validateProject delegates per-entry checks to validateContent
…handlers

Completes the provider-abstraction plumbing. createServer now accepts a
provider option alongside projectRoot; tool register functions take the
provider as a first-class parameter; write-path handlers gate on
projectRoot presence and return a uniform capability error when local
disk access is unavailable. Read-only static tools (describe_format)
now work against any ToolProvider — the foundation for phase 5.4's
remote write path.

Changes:

Read helpers (core/)
- readConfig / readVocabulary (core/config.ts) and readModel / listModels
  (core/model-manager.ts) add RepoReader-based overloads alongside the
  legacy projectRoot signature. Backward-compatible: every existing
  caller keeps working; GitHubProvider reads go through the same
  helpers via reader-based overload.

Server plumbing (server.ts)
- ToolProvider type alias (RepoReader + capabilities) — narrower than
  the full RepoProvider so LocalProvider (reader + applyPlan today) and
  GitHubProvider (full provider) both satisfy it without requiring
  LocalProvider to stub branch-ops yet.
- createServer({ provider, projectRoot? }) overload. String argument
  continues to work via a backward-compat path that wraps projectRoot
  in a LocalProvider. When only a provider is supplied and the provider
  is a LocalProvider, projectRoot is derived from it; otherwise stays
  undefined.
- Every registerXxxTools call receives (server, provider, projectRoot).

Tool handlers (tools/)
- guards.ts: new capabilityError helper emits a uniform JSON response
  with capability_required, a hint, and isError:true so agents can
  decide whether to retry against another transport.
- content, model, context, workflow, normalize, setup, bulk — signature
  updated to accept ToolProvider + optional projectRoot. Each handler
  adds a one-line gate at the top: if projectRoot is undefined, return
  capabilityError. contentrain_describe_format is the exception — it
  is static data and therefore works against any provider.
- Normalize surfaces the right capability key: contentrain_scan →
  astScan, contentrain_apply → sourceRead (extract) or sourceWrite
  (reuse). contentrain_bulk, contentrain_content_*, model_*, setup,
  workflow → localWorktree.

HTTP transport (server/http/)
- startHttpMcpServerWith({ provider, projectRoot? }) — new variant that
  accepts a pre-built provider. Internally shares the same
  StreamableHTTPServerTransport wiring as the legacy
  startHttpMcpServer({ projectRoot }) path. Node http server,
  auth-token guard, mount-path enforcement identical to phase 5.2.
- Both entry points now go through a single startHttpMcpServerInternal
  helper so the Bearer / mount / listen / close semantics stay in one
  place.

Tests
- tests/server/http.test.ts adds two Phase 5.3 cases:
  * describe_format over HTTP with a provider-only config (no
    projectRoot) returns the format spec.
  * contentrain_status over HTTP with a provider-only config returns
    capability_required: localWorktree with isError:true.
- Existing 4 HTTP tests, 25 conformance / parity / validator / github
  tests, and 40 tool tests (content 17, model 8, workflow 15) all stay
  green — the stdio path is bit-for-bit unchanged.

Verification
- pnpm vitest run conformance serialization-parity core/validator
  providers/github server/http → 65/65 (plus 2 new Phase 5.3 cases in
  server/http = 6/6 there).
- pnpm vitest run tools/content.test tools/model.test tools/workflow.test
  → 40/40 (~610s).
- pnpm typecheck → all 6 packages clean.
- oxlint on mcp src/ → 0 warnings.

What phase 5.3 does NOT ship (staged for phase 5.4)
- Write path via GitHubProvider. LocalProvider.applyPlan still runs the
  full worktree + workflow flow; GitHubProvider.applyPlan is available
  but not yet routed from tool handlers. Phase 5.4 picks off
  content_save / content_delete / model_save / model_delete and runs
  them against a GitHubProvider once the workflow semantics are
  adapted (PR fallback, no selective sync, base branch resolution).
- validateProject reader overload. Project-wide validation still reads
  via projectRoot, so post-save validation runs only on LocalProvider.
  A reader-backed validateProject lands alongside the write path in
  phase 5.4.

Refs:
- .internal/refactor/01-mcp-engine-plan.md phase 5
- .internal/refactor/00-principles.md §5.3 capability system
- .internal/refactor/02-studio-handoff.md phase S6 (MCP Cloud endpoint)

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

refactor(mcp): phase 5.3 — thread RepoProvider through server + tool handlers
…tent + model CRUD

Opens the content/model CRUD tools to any RepoProvider, not just
LocalProvider. Each write handler now takes a dual path:

- LocalProvider instance → legacy flow (worktree + workflow + selective
  sync + context write-through). Bit-for-bit identical to pre-5.4
  behaviour; every stdio / HTTP+Local test keeps passing.
- Any other ToolProvider (GitHubProvider today, GitLab/Bitbucket later)
  → generic RepoWriter.applyPlan. Context.json is bundled as a
  FileChange inside the plan; commit base defaults to the repo's
  contentrain branch with fallback to config.repository.default_branch;
  workflow action is always `pending-review` (no auto-merge for remote
  provider — Studio/agent orchestrates the merge).

Changes:
- core/context.ts
  buildContextChange(reader, operation, source?) — produces a FileChange
  for .contentrain/context.json using a reader. Entries count is left
  null for remote paths (no cheap directory walk via Git Data API yet);
  the legacy writeContext continues to supply real counts for local
  flows so the stdio conformance surface does not drift.
- providers/local/types.ts
  LocalApplyPlanInput grows optional `author` and `base` fields — now a
  superset of RepoWriter.ApplyPlanInput so LocalProvider cleanly
  satisfies RepoWriter. author / base are accepted for interface parity
  but still pass through to createTransaction which reads author from
  the existing env vars.
- server.ts
  ToolProvider widened to RepoReader & RepoWriter & { capabilities } so
  both LocalProvider and GitHubProvider satisfy the type. Narrower than
  the full RepoProvider (no branch-ops yet).
- tools/content.ts
  content_save, content_delete: unified handler with the LocalProvider
  vs remote split. projectRoot gate dropped; writes work against any
  ToolProvider. Post-save validateProject runs only when projectRoot is
  available (LocalProvider-backed servers). Branch-health gate and
  checkReferences remain LocalProvider-only because they read directly
  from the working tree.
- tools/model.ts
  model_save, model_delete: same dual path. model_save's DX helpers
  (import_snippet, example_file) are emitted only when projectRoot is
  available — they are Local-specific UX and the Studio-backed agent
  does not need them.

Tests (tests/server/http.test.ts):
- New end-to-end case: `commits content_save through a GitHubProvider-
  like remote provider`. Mounts a mocked Octokit against a fresh
  GitHubProvider, drives contentrain_content_save over HTTP, asserts
  that the resulting commit
    (a) lands on a cr/content/blog/... branch,
    (b) reports action: 'pending-review' (no auto-merge),
    (c) carries the expected tree payload — content + meta + context.json
        blobs — with the base SHA resolved from the contentrain branch.
- Existing 6 HTTP tests still pass.

Verification:
- pnpm vitest run conformance serialization-parity core/validator
  providers/github server/http → 67/67 green (6 Phase-0+1+2+3+4 tests,
  16 github, 3 conformance, 20 validator, 22 parity, 7 http —
  including the new E2E).
- pnpm vitest run tools/content.test tools/model.test → 25/25 green
  (~434s, ~7 min). LocalProvider paths for content save / delete and
  model save / delete all behave exactly as they did before phase 5.4.
- pnpm typecheck → all 6 packages clean.
- oxlint on packages/mcp/src + tests/server → 0 warnings.

Phase 5 scope check:
- 5.1 GitHubProvider (reader + applyPlan + branch ops) ✓
- 5.2 HTTP transport ✓
- 5.3 Provider threading through server + tool handlers ✓
- 5.4 Remote write path for content + model CRUD ✓ (this PR)

What phase 5.5 picks up next (intentionally not in this PR):
- bulk, workflow, setup, normalize tools — still projectRoot-only. Each
  has local-disk-heavy logic (AST walks, git operations, dir scaffolding)
  that needs a separate abstraction pass.
- validateProject reader overload. Post-save validation is skipped for
  remote flows today; a reader-backed validateProject would fold the
  post-write check into the remote path too.
- checkReferences reader overload — referenced_model pre-check for
  model_delete on remote providers.
- Entry count via reader — populates stats.entries on the remote
  context.json write-through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contentrain and others added 27 commits April 17, 2026 19:44
Closes the integration gap between the finished MCP refactor and the
consumers that want to embed it (Studio first, but the public guide
extends to any host). Also fixes two bugs surfaced while writing
the handoff.

### Studio handoff (internal)

`.internal/refactor/02-studio-handoff.md` was written at the start
of the refactor (Phase 2) and had drifted on every subsequent phase.
Updated to the finished-refactor state:

- Preconditions now list the actual shipped subpath exports
  (`core/ops`, `core/overlay-reader`, `core/contracts`,
  `providers/local`, etc.), not the Phase-2-era stubs.
- Contracts canonical source is `@contentrain/types`, not
  `@contentrain/mcp/core/contracts` (which re-exports for back-compat).
- S2 (Content Engine Sökümü) code samples updated for today's APIs:
  `buildContextChange(reader, operation)` with `OverlayReader`
  wrapping, `provider.applyPlan({..., base: CONTENTRAIN_BRANCH})`
  invariant, no `.reader()` accessor (didn't exist then, doesn't
  now).
- New "Phase 10 tuzakları" section spelling out the three traps
  Studio should avoid: fork from contentrain, OverlayReader before
  context / validation, `isNotFoundError` is internal.
- S6 (MCP Cloud Endpoint) reframed around `startHttpMcpServerWith`
  rather than a hand-rolled JSON-RPC layer — Studio can reuse the
  MCP package's HTTP primitive and put Bearer / quota / metering in
  a Nitro middleware in front of it.
- Removed the hypothetical "core branch-policy module" placeholder
  (Phase 3.5 that never shipped separately — the helper logic lives
  inline in `providers/local/migration.ts` now).
- Provider selection matrix clarifying that Studio never uses
  LocalProvider, so normalize / scan / apply / init / scaffold /
  submit / merge / validate --fix / bulk are always
  `capability_required` on Studio's Cloud endpoint — not something
  Studio has to filter client-side, the capability gate handles it.

### Public embedding guide (public docs)

New `docs/guides/embedding-mcp.md` — a consumer-agnostic guide that
sits under the existing Providers / HTTP Transport guides. Documents
four construction recipes (stdio+Local, HTTP+Local, HTTP+Remote,
programmatic no-transport), the three primitives every integrator
must understand (`CONTENTRAIN_BRANCH` as fork point, `OverlayReader`
for post-commit consistency, `capability_required` as a structured
error), auth model, and an extension point for custom providers.
Studio is called out as a reference integration alongside CI and
scripted automation. Sidebar + top nav updated.

### P2 — `ApplyPlanInput.base` contract vs implementation

The `@contentrain/types` docstring said "defaults to provider's
content-tracking branch", but both `GitHubProvider` and
`GitLabProvider` defaulted to the repository's default branch
(main / master / trunk). Tests locked in that wrong behaviour. MCP's
own tool-level write path (`commit-plan.ts`) bypassed the issue by
always passing `base: CONTENTRAIN_BRANCH` explicitly, but any
Studio-style direct `provider.applyPlan` call with `base` omitted
would silently fork from `main`.

Both implementations now default to `CONTENTRAIN_BRANCH`, matching
the documented contract and the local-transaction behaviour. Tests
rewritten to assert:

- `github.apply-plan.test.ts > defaults to the contentrain branch
  when no base is provided` — `repos.get` is NOT called, `getRef`
  resolves `heads/contentrain`.
- `gitlab.apply-plan.test.ts > defaults to the contentrain branch
  when input.base is absent` — `Projects.show` is NOT called,
  `startBranch` is `'contentrain'`.

Docstring tightened in `packages/types/src/provider.ts` to leave no
room for a split-brain reinterpretation.

`commit-plan.ts` can now omit `base` entirely (provider default is
correct), but keeps it explicit for readability.

### P2 — `contentrain_status` context field on remote

Remote `contentrain_status` returned `context: null` unconditionally,
even though remote writes commit `.contentrain/context.json` through
the overlay reader. Studio's surface reported "no last operation"
and "stats.entries: null" forever.

`readContext` gains a reader overload in `core/context.ts`.
`tools/context.ts` uses the reader form for remote sessions. The
HTTP E2E now seeds `.contentrain/context.json` and asserts the
`lastOperation` + `stats` surface from the remote provider.

### New public subpath exports

- `@contentrain/mcp/core/ops` — plan helpers + path helpers, exactly
  what an embedder needs to compose custom write paths against any
  `RepoProvider`.
- `@contentrain/mcp/core/overlay-reader` — the `OverlayReader`
  primitive. Required by any integrator writing to a remote
  provider so `buildContextChange` / `validateProject` see
  post-commit state.

Both paths are referenced from the embedding guide and the Studio
handoff; without them, a from-scratch Studio-style integration would
require reaching into `dist/` which is explicitly blocked by
`package.json#exports`.

### Verification

- `pnpm -r typecheck` → 0 errors across 8 packages.
- `oxlint` → 0 warnings on 397 files.
- `pnpm --filter @contentrain/mcp build` → clean; dist contains
  `core/ops/`, `core/overlay-reader.{d.mts,mjs}`, `core/contracts/`,
  `providers/local/`.
- `vitest run tests/core tests/conformance tests/serialization-parity
      tests/git tests/providers tests/server tests/util` →
  440/440 green, 2 skipped.
- `vitest run tests/providers/github tests/providers/gitlab
      tests/server/http.test.ts` → 60/60 green
  (includes the rewritten base-default tests and the augmented
  remote-context status E2E).
- `node -e import('@contentrain/mcp/core/ops')` + overlay-reader
  + contracts + providers/local → all four subpaths resolve.

### Changesets

`.changeset/mcp-phase-11-studio-handoff.md` — `@contentrain/mcp`
minor bump (new subpath exports, two P2 bug fixes). Stacks with the
existing Phase 10 changesets into a single minor release.

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

refactor(mcp,types): phase 10 — review alignment + P1/P2 fixes + docs parity
feat(mcp,docs): studio handoff + embedding guide + two P2 fixes
…y-default auth

Consolidates a four-agent review of `contentrain serve` and the
MCP helpers it consumes. Fixes drift, ships the UI affordances that
make the fixes visible, hardens auth, and eliminates ~300 lines of
duplicated merge/diff logic by delegating to MCP.

### MCP — public helpers + empty-repo init

- New `branchDiff(projectRoot, { branch, base? })` exported from
  `@contentrain/mcp/git/branch-lifecycle`. Defaults `base` to
  `CONTENTRAIN_BRANCH` — the singleton content-tracking branch every
  feature branch forks from. Replaces the CLI's duplicated
  `git.diff([${defaultBranch}...${branch}])` calls, which surfaced
  unrelated historical content changes once contentrain advanced
  past the repo's default branch.
- `tools/setup.ts` `contentrain_init` now handles greenfield
  directories: seeds an `--allow-empty` initial commit when the repo
  has zero commits so `ensureContentBranch` has a base ref to
  anchor on. Previously the CLI `init` command created this commit
  manually while the MCP tool skipped the step — the tool failed
  on an empty directory the CLI handled fine.
- Tests: new `tests/git/branch-lifecycle.test.ts` (3 cases) +
  new setup case (`initialises a greenfield directory with no .git
  and no commits`).

### Serve server — correctness + new routes + auth

- Three duplicated merge-via-worktree implementations
  (`/api/branches/approve`, `/api/normalize/approve`, CLI `diff`)
  replaced with calls to MCP's `mergeBranch()` helper. Runs the
  worktree transaction with selective sync + dirty-file protection.
  Skipped files are cached and surfaced to the UI via a new
  `sync:warning` WebSocket event and a new
  `/api/branches/:name/sync-status` route. Merge conflicts now
  broadcast `branch:merge-conflict` instead of silently succeeding.
- `/api/branches/diff` delegates to the new `branchDiff()` helper
  with CONTENTRAIN_BRANCH as the default base.
- History filter tolerates BOTH legacy `Merge branch 'contentrain/'`
  and current `Merge branch 'cr/'` commit patterns so post-Phase-7
  history doesn't drop merges.
- `.catch(() => {})` error-swallowing at three sites removed.
  Merge conflicts and cleanup failures no longer pretend to
  succeed.
- Normalize plan approve broadcasts `branch:created` on the
  returned `git.branch` metadata (parity with content save route).
- New `/api/capabilities` route — provider type, transport,
  capability manifest, branch health, repo info in one call.
  Dashboard consumes this to render the capability badge.
- New `/api/branches/:name/sync-status` — on-demand sync warning
  fetch for the branch detail page; 1h TTL cache in memory.
- New WebSocket events: `branch:rejected`, `branch:merge-conflict`,
  `sync:warning`.
- Zod input validation on every write route via new
  `serve/schemas.ts`. Catches malformed bodies with structured 400
  errors before they reach the MCP tool layer. Adds `zod` as a
  direct CLI dependency.
- `serve` command — secure-by-default auth. Binding to a
  non-localhost interface now HARD ERRORS when no `--authToken` is
  set. No `--allow-unsafe` opt-out flag (OWASP Secure-by-Default;
  matches Postgres, helm, kubectl port-forward conventions).

### Serve UI — level-ups that make the fixes visible

- `useWatch.ts` — WSEvent union widened for the new event types
  (branch:rejected, branch:merge-conflict, sync:warning, connected).
- `project` store — `capabilities` state + `branchHealthAlarm`
  computed + `fetchCapabilities()` action.
- `AppLayout` — global branch-health banner (warning / blocked
  thresholds from MCP), sync-warning toasts with "View details"
  action deep-linking to the branch detail page, merge-conflict
  toasts with the failure message.
- `DashboardPage` — capability badge (provider type · transport)
  next to the workflow + stack badges.
- `BranchDetailPage` — sync warnings panel listing every file the
  selective sync skipped and why, rendered only when the last
  mergeBranch() call produced warnings.
- `ValidatePage` — issues are now clickable when a `model` is
  present; deep-links to the content list filtered to the issue's
  `locale` / `id` / `slug`. Fallback to the model list for
  aggregate issues (e.g. i18n parity).

### CLI — delegation to MCP helpers

- `commands/diff.ts` — both the per-branch summary line and the
  merge confirmation call `branchDiff()` / `mergeBranch()` from
  MCP. Surfaces `sync.skipped[]` warnings to the user with the
  list of preserved files. Removes the duplicated
  `contentrain` branch + worktree + update-ref + checkout dance
  (~45 lines).
- `commands/doctor.ts` — branch health check delegates to MCP's
  `checkBranchHealth()`. Previously filtered `contentrain/*`
  directly after the Phase 7 naming migration to `cr/*`, so the
  check silently reported "None" no matter how many branches had
  accumulated. The delegation uses MCP's canonical thresholds
  (warn at 50, block at 80).
- `commands/validate.ts` non-interactive path — captures
  `tx.complete()` result and surfaces the branch name + workflow
  action in review mode. Previously this metadata was dropped; the
  user ran `contentrain validate --fix`, saw "fixed 3 issues", and
  had no indication that the fix landed on a `cr/fix/validate/...`
  branch that needed manual review.

### Verification

- `pnpm -r typecheck` → 0 errors across 8 packages.
- `oxlint` monorepo → 0 warnings across 399 files.
- `vue-tsc --noEmit` serve-ui → 0 errors.
- `pnpm --filter @contentrain/mcp build` +
  `pnpm --filter contentrain build:cli-only` → clean.
- MCP fast suite → 443/443 green, 2 skipped.

### Tool surface

No changes. Same 16 MCP tools, same arg schemas, same JSON response
shapes. Stdio + LocalProvider flows behave identically to the
previous release.

### Changeset

`.changeset/phase-13-serve-correctness-levelup.md` — minor bump for
both `@contentrain/mcp` (new `branchDiff` export, empty-repo init
fix) and `contentrain` (new serve capabilities + secure auth + zod
dependency).

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

The Phase 13 commit delegated CLI diff/merge paths to MCP helpers
and hardened serve auth, but three CLI test files locked in the
pre-refactor behavior and failed CI:

- `tests/commands/diff.test.ts` — asserted diffs against `main`
  (default branch) and legacy `contentrain/review/hero` branch
  naming. Rewritten to mock `@contentrain/mcp/git/branch-lifecycle`
  branchDiff + `@contentrain/mcp/git/transaction` mergeBranch, assert
  the delegation, and use `cr/*` branch naming throughout.
- `tests/commands/serve.test.ts` — mocked consola without `.error`,
  so the new "refuse to start on 0.0.0.0 without --authToken" path
  crashed at `consola.error(...)`. Mock now includes `error`; the
  existing `--host 0.0.0.0` test passes `authToken` to exercise the
  success path; a new test asserts the hard-error path.
- `tests/integration/serve.integration.test.ts` — asserted the old
  merge-via-worktree dance (worktree add + update-ref + checkout).
  Rewritten to mock the MCP helpers instead, verify the delegation
  call shapes, and assert `sync:warning` + `branch:merge-conflict`
  WS broadcasts land on the fake client.

CI target: `contentrain` package vitest run → 119/119 green
(previously 111/117 with 6 failures; 8 new assertions added for
the secure-auth + merge-conflict + sync-warning paths).

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

Closes the P2 "MCP entrypoint owns a private provider contract" finding
and folds four CLI gap-fillers into the same PR so the new shared
`openMcpSession` helper ships alongside the boundary work that makes
it safe to commit to.

### `@contentrain/types` — one shared MergeResult

- `MergeResult` gains an optional `sync?: SyncResult` field. Remote
  providers (GitHub, GitLab) omit it; LocalProvider populates it so
  selective-sync bookkeeping survives the trip through the shared
  `RepoProvider.mergeBranch()` boundary.

### `@contentrain/mcp` — provider boundary

- `LocalProvider` now `implements RepoProvider` for real: the seven
  branch-ops methods (`listBranches`, `createBranch`, `deleteBranch`,
  `getBranchDiff`, `mergeBranch`, `isMerged`, `getDefaultBranch`)
  wrap existing simple-git + transaction helpers through a new
  `providers/local/branch-ops.ts` module that mirrors the shape of
  `providers/github/branch-ops.ts`.
- `mergeBranch(branch, into)` asserts `into === CONTENTRAIN_BRANCH` —
  the local flow merges feature branches into the content-tracking
  branch and advances the base branch via `update-ref`, so arbitrary
  merge targets would bypass that invariant.
- `server.ts`: the private `ToolProvider = RepoReader & RepoWriter &
  { capabilities }` alias collapses to `type ToolProvider =
  RepoProvider`. Tool handlers now depend on the shared surface
  directly; the alias stays re-exported so existing `ToolProvider`
  imports do not migrate.
- `providers/local/types.ts`: `LocalSelectiveSyncResult` is removed
  in favour of the shared `SyncResult`. `workflowOverride` types with
  the shared `WorkflowMode` union instead of the duplicated literal.
  Same swap in `git/transaction.ts` so the whole write path speaks
  one union.

### `contentrain` — four new commands + shared MCP client

- `utils/mcp-client.ts` — shared `openMcpSession(projectRoot)` helper
  built on `InMemoryTransport.createLinkedPair()`. Each command
  opens a session, calls one tool, and closes in a `finally` block.
- `contentrain merge <branch>` — scriptable single-branch sibling to
  the interactive `contentrain diff`. Same `mergeBranch()` helper, so
  dirty-file protection + selective-sync warnings are identical.
  `--yes` skips the confirmation prompt for CI/agents.
- `contentrain describe <model>` — wraps `contentrain_describe`.
  Human-readable metadata + fields + stats + import snippet. Flags:
  `--sample`, `--locale`, `--json`.
- `contentrain describe-format` — wraps `contentrain_describe_format`.
  Quick format-spec primer for humans pairing with an agent.
- `contentrain scaffold --template <id>` — wraps
  `contentrain_scaffold`. Interactive template picker when no flag
  is passed; `--locales en,tr,de`, `--no-sample`, `--json`.
- `commands/status.ts`: branch-health thresholds (50/80) now come
  from `checkBranchHealth()` instead of being duplicated inline. The
  JSON output surfaces the full `branch_health` object so CI
  consumers see the same warning/blocked state as text mode.

### Verification

- `pnpm -r typecheck` across types, mcp, cli → 0 errors.
- `oxlint` across mcp/cli/types src + tests → 0 warnings on 349 files.
- `@contentrain/types` vitest → 110/110.
- `contentrain` vitest → 130/130. Includes the 11 new command tests
  (merge, describe, scaffold) and the updated status test against
  the `checkBranchHealth()` mock.
- `@contentrain/mcp` fast suite (`tests/providers tests/git tests/core
  tests/util tests/server tests/serialization-parity tests/conformance`)
  → **450 passed / 2 skipped / 30 files**. Includes the new
  `tests/providers/local/branch-ops.test.ts` (7/7): contract shape,
  prefix-filtered branch listing, create/delete round-trip, diff
  status mapping (added/modified), post-merge `isMerged` flip,
  `mergeBranch` target guard, and config-driven `getDefaultBranch`.

### Tool surface

No changes. Same 16 MCP tools, same arg schemas, same response shapes.
The boundary changes are purely internal — existing consumers
(Studio, CLI, IDE agents) observe identical behaviour.

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

fix(serve,mcp,cli): serve correctness + MCP helper surface + secure-by-default auth
…ands

Phase 14a: MCP boundary hardening + CLI command polish
…w routes, defensive Zod

Second pass on `contentrain serve` after Phase 13's auth + drift fixes.
Tight, surgical — additive routes and events, no behaviour regressions.

### File watcher coverage

- `.contentrain/meta/` now recognised: new `meta:changed` WS event
  fires with `modelId`, optional `entryId`, `locale`. Covers both
  per-model SEO layout (`meta/<model>/<locale>.json`) and per-entry
  SEO layout (`meta/<model>/<entry>/<locale>.json`). Previously
  editing a meta file left the Serve UI rendering stale metadata
  until a full refresh.
- chokidar `'error'` handler was previously unhandled. Now broadcasts
  `file-watch:error` with `message` + ISO `timestamp`. The UI can
  render "watcher down, live updates paused" instead of silently
  degrading (e.g. OS inotify limit on Linux).

### New HTTP routes

- `GET /api/describe-format` — thin wrapper around the
  `contentrain_describe_format` MCP tool. The Serve UI's model
  inspector can render the content-format spec as a reference panel.
- `GET /api/preview/merge?branch=cr/...` — side-effect-free preview
  before approve. Returns `alreadyMerged`, `canFastForward`,
  `conflicts` (best-effort via `git merge-tree`; `null` when unable
  to run), `filesChanged`, `stat`. Complements the approve route,
  which continues to surface runtime conflicts by throwing.

### Defensive Zod parity

- `/api/normalize/plan/reject` now parses an optional `{ reason? }`
  body through a new `NormalizePlanRejectBodySchema`. Empty-body and
  reason-only requests still work (backwards compatible); malformed
  bodies return structured 400. Whole serve write surface now goes
  through one `parseOrThrow()` gate.

### Explicitly out of scope

- `/api/doctor` — no `contentrain_doctor` MCP tool exists; only the
  CLI's 540-line command. Rather than duplicate CLI logic into
  serve, defer until doctor is extracted into a reusable MCP tool.
- `sdk:regenerated` WS event — `contentrain generate` runs outside
  serve's process so the watcher can't observe it. Needs a different
  hook; defer until the design is concrete.

### Verification

- `oxlint` cli src + tests → 0 warnings on 209 files.
- `contentrain` typecheck → 0 errors.
- `contentrain` vitest → **137/137** (was 130 on next-mcp). 7 new
  tests cover `meta:changed` (with / without entryId),
  `file-watch:error` payload, `/api/describe-format` tool call,
  `/api/preview/merge` validation + happy FF path, plan/reject body
  validation + backwards compat.

No MCP changes. No tool surface changes.

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

Phase 14b: serve backend — meta watcher, watcher errors, new routes, defensive Zod
Pulls the 540-line `contentrain doctor` CLI command apart so the same
health report drives three consumers: the CLI, a new
`contentrain_doctor` MCP tool, and the Serve UI's `/api/doctor` route.

### `@contentrain/mcp`

- `@contentrain/mcp/core/doctor` — `runDoctor(projectRoot, { usage? })`
  returns a structured `DoctorReport`:
    { checks: Array<{ name, pass, detail, severity? }>,
      summary: { total, passed, failed, warnings },
      usage?: { unusedKeys, duplicateValues, missingLocaleKeys } }
  Every check now carries an explicit severity (error / warning /
  info) so consumers render independently instead of inferring from
  text. Orphan content + stale SDK client drop to `warning`; missing
  git / config / structure stay at `error`.
- `contentrain_doctor` MCP tool — read-only, gated behind
  `localWorktree`. Arg: `{ usage?: boolean }`. Returns the
  `DoctorReport` verbatim. Advertised alongside describe-format.

### `contentrain`

- CLI `contentrain doctor` collapses to a thin pretty-printer over
  `runDoctor()`. Default interactive output is byte-identical —
  same labels, same icons, same grouped usage blocks. New:
    --json — silent, emits raw report; exits non-zero on failures.
    Interactive mode also now exits non-zero on failure (was always 0).
- `GET /api/doctor?usage=true` wraps the MCP tool for the Serve UI.

### Scope notes

- Doctor is inherently local-filesystem work (Node version, git
  binary, mtime comparisons, orphan walk, source scan), so the MCP
  tool is capability-gated behind `localWorktree` and throws a
  structured capability error over remote providers — matches the
  `contentrain_setup` / `contentrain_scaffold` pattern.
- No behaviour change for existing CLI users beyond the additive
  --json flag + exit-code hardening.

### Verification

- oxlint across mcp+cli src+tests → 0 warnings on 350 files.
- @contentrain/mcp typecheck → 0 errors.
- contentrain typecheck → 0 errors.
- Unit tests (21 new, all pass):
  - tests/core/doctor.test.ts 6/6 — uninitialised, minimal, orphan
    warning, default-omits-usage, usage-adds-3-checks, stale-SDK.
  - tests/tools/doctor.test.ts 4/4 — structured report, usage opt-in,
    capability error over remote provider, tools-list advert.
  - tests/commands/doctor.test.ts (CLI) 7/7 — rewritten to mock
    runDoctor directly. Covers --json, exit codes, usage detail
    rendering, flag forwarding.
  - tests/integration/serve.integration.test.ts 24/24 — new
    /api/doctor cases: default, ?usage=true, ?usage=1.

Tool surface: +1 tool (contentrain_doctor). Everything else
unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the Serve UI to the routes and events added in 14b + 14c so the
new backend capabilities become visible to the user.

### New pages

- /doctor — structured health report from /api/doctor. Four stat
  cards (passed / errors / warnings / summary) mirror ValidatePage.
  Per-check rows with severity icon + badge. Optional usage mode
  expands into three collapsibles (unused keys, duplicate values,
  missing locale keys).
- /format — content-format spec from /api/describe-format, grouped
  by top-level section, each a collapsible Card.

### Extended pages

- BranchDetailPage — new "Merge preview" panel fetched on mount
  from /api/preview/merge. Four render states: already-merged
  (info), fast-forward clean (success), requires three-way
  (warning), conflicts (error + lists conflicting paths). Sits
  above the sync-warning panel so reviewers see the upcoming
  merge before the previous merge's outcome.

### Global shell (AppLayout)

- File-watcher error banner — when chokidar emits error the backend
  broadcasts `file-watch:error`; the layout renders a persistent
  destructive banner with message + Dismiss button.
- `meta:changed` toast — light informational toast for SEO metadata
  edits (no CTA).

### Store + composable

- stores/project.ts: doctor, formatReference, fileWatchError state.
  fetchDoctor, fetchFormatReference, fetchMergePreview actions.
  setFileWatchError / dismissFileWatchError. Types DoctorReport,
  DoctorCheck, DoctorUsage, MergePreview, FileWatchError.
- composables/useWatch.ts: WSEvent union extended with meta:changed
  and file-watch:error. New optional fields entryId, timestamp.

### Dictionary-first (eating our own dog food)

Every new user-facing string is pulled from
dictionary('serve-ui-texts').locale('en').get() — no hardcoded
copy. New keys added via contentrain_content_save (auto-merged,
landed as two content ops in the branch history). Reused existing
keys where applicable: dashboard.run, trust-badge.warnings,
validate.all-checks-passed, validate.errors, dashboard.total,
common.on/off.

### Verification

- vue-tsc --noEmit → 0 errors
- oxlint cli src → 0 warnings on 185 files

No backend changes. Pure UI wiring on top of 14b + 14c.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the CLI ergonomics gap identified in 14b/14c audits. Three
additive flags that make the CLI usable in CI, dev loops, and when
something goes wrong internally.

### --json on diff and generate

- contentrain diff --json — structured pending-branches summary,
  skips the interactive review loop:
    { branches: [{ name, base, filesChanged, insertions, deletions,
                   stat }] }
  Agents and CI can inspect cr/* branches without a TTY.
- contentrain generate --json — emits SDK-generate result
  (generatedFiles, typesCount, dataModulesCount, packageJsonUpdated)
  so pipelines can wire generation into automated refresh flows.
- doctor --json already shipped in 14c; this completes the set for
  the most CI-relevant read commands.

### --watch on validate

- validate --watch: chokidar watcher on .contentrain/content,
  .contentrain/models, config.json. Re-runs validation on change
  with 300ms debounce. Graceful SIGINT teardown.
- Read-only by design — force-disables --fix / --interactive
  because those would spawn a fresh cr/fix/* branch per keystroke.
- --json composes: each run prints one JSON line so
  `validate --watch --json | jq` works.

### --debug + CONTENTRAIN_DEBUG

- Global --debug flag, stripped at the root before citty parses
  subcommands so every command's debug() / debugTimer() calls
  see it. Same effect from CONTENTRAIN_DEBUG=1.
- utils/debug.ts: debug(ctx, msg), debugJson(ctx, label, value),
  debugTimer(ctx, label) → end() that no-ops when off. All output
  → stderr so --json stdout payloads stay clean.
- validate --watch is first consumer; future commands can sprinkle
  where user-facing output isn't enough to diagnose.

### Verification

- oxlint cli src+tests → 0 warnings on 213 files
- contentrain typecheck → 0 errors
- 13 new unit tests pass:
  - tests/utils/debug.test.ts (5): default silent, enableDebug()
    turns on, CONTENTRAIN_DEBUG=1 env var, timer no-op, timer ms.
  - diff.test.ts (+1): --json emits branches + no select().
  - generate.test.ts (+1): --json emits result, suppresses pretty.
  - validate.test.ts (+1): --watch advertised.
- Full command unit suite 38/38.

No backend or tool-surface changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 14c-e: doctor MCP tool + Serve UI integration + cross-cutting CLI flags
… + cr/* branches + parity tests

Closes the two P1 drift findings and installs a drift-detection
mechanism so they don't come back:

1. contentrain_merge missing from public catalog — @contentrain/rules
   MCP_TOOLS listed 15 tools while @contentrain/mcp registers 17
   (merge + doctor). Skills tool reference also jumped submit → bulk
   with no merge section.
2. Legacy contentrain/{operation}/... branch namespace still taught
   by essentials, review/normalize prompts, and multiple skills.
   MCP's buildBranchName() returns cr/... (Phase 7). Agents following
   shipped guidance would look for branches that don't exist.

### @contentrain/mcp

- Public export TOOL_NAMES in ./tools/annotations (frozen, derived
  from TOOL_ANNOTATIONS). Single source of truth.
- New ./tools/annotations subpath export. Build script emits it.

### @contentrain/rules

- MCP_TOOLS extended to 17 (contentrain_merge + contentrain_doctor).
- essentials: doctor row added; feature-branch pattern → cr/...;
  health-threshold language mentions cr/*.
- review-mode prompt: every legacy contentrain/<op>/... → cr/<op>/...
- normalize-mode prompt: branch pattern table rewritten.
- shared/workflow-rules: branch pattern spec rewritten.
- tests/mcp-parity.test.ts (new, 4 tests): MCP_TOOLS ↔ TOOL_NAMES
  match, essentials cite every tool, buildBranchName emits cr/,
  docs contain no legacy branch prefix.
- package.json: @contentrain/mcp workspace devDep for parity test.

### @contentrain/skills

- references/mcp-tools.md: new contentrain_merge (after submit) +
  contentrain_doctor (new Doctor Tools subsection). Submit description
  updated to cr/*.
- references/mcp-pipelines.md + workflow.md: branch spec + examples
  rewritten to cr/*.
- contentrain-normalize SKILL + references (extraction, reuse):
  4 stale contentrain/normalize/* → cr/normalize/*.
- contentrain-translate SKILL: translate branch pattern updated.
- tests/mcp-parity.test.ts (new, 2 tests): references/mcp-tools.md
  has ### <tool> heading for every MCP tool; 7 skill docs contain no
  legacy branch prefix.
- package.json: workspace devDep.

### Monorepo

- tsconfig.json paths: @contentrain/mcp/tools/* alias so TS + vitest
  resolve the new subpath from source during dev.

### Verification

- oxlint rules + skills + mcp/tools → 0 warnings
- tsc --noEmit rules + skills + mcp → 0 errors
- @contentrain/rules vitest → 16/16 (was 12, +4 parity)
- @contentrain/skills vitest → 85/85 (was 83, +2 parity)

Tool surface: no behaviour changes. TOOL_NAMES export is additive;
everything else is documentation + tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every package README cross-checked against src/ exports, package.json
exports map, and (for MCP) TOOL_ANNOTATIONS registry. Every claim in
the rewritten READMEs is verified against current code.

### types
- Provider contracts section (RepoProvider, RepoReader, RepoWriter,
  ProviderCapabilities, Commit, Branch, FileDiff, MergeResult with
  optional sync?: SyncResult, LOCAL_CAPABILITIES).
- NormalizePlan* types, CONTENTRAIN_BRANCH, SECRET_PATTERNS, ModelSummary.
- Browser-compatible validate/serialize surface for Studio integration.

### mcp
- Tool count corrected to 17 (was 13/16 in different sections).
  contentrain_doctor row added to annotations table.
- Subpath export list now lists every entry in package.json:
  /core/doctor, /core/contracts, /core/ops, /core/overlay-reader,
  /tools/annotations.
- mergeBranch description uses cr/* branch naming.
- Capability gates section mentions doctor alongside scan/apply.

### cli (contentrain)
- Global --debug flag + CONTENTRAIN_DEBUG env var.
- Flag matrix: --json on status/doctor/validate/generate/diff/describe/
  scaffold; --watch on validate/generate; --demo and --mcpHttp /
  --authToken on serve.
- setup, skills, merge, describe, describe-format, scaffold commands
  in command table.
- Secure-by-default HTTP transport auth described.

### sdk (@contentrain/query)
- contentrain generate (CLI) is now the recommended entry point;
  contentrain-query generate clarified as programmatic path.
- Added programmatic generate() API snippet.

### rules
- MCP_TOOLS length corrected to 17 (+merge +doctor).
- New Parity section explaining tests/mcp-parity.test.ts.
- shared/ directory catalog added (11 rule files, previously
  undocumented).
- Context bridge section mentions the 4 stack templates.

### skills
- Reference discovery pattern (references/*.md on-demand, tier table
  for progressive disclosure).
- New Parity section mirroring rules package.
- Public Exports includes discovery snippet.

No code changes — READMEs only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every page under docs/ audited by 5 parallel Explore agents against
source (top-level, packages, reference, guides-infra, guides-content)
then applied with VitePress build verification.

### Tool-count corrections (16 → 17)

getting-started.md, concepts.md, packages/mcp.md, packages/cli.md,
guides/embedding-mcp.md, guides/http-transport.md, guides/providers.md,
guides/serve-ui.md — every "16 tools" updated to 17 (contentrain_merge
+ contentrain_doctor).

### Branch-naming corrections (Phase 7 migration)

concepts.md, guides/normalize.md — legacy contentrain/{operation}/...
branch prefixes rewritten to cr/*. MCP emits cr/; docs must not teach
the stale prefix.

### Major rewrites

- packages/mcp.md — full 17-tool table, contentrain_doctor in read
  section, complete subpath-export list (adds /core/doctor,
  /core/contracts, /core/ops, /core/overlay-reader, /tools/annotations).
- packages/cli.md — every command with real flags: --json on read
  commands, --watch on validate+generate, global --debug /
  CONTENTRAIN_DEBUG, new commands (merge, describe, describe-format,
  scaffold, setup, skills). Serve section documents --demo, --mcpHttp,
  secure-by-default bearer token requirement.
- packages/types.md — new Provider Contract Types section
  (RepoProvider, RepoReader, RepoWriter, ProviderCapabilities,
  FileChange, ApplyPlanInput, Commit, Branch, FileDiff, MergeResult
  with sync?, SyncResult) + LOCAL_CAPABILITIES.
- packages/rules.md — MCP_TOOLS.length === 17 + explicit includes for
  contentrain_merge and contentrain_doctor.
- reference/providers.md — complete capability matrix, MergeResult
  shape with sync? for LocalProvider, supporting types, minimum-viable
  custom-provider recipe.
- guides/serve-ui.md — new sections for every Phase 14b/c/d surface:
  /doctor and /format UI pages, merge preview on BranchDetail,
  meta:changed / file-watch:error / sync:warning /
  branch:merge-conflict / branch:rejected WS events, new HTTP routes
  (/api/doctor, /api/describe-format, /api/preview/merge,
  /api/capabilities, /api/branches/:name/sync-status), secure-by-
  default HTTP MCP auth.

### Minor

- packages/sdk.md — contentrain generate (CLI) is now the recommended
  generation entry; @contentrain/query/generate API documented for
  build-tool authors.
- demo.md — code snippet gets explicit `import { singleton } from
  '#contentrain'`.

### Verified

- npx vitepress build → success in 5.33s, no broken links, no
  rendering errors.
- Every claim cross-checked against current source code.

No code changes — docs only.

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

README.md:
- Architecture diagram: MCP (16 tools) → MCP (17 tools)
- Feature bullet: MCP engine 16 tools → 17
- Packages table: @contentrain/mcp row → 17 MCP tools

CLAUDE.md:
- Monorepo tree packages/mcp comment → 17 MCP tools
- npm-packages table → 17 MCP tools
- Obsolete "Octokit YOK in MCP" decision rewritten: @octokit/rest and
  @gitbeaker/rest are optional peer dependencies (Phase 5.1 + 8)

AGENTS.md:
- Essentials bullet 16 → 17 MCP tools
- Packages table mcp row → 17 MCP tools

RELEASING.md, CONTRIBUTING.md, CLA.md, CODE_OF_CONDUCT.md — no changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 pending changesets collectively produce this release, verified via
pnpm release:status:

  @contentrain/mcp     → 1.3.0 (minor)
  @contentrain/types   → 0.5.0 (minor)
  contentrain          → 0.5.0 (minor)
  @contentrain/rules   → 0.4.0 (minor)
  @contentrain/skills  → 0.4.0 (minor)
  @contentrain/query   → 5.1.5 (patch)

### Studio handoff pre-flight — satisfied

- @contentrain/types ≥ 0.5.0 ✓ (handoff pre-req was ≥ 0.4.2)
- @contentrain/mcp ≥ 1.3.0 ✓

### Pre-release verification

- pnpm release:check → passed
- pnpm release:status → 14 changesets, 5 minor + 1 patch
- pnpm -r typecheck → 0 errors across 8 packages
- pnpm lint → 0 warnings on 419 files
- vitepress build → success

### Automated release flow (post-merge)

1. R1-R4 PRs merge into next-mcp
2. next-mcp merges into main
3. Changesets action opens a Version Packages PR
4. Merging that PR publishes to npm + per-package tags

No manual pnpm release required.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase R1: align rules/skills with MCP surface + cr/* branches + parity tests
Phase R2: align every package README with current public surface
Phase R3: align production docs/ site with current codebase
…ng-claude

Phase R3b: align root README / CLAUDE / AGENTS with current codebase
Phase R4: release manifest + pre-flight verification
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 18, 2026

Deploy Preview for contentrain-ai ready!

Name Link
🔨 Latest commit 590bcfc
🔍 Latest deploy log https://app.netlify.com/projects/contentrain-ai/deploys/69e34944badd3600089a8a77
😎 Deploy Preview https://deploy-preview-47--contentrain-ai.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@ABB65 ABB65 merged commit 42106d0 into main Apr 18, 2026
5 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Apr 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant