Skip to content

refactor(mcp,types): phase 10 — review alignment + P1/P2 fixes + docs parity#36

Merged
ABB65 merged 1 commit into
next-mcpfrom
refactor/phase-10-review-alignment
Apr 17, 2026
Merged

refactor(mcp,types): phase 10 — review alignment + P1/P2 fixes + docs parity#36
ABB65 merged 1 commit into
next-mcpfrom
refactor/phase-10-review-alignment

Conversation

@ABB65

@ABB65 ABB65 commented Apr 17, 2026

Copy link
Copy Markdown
Member

Summary

One PR that closes every gap a four-agent review surfaced after phases 0–9 landed. Scope is broad but coherent: each piece unblocks another (the overlay reader enables both P1 fixes; the read-only tool relaxation assumes the overlay; the docs describe the now-correct behaviour).

What's in the box

Contracts moved to @contentrain/types (minor)

RepoReader / RepoWriter / RepoProvider / ProviderCapabilities / FileChange / Branch / Commit / FileDiff / MergeResult / ApplyPlanInput and LOCAL_CAPABILITIES now live in @contentrain/types/provider and are re-exported from the types package root. Third-party RepoProvider implementations no longer need a runtime dependency on @contentrain/mcp. ContentrainConfig.repository.provider widens from 'github' to 'github' | 'gitlab'. @contentrain/mcp/core/contracts keeps re-exporting everything for backward compat.

P1 — remote write base branch invariant

commit-plan.ts:commitThroughProvider now always forks remote feature branches from CONTENTRAIN_BRANCH, matching the local transaction flow. Previously used config.repository.default_branch ?? 'contentrain' → projects with default_branch: 'main' silently broke the contentrain singleton invariant.

P1 — stale remote context.json + validation

New core/overlay-reader.ts:OverlayReader layers pending FileChanges on top of the base reader. buildContextChange and post-save validateProject now see the post-commit state, not the pre-change base branch. Stats in committed context.json and validation envelopes reflect the new commit.

P2 — read-only tools on remote providers

contentrain_status, contentrain_describe, contentrain_content_list drop the !projectRoot gate and route through provider. Local-only features (stack detection, branch health, content_list --resolve) stay local and degrade gracefully. The MCP server's documented "read-only tools work over HTTP" claim is now true.

P2 — broken export paths fixed

@contentrain/mcp/core/contracts and @contentrain/mcp/providers/local are now in package.json exports. External imports previously failed with ERR_PACKAGE_PATH_NOT_EXPORTED.

Cohesion polish

  • tools/commit-plan.ts collapses the 4× if (provider instanceof LocalProvider) { … } else { … } applyPlan dispatch into one shared helper with a uniform return shape.
  • providers/shared/{errors,paths}.ts consolidate isNotFoundError + normaliseContentRoot/resolveRepoPath previously duplicated across GitHub + GitLab. GitLab's lenient description-match fallback is gone — strict status-based only (removes silent-suppression risk).
  • tools/workflow.tssubmit + merge gate on explicit provider.capabilities.X instead of the !projectRoot proxy, matching normalize.ts.

Docs parity

Tool count drift (15 → 16) fixed in: root README.md (×3), CLAUDE.md (×2), docs/packages/mcp.md frontmatter, docs/concepts.md. Tool listings now include contentrain_merge.

"Local-first only / no GitHub API" framing rewritten in packages/mcp/README.md (×2), docs/packages/mcp.md (×2). New framing: "provider-agnostic, local-first by default, optional GitHub + GitLab backends".

New VitePress pages (sidebar + top nav updated):

  • docs/guides/providers.md — Local / GitHub / GitLab overview + capability matrix
  • docs/guides/http-transport.md — HTTP deployment, Bearer auth, Studio / CI / remote-agent patterns
  • docs/reference/providers.md — RepoProvider contract reference with a custom-provider example

Updated pages: getting-started (HTTP tip), concepts (provider + capability concepts), studio (content engine described as MCP over HTTP + RepoProvider), guides/normalize (LocalProvider-required warning), reference/config (widened provider type).

