Skip to content

Phase 14a: MCP boundary hardening + CLI command polish#39

Merged
ABB65 merged 1 commit into
next-mcpfrom
fix/mcp-boundary-and-cli-commands
Apr 17, 2026
Merged

Phase 14a: MCP boundary hardening + CLI command polish#39
ABB65 merged 1 commit into
next-mcpfrom
fix/mcp-boundary-and-cli-commands

Conversation

@ABB65

@ABB65 ABB65 commented Apr 17, 2026

Copy link
Copy Markdown
Member

Summary

  • MCP boundaryLocalProvider now implements the full RepoProvider (7 branch-ops methods). ToolProvider collapses to RepoProvider. LocalSelectiveSyncResult + 'review'|'auto-merge' literals replaced by shared SyncResult + WorkflowMode from @contentrain/types. MergeResult gains optional sync? field so selective-sync bookkeeping survives the shared contract.
  • CLI — new utils/mcp-client.ts in-memory session helper; new merge, describe, describe-format, scaffold commands; status now delegates to checkBranchHealth() for shared 50/80 thresholds.
  • TypesMergeResult.sync extension; no breaking changes.

Details

@contentrain/mcp

  • New providers/local/branch-ops.ts mirrors the shape of providers/github/branch-ops.ts. LocalProvider wraps these + existing transaction helpers.
  • mergeBranch(branch, into) asserts into === CONTENTRAIN_BRANCH — the local flow advances the base branch via update-ref in transaction.mergeBranch, so arbitrary merge targets would bypass that invariant.
  • server.ts ToolProvider kept as a re-export of RepoProvider — existing imports do not migrate.
  • LocalSelectiveSyncResult removed in favour of shared SyncResult; workflowOverride typed with WorkflowMode in transaction.ts + providers/local/types.ts.

contentrain

  • utils/mcp-client.tsopenMcpSession(projectRoot) built on InMemoryTransport.createLinkedPair(). Each new command opens, calls one tool, closes in finally.
  • contentrain merge <branch> — scriptable sibling to interactive contentrain diff. Same mergeBranch() helper, so dirty-file protection + sync warnings are identical. --yes skips confirm.
  • contentrain describe <model> — wraps contentrain_describe. Flags: --sample, --locale, --json.
  • contentrain describe-format — wraps contentrain_describe_format.
  • contentrain scaffold --template <id> — wraps contentrain_scaffold. Interactive picker when no flag; --locales, --no-sample, --json.
  • commands/status.ts — branch-health thresholds now come from checkBranchHealth() instead of duplicated inline. JSON output surfaces branch_health for CI.

Tool surface

No changes. Same 16 MCP tools, same arg schemas, same response shapes. Internal refactor only.

Test plan

  • pnpm -r typecheck (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 11 new command tests + updated status health test)
  • @contentrain/mcp fast suite (providers/git/core/util/server/serialization-parity/conformance) → 450 passed / 2 skipped / 30 files
  • New tests/providers/local/branch-ops.test.ts (7/7): contract shape, prefix listing, create/delete round-trip, diff status mapping, post-merge isMerged flip, mergeBranch target guard, config-driven getDefaultBranch

🤖 Generated with Claude Code

… 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>
@ABB65 ABB65 merged commit 70459c3 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