CLI: packages/cli/src/commands/serve.ts description updated. packages/cli/README.md gains a "MCP HTTP (Studio, CI, remote drivers)" subsection and migrates contentrain/* references to cr/*.

Rules + skills: packages/rules/shared/mcp-usage.md gets a Transport and Provider Capabilities section + capability-matrix table + capability_required error entry + contentrain_merge in the catalogue + cr/* branch naming; workflow-rules.md and normalize-rules.md branch examples migrated. Skills: contentrain-normalize/SKILL.md adds a Transport Requirements subsection; translate + normalize workflows migrated to cr/*.

No tool-surface changes

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

Changesets

  • .changeset/types-provider-alignment.md@contentrain/types minor
  • .changeset/mcp-phase-10-alignment.md@contentrain/mcp minor

Test plan

  • pnpm -r typecheck (8 packages) → 0 errors
  • npx oxlint (monorepo, 397 files) → 0 warnings
  • pnpm --filter @contentrain/mcp build → clean. Dist contains dist/core/contracts/, dist/providers/local/, dist/providers/github/, dist/providers/gitlab/.
  • npm pack --dry-run → 130 files, all expected subpaths present.
  • Export surface verified: node -e "import('@contentrain/mcp/core/contracts')" and node -e "import('@contentrain/mcp/providers/local')" both resolve.
  • Fast suite (tests/core tests/conformance tests/serialization-parity tests/git tests/providers tests/server tests/util) → 440/440 green, 2 skipped. Includes 11 new LocalReader tests + 11 new OverlayReader tests + 5 new HTTP E2Es.
  • Tool suite (tests/tools) → 90/91 on parallel; the one flaky case (content.test.ts > blocks new writes when 80 active contentrain branches exist) is a pre-existing parallel-load timeout, not a regression. Re-run in isolation: 17/17 green in 411s. No new tool-surface code is exercised by that test.

Scope notes

  • providers/shared is intentionally not a public export — it is implementation-detail shared between the two remote providers.
  • Relation hydration in content_list --resolve still needs local disk. The reader path rejects it cleanly rather than silently returning partial results; a future PR can lift this if / when relation resolution gets a reader overload.
  • Bitbucket provider remains on the roadmap (README still says "coming soon").

🤖 Generated with Claude Code

…docs parity

Single PR that closes the gaps a four-agent review surfaced after
phases 0-9 landed. "Hiçbir şeyi atlamadan" — every finding is
addressed in this change, from cohesion polish to the docs drift the
refactor left in its wake.

### Provider contracts move to @contentrain/types

- packages/types/src/provider.ts (new) — RepoReader, RepoWriter,
  RepoProvider, ProviderCapabilities, LOCAL_CAPABILITIES, FileChange,
  Branch, Commit, CommitAuthor, FileDiff, MergeResult, ApplyPlanInput.
  Third-party tools can now implement a custom RepoProvider without
  a runtime dependency on @contentrain/mcp.
- packages/types/src/index.ts — repository.provider widened from
  `'github'` to `'github' | 'gitlab'`; all provider contracts
  re-exported from the root.
- packages/mcp/src/core/contracts/index.ts — now a thin barrel that
  re-exports from @contentrain/types. The per-file contract modules
  (branch.ts / capabilities.ts / file-change.ts / provider.ts /
  repo-reader.ts / repo-writer.ts) are deleted; existing MCP imports
  through `core/contracts` continue to work unchanged.

### P1 — remote write base branch invariant

- packages/mcp/src/tools/commit-plan.ts (new) extracts the
  LocalProvider vs remote RepoProvider dispatch from four repeated
  blocks in content.ts / model.ts into `commitThroughProvider`.
- Remote writes now always fork from CONTENTRAIN_BRANCH, matching the
  local transaction flow. Previously they used
  `config.repository.default_branch ?? 'contentrain'` — in any
  project that set `repository.default_branch: 'main'`, feature
  branches were silently forked from `main`, breaking the
  `contentrain` singleton invariant.

### P1 — stale remote context.json + validation

- packages/mcp/src/core/overlay-reader.ts (new) — OverlayReader wraps
  a RepoReader and layers pending FileChanges on top. Semantics:
  pending adds → visible; pending deletes → missing; base falls
  through for everything else. listDirectory merges additions into
  the base listing, removes deletes, and surfaces nested
  subdirectories for deeper pending paths.
- commit-plan.ts: `buildContextChange` is now called against an
  OverlayReader over the pending plan, so `stats.entries` /
  `stats.models` reflect the state the new commit produces instead
  of the pre-change base branch.
- tools/content.ts: post-save validateProject runs against the same
  overlay on remote, so the validation envelope matches the new
  content state. Local flow still uses projectRoot-based validation
  — the transaction layer has already written the worktree.

### P2 — read-only tools work over remote providers

- tools/context.ts (`contentrain_status`, `contentrain_describe`)
  and tools/content.ts (`contentrain_content_list`) no longer gate
  on `!projectRoot`. They route through `provider` and degrade
  gracefully when no project root is available: stack detection,
  branch health, and `content_list --resolve` stay local-only and
  are skipped / rejected with a descriptive error on remote.
- core/content-manager.ts: listContent gains a reader overload
  (`listContentViaReader`) that handles all four kinds (singleton,
  collection, document, dictionary). Relation hydration
  (`opts.resolve`) still requires local filesystem access because
  the walk touches other models.

### P2 — public export surface unbroken

- packages/mcp/package.json: adds `./core/contracts` and
  `./providers/local` to `exports` and the build targets. Both paths
  were referenced in package docstrings since phase 5 but were never
  publishable — external imports failed with
  `ERR_PACKAGE_PATH_NOT_EXPORTED`. Now they resolve.

### Cohesion — provider cleanup

- packages/mcp/src/providers/shared/ (new) consolidates helpers that
  were duplicated across GitHub + GitLab:
  - `errors.ts:isNotFoundError` — unified strict-status-based check.
    Removes four per-provider `isNotFound` helpers and GitLab's
    lenient description-match fallback that risked masking non-404
    failures silently.
  - `paths.ts:normaliseContentRoot + resolveRepoPath` — one copy
    instead of two identical files under github/ and gitlab/.
- tools/workflow.ts: `contentrain_submit` and `contentrain_merge`
  now gate on explicit `provider.capabilities.X` (pushRemote /
  localWorktree) instead of the `!projectRoot` proxy. Matches the
  pattern `tools/normalize.ts` adopted in phase 6.

### Tool surface

No changes. Same 16 tools, same parameters, same response JSON.

### Docs

Tool-count drift (15 → 16) fixed in root README, CLAUDE.md,
docs/packages/mcp.md frontmatter, docs/concepts.md. Tool lists
updated to include `contentrain_merge`.

"Local-first only / no GitHub API" framing rewritten in four spots
(packages/mcp/README.md opening + design constraints,
docs/packages/mcp.md frontmatter + body section). New framing:
"provider-agnostic, local-first by default, optional GitHub and
GitLab backends".

New VitePress pages (sidebar + nav updated):
- docs/guides/providers.md — Local / GitHub / GitLab overview and
  capability matrix
- docs/guides/http-transport.md — HTTP deployment, Bearer auth,
  Studio / CI / remote-agent patterns
- docs/reference/providers.md — RepoProvider contract reference

Existing pages updated: getting-started (HTTP transport tip),
concepts (provider concept + capability gates), studio (content
engine is MCP over HTTP + RepoProvider), normalize (LocalProvider
warning), config reference (widened provider type).

CLI:
- packages/cli/src/commands/serve.ts description now says
  "stdio + HTTP"
- packages/cli/README.md adds a "MCP HTTP (Studio, CI, remote
  drivers)" subsection under `serve` modes and updates branch
  references from `contentrain/*` to `cr/*`.

Rules and skills:
- packages/rules/shared/mcp-usage.md — new "Transport and Provider
  Capabilities" section with the capability matrix;
  `contentrain_merge` added to the tool catalogue; branch naming
  corrected to `cr/*`; `capability_required` entry added to the
  common-errors table.
- packages/rules/shared/{workflow,normalize}-rules.md — branch
  examples migrated to `cr/*`.
- packages/skills/skills/contentrain-normalize/SKILL.md — new
  "Transport Requirements" section documenting the LocalProvider
  constraint.
- packages/skills/workflows/{contentrain-normalize,translate}.md —
  branch references migrated to `cr/*`.

### Tests

- packages/mcp/tests/providers/local/reader.test.ts (new, 11 cases)
  — LocalReader was previously untested at the unit level.
- packages/mcp/tests/core/overlay-reader.test.ts (new, 11 cases)
  — exercises the primitive behind both P1 fixes.
- packages/mcp/tests/server/fixtures/github-mock.ts (new) — shared
  mock for GitHubProvider HTTP E2Es; previously inlined in each
  case.
- packages/mcp/tests/server/http.test.ts — five new E2Es
  (content_delete, model_save, model_delete, validate read-only,
  status read-only) plus an updated capability-error test that
  exercises `contentrain_submit` (genuinely local-only) instead of
  the now-remote-capable `contentrain_status`.

### Changesets

- `.changeset/types-provider-alignment.md` — @contentrain/types minor
  (provider widened + contracts exposed).
- `.changeset/mcp-phase-10-alignment.md` — @contentrain/mcp minor
  (new subpath exports + P1 bug fixes + read-only-over-remote
  feature additions).

### Verification

- pnpm -r typecheck (8 packages) → 0 errors
- npx oxlint (monorepo, 397 files) → 0 warnings
- pnpm --filter @contentrain/mcp build → clean, 130 files packed
- vitest run tests/core tests/conformance tests/serialization-parity
      tests/git tests/providers tests/server tests/util
  → 440/440 green, 2 skipped (22 new tests land in this pass: 11
  LocalReader + 11 OverlayReader, plus 5 new HTTP E2Es replacing
  one rewritten case).
- vitest run tests/tools → 90/91 on parallel; 1 pre-existing flaky
  timeout on `content.test.ts > blocks new writes when 80 active
  contentrain branches exist` (80 sequential git checkouts inside a
  120s budget, unrelated to phase 10). Re-run in isolation: 17/17
  green in 411s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ABB65 ABB65 merged commit fad1371 into next-mcp Apr 17, 2026
1 check 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