From c4bccd1b2016ea075d87fef0e9b693f1bd5af08c Mon Sep 17 00:00:00 2001 From: AHMET BAYHAN BAYRAMOGLU <49499275+ABB65@users.noreply.github.com> Date: Mon, 13 Jul 2026 15:35:19 +0300 Subject: [PATCH] fix(cli): restore Serve UI labels, unify sidebar i18n, fix capabilities transport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Serve UI is the one layer untouched since March; bring it in line with July. - Regenerate the committed #contentrain client: it had drifted 40 keys behind the serve-ui-texts source dictionary (blank Doctor/Format nav labels + entire Doctor page copy + branch-detail preview + Format page), and also re-syncs the docs/marketing content the same client mirrors via content_path (now current with this session's docs edits — 24-tool / media surface). - Unify PrimarySidebar: all 8 nav items now use one primary-sidebar.* namespace (was 6 hardcoded + 2 via a primary-nav.* namespace missing from the built bundle). Source dictionary keys moved accordingly. - /api/capabilities: transport 'stdio' -> 'http' — the web dashboard is reached over HTTP; the MCP engine is embedded in-process. 'stdio' only applies to 'serve --stdio', which does not serve this UI. Coverage checked against product philosophy and deliberately left as-is: read-only dashboard with agent-delegated mutations, and media staying Studio-only (LocalProvider has no media facet, so no media UI). No stale tool-count/feature copy found in the dictionary. serve-ui build (vue-tsc + vite) passes; oxlint clean. --- .changeset/serve-ui-july-alignment.md | 12 + .../client/data/docs-guide--concepts.mjs | 8 + .contentrain/client/data/docs-guide--demo.mjs | 8 + .../client/data/docs-guide--ecosystem.mjs | 8 + .../data/docs-guide--getting-started.mjs | 8 + .../client/data/docs-guide--index.mjs | 31 +++ .../client/data/docs-guide--studio.mjs | 8 + .../data/docs-guides--embedding-mcp.mjs | 6 + .../client/data/docs-guides--frameworks.mjs | 7 + .../data/docs-guides--http-transport.mjs | 6 + .../client/data/docs-guides--i18n.mjs | 7 + .../client/data/docs-guides--normalize.mjs | 7 + .../client/data/docs-guides--providers.mjs | 6 + .../client/data/docs-guides--serve-ui.mjs | 7 + .../client/data/docs-packages--cli.mjs | 7 + .../client/data/docs-packages--mcp.mjs | 7 + .../client/data/docs-packages--rules.mjs | 7 + .../client/data/docs-packages--sdk.mjs | 7 + .../client/data/docs-packages--types.mjs | 7 + .../client/data/docs-reference--config.mjs | 7 + .../data/docs-reference--field-types.mjs | 7 + .../data/docs-reference--model-kinds.mjs | 7 + .../client/data/docs-reference--providers.mjs | 6 + .contentrain/client/data/integrations.en.mjs | 215 ++++++++++++++++++ .contentrain/client/data/integrations.tr.mjs | 215 ++++++++++++++++++ .../client/data/serve-ui-texts.en.mjs | 40 ++++ .contentrain/client/data/showcase.en.mjs | 35 --- .contentrain/client/index.cjs | 19 +- .contentrain/client/index.d.ts | 78 ++++++- .contentrain/client/index.mjs | 118 ++++++++-- .../content/serve-ui/serve-ui-texts/en.json | 4 +- .../src/components/layout/PrimarySidebar.vue | 16 +- packages/cli/src/serve/server.ts | 5 +- 33 files changed, 861 insertions(+), 75 deletions(-) create mode 100644 .changeset/serve-ui-july-alignment.md create mode 100644 .contentrain/client/data/docs-guide--concepts.mjs create mode 100644 .contentrain/client/data/docs-guide--demo.mjs create mode 100644 .contentrain/client/data/docs-guide--ecosystem.mjs create mode 100644 .contentrain/client/data/docs-guide--getting-started.mjs create mode 100644 .contentrain/client/data/docs-guide--index.mjs create mode 100644 .contentrain/client/data/docs-guide--studio.mjs create mode 100644 .contentrain/client/data/docs-guides--embedding-mcp.mjs create mode 100644 .contentrain/client/data/docs-guides--frameworks.mjs create mode 100644 .contentrain/client/data/docs-guides--http-transport.mjs create mode 100644 .contentrain/client/data/docs-guides--i18n.mjs create mode 100644 .contentrain/client/data/docs-guides--normalize.mjs create mode 100644 .contentrain/client/data/docs-guides--providers.mjs create mode 100644 .contentrain/client/data/docs-guides--serve-ui.mjs create mode 100644 .contentrain/client/data/docs-packages--cli.mjs create mode 100644 .contentrain/client/data/docs-packages--mcp.mjs create mode 100644 .contentrain/client/data/docs-packages--rules.mjs create mode 100644 .contentrain/client/data/docs-packages--sdk.mjs create mode 100644 .contentrain/client/data/docs-packages--types.mjs create mode 100644 .contentrain/client/data/docs-reference--config.mjs create mode 100644 .contentrain/client/data/docs-reference--field-types.mjs create mode 100644 .contentrain/client/data/docs-reference--model-kinds.mjs create mode 100644 .contentrain/client/data/docs-reference--providers.mjs create mode 100644 .contentrain/client/data/integrations.en.mjs create mode 100644 .contentrain/client/data/integrations.tr.mjs diff --git a/.changeset/serve-ui-july-alignment.md b/.changeset/serve-ui-july-alignment.md new file mode 100644 index 0000000..59982d0 --- /dev/null +++ b/.changeset/serve-ui-july-alignment.md @@ -0,0 +1,12 @@ +--- +"contentrain": patch +--- + +fix(cli): restore blank Serve UI labels and correct the capabilities transport badge + +The Serve UI's Doctor and Format sidebar labels rendered blank. Two causes, both fixed: + +- **Stale generated client.** The committed `#contentrain` client the UI bundles was last generated in March and had drifted 40 keys behind the source `serve-ui-texts` dictionary — including the two nav labels, the entire Doctor page copy (21 keys), the branch-detail merge-preview strings (7), and the Format page (4). Regenerating brings the client current (and also syncs the docs/marketing content the same client mirrors via `content_path`). +- **Half-migrated sidebar.** `PrimarySidebar` used a `primary-nav.*` namespace for only Doctor/Format while the other six items were hardcoded. All eight nav items now use the single, complete `primary-sidebar.*` namespace, matching `MobileNav`'s dictionary-driven approach. + +Also: `/api/capabilities` reported `transport: "stdio"` in Web-UI mode, where the dashboard is actually reached over HTTP (the MCP engine is embedded in-process). The badge now reads `local · http`. `stdio` remains correct only for `serve --stdio`, which does not serve this UI. diff --git a/.contentrain/client/data/docs-guide--concepts.mjs b/.contentrain/client/data/docs-guide--concepts.mjs new file mode 100644 index 0000000..f8169a4 --- /dev/null +++ b/.contentrain/client/data/docs-guide--concepts.mjs @@ -0,0 +1,8 @@ +export default { + "body": "# Core Concepts\n\n## The Big Idea\n\n**Governance, not generation.**\n\nContentrain is not an AI content generator — there are dozens of those. Contentrain is the infrastructure that governs what happens _after_ AI generates content: validation, structure, review, approval, and delivery.\n\nMost content tools ask you to learn their system — their dashboard, their API, their markup syntax. Contentrain inverts this: **your AI agent already understands your codebase**, so let it manage your content. Contentrain provides the deterministic infrastructure (MCP tools, validation, Git transactions) while the agent provides the intelligence (what to extract, how to structure, where to replace).\n\n> Agent generates. Human approves. System standardizes.\n\nThe result is a content pipeline with full auditability:\n\n```\nAgent generates → MCP validates → Human reviews → Git commits → Content delivered\n```\n\nEvery step is a git diff. Nothing reaches production without your approval.\n\n## The Agent-Driven Content Model\n\nContentrain AI inverts the traditional CMS workflow:\n\n| Traditional CMS | Contentrain AI |\n|---|---|\n| Human opens dashboard | Human talks to AI agent |\n| Human creates schema manually | Agent creates models via MCP |\n| Human types content in forms | Agent writes content to Git |\n| Content stored in database | Content stored as files in `.contentrain/` |\n| API calls to read content | Plain JSON files — read from any language, optional typed SDK |\n| Deploys need API availability | Static files — works offline, zero runtime deps, any platform |\n\n## Three Layers\n\n### 1. MCP (Infrastructure)\n\n24 tools (19 core + 5 media) that AI agents call to manage content:\n\n- **Read:** `contentrain_status`, `contentrain_describe`, `contentrain_describe_format`, `contentrain_doctor`, `contentrain_content_list`\n- **Project setup:** `contentrain_init`, `contentrain_scaffold`\n- **Content and schema writes:** `contentrain_model_save`, `contentrain_model_delete`, `contentrain_content_save`, `contentrain_content_delete`\n- **Normalize:** `contentrain_scan`, `contentrain_apply`\n- **Workflow and operations:** `contentrain_validate`, `contentrain_submit`, `contentrain_merge`, `contentrain_bulk`\n\nMCP is **deterministic infrastructure** — it doesn't make content decisions. The agent decides what to create; MCP executes it.\n\nMCP runs over two transports (stdio for IDE agents, HTTP for Studio / CI / remote drivers) and three provider backends: **Local** (simple-git + worktree), **GitHub** (Octokit over the Git Data API), and **GitLab** (gitbeaker over the REST API). The tool surface is identical across all three; some tools require `LocalProvider` — see [Providers and transports](/guides/providers) for the capability matrix.\n\n### 2. Agent (Intelligence)\n\nThe AI agent (Claude, GPT, etc.) is the intelligence layer:\n\n- Understands your codebase structure\n- Evaluates which strings are user-facing content\n- Chooses the right model kind for each content group\n- Determines framework-specific replacement patterns\n- Manages the review workflow with the developer\n\n### 3. Serve UI (Review Surface)\n\nA local web UI for developers to:\n\n- **Monitor:** browse models, content, validation, history\n- **Approve:** review and merge agent-created branches\n- **Prompt:** copy pre-built prompts to paste into their agent\n\nThe UI never triggers mutations directly — all actions go through the agent.\n\n## Model Kinds\n\nContentrain supports four content model kinds:\n\n### Collection\n\nMultiple entries with the same field schema. Each entry has an auto-generated ID.\n\n```json\n// .contentrain/content/marketing/faq/en.json\n{\n \"abc123\": { \"question\": \"What is Contentrain?\", \"answer\": \"...\" },\n \"def456\": { \"question\": \"How does it work?\", \"answer\": \"...\" }\n}\n```\n\n**Use for:** blog posts, FAQ items, team members, testimonials, pricing plans.\n\n### Singleton\n\nA single entry per locale with structured fields.\n\n```json\n// .contentrain/content/marketing/hero/en.json\n{ \"title\": \"Build Faster\", \"subtitle\": \"With AI\", \"cta_text\": \"Get Started\" }\n```\n\n**Use for:** hero sections, site settings, navigation config, footer content.\n\n### Dictionary\n\nFlat key-value pairs — all values are strings. No field schema.\n\n```json\n// .contentrain/content/system/ui-labels/en.json\n{ \"nav.home\": \"Home\", \"nav.about\": \"About\", \"error.required\": \"This field is required\" }\n```\n\n**Use for:** UI labels, button text, error messages, validation messages, i18n strings.\n\n::: tip Parameterized Templates\nDictionary values support `{placeholder}` syntax:\n```ts\ndictionary('ui-labels').locale('en').get('welcome', { name: 'Ahmet' })\n// \"Hello, {name}!\" → \"Hello, Ahmet!\"\n```\n:::\n\n### Document\n\nMarkdown files with frontmatter metadata.\n\n```markdown\n---\ntitle: Getting Started\ncategory: guides\n---\n\n# Getting Started\n\nThis is the markdown body content...\n```\n\n**Use for:** blog articles, documentation pages, changelogs, legal pages.\n\n## Domains\n\nModels are organized by domain — a logical grouping:\n\n```\n.contentrain/content/\n marketing/ ← hero, pricing, testimonials\n blog/ ← blog-post, author\n system/ ← navigation, ui-labels\n```\n\nDomains are configured in `.contentrain/config.json`.\n\n## Locales & i18n\n\nEvery model can be i18n-enabled (`i18n: true`). Content is stored per locale:\n\n```\n.contentrain/content/marketing/hero/\n en.json ← English\n tr.json ← Turkish\n```\n\nAny platform can read the locale files directly. The TypeScript SDK adds convenience:\n\n```ts\n// Option 1: Read JSON directly (any language)\n// Just read .contentrain/content/marketing/hero/tr.json\n\n// Option 2: TypeScript SDK (optional)\nsingleton('hero').locale('tr').get() // typed, with query API\n```\n\n## Git Workflow\n\nEvery write operation creates a Git commit on a namespaced branch:\n\n```\ncr/model/hero-section ← model changes\ncr/content/blog-post ← content changes\ncr/normalize/extract/... ← normalize extraction\ncr/normalize/reuse/... ← source patching\n```\n\nBranches are auto-merged or held for review depending on your workflow config. When a review branch is merged or rejected, its remote copy is deleted too, so merged branches don't linger as phantom pending reviews — run `contentrain prune` to drain any existing backlog, or set `remoteBranchCleanup: false` to opt out.\n\n## How It Compares\n\n| | Contentrain AI | Headless CMS (Sanity, Strapi) | Git CMS (Tina, Decap) |\n|---|---|---|---|\n| **Content source** | Your codebase or agent-created from scratch | External dashboard | Markdown/JSON in repo |\n| **AI integration** | Native (MCP tools) | Manual API calls | None |\n| **Extract from code** | Yes (normalize flow) | No | No |\n| **Vendor lock-in** | None (plain files + Git) | API dependency | Editor dependency |\n| **Type safety** | Generated SDK (optional) | Manual types | Manual types |\n| **i18n** | Built-in (per-locale files) | Plugin/addon | Plugin/addon |\n| **Review workflow** | Git branches + local UI | Dashboard roles | PR-based |\n| **Runtime dependency** | Zero (static files) | API availability | Build-time |\n\n::: tip No Lock-In\nContentrain stores everything as plain JSON and Markdown files in your Git repo. If you stop using Contentrain tomorrow, your content is still there — readable, portable, yours.\n:::\n\n## Beyond Web: Content CDN\n\nWeb projects deploy content with `git push` — no extra infrastructure needed. But non-web platforms (iOS, Android, React Native, Flutter, desktop apps, game engines, IoT) can't read from a git repo at runtime.\n\nFor these use cases, Contentrain publishes merged content to a CDN (`cdn.contentrain.io`), delivering the same structured JSON your SDK queries — just over HTTP instead of the filesystem.\n\n```\nGit repo → merge → CDN publish → iOS/Android/Flutter fetch → typed response\n```\n\nThis means one content source powers your website, mobile app, and any other platform — all governed by the same review workflow.\n\n## Contentrain Studio\n\n[Contentrain Studio](/studio) is the open-core team operations panel for Git-native structured content. Teams can self-host the AGPL core or use a managed Pro/Enterprise offering on top of the same model.\n\nThe local open-source stack gives you:\n\n- MCP tools\n- CLI workflows\n- local review UI\n- generated SDK\n- rules and skills\n\nStudio adds the multi-user operating layer on top of that:\n\n- **Workspace and project management** — organize repos, teams, and environments\n- **Chat-first operations** — run bounded content workflows through a web app\n- **Permissioned collaboration** — owners, admins, editors, reviewers, and viewers\n- **Branch and diff review** — inspect, merge, reject, and track content changes\n- **Media and submissions** — manage assets and inbound content workflows\n- **CDN delivery** — publish merged content to apps that cannot read from Git at runtime\n\nThe local tools and Studio share the same Git-native content model. The difference is not the content format — it is the operating surface.", + "category": "getting-started", + "description": "Understand the architecture: models, content kinds, domains, locales, and the agent-driven workflow", + "order": 2, + "slug": "concepts", + "title": "Core Concepts" +} diff --git a/.contentrain/client/data/docs-guide--demo.mjs b/.contentrain/client/data/docs-guide--demo.mjs new file mode 100644 index 0000000..9c298ca --- /dev/null +++ b/.contentrain/client/data/docs-guide--demo.mjs @@ -0,0 +1,8 @@ +export default { + "body": "# 2-Minute Demo\n\nThis is the **one demo story** that explains Contentrain fastest:\n\n> You have hardcoded UI text in a real app. \n> Your agent extracts it into a governed content layer. \n> Contentrain keeps the process reviewable, typed, and Git-native.\n\n## Before\n\nYour component ships with inline text:\n\n```tsx\nexport default function Hero() {\n return (\n
\n

Build faster with AI-powered content

\n

Ship your next project in days, not weeks

\n \n
\n )\n}\n```\n\nThis works until:\n\n- marketing wants copy changes\n- you need a second language\n- mobile needs the same content\n- the agent starts editing strings directly in source files\n\n## Step 1. Initialize\n\n```bash\nnpx contentrain init\n```\n\nNow you have a governed `.contentrain/` workspace in your repo.\n\n## Step 2. Connect your agent\n\nRun the local MCP entrypoint:\n\n```bash\nnpx contentrain serve --stdio\n```\n\nThen ask your agent:\n\n> Scan this project and extract the hardcoded strings in the hero section.\n\n## Step 3. Extract\n\nThe agent uses `contentrain_scan` to find strings and `contentrain_apply(mode: \"extract\")` to move them into content files.\n\nGenerated content:\n\n```json\n{\n \"title\": \"Build faster with AI-powered content\",\n \"subtitle\": \"Ship your next project in days, not weeks\",\n \"cta\": \"Get started free\"\n}\n```\n\nExample location:\n\n```text\n.contentrain/content/marketing/hero-section/en.json\n```\n\nAt this stage, **source files are untouched**. You can already review, translate, and validate the extracted content.\n\n## Step 4. Reuse\n\nThen ask your agent:\n\n> Replace the hardcoded hero strings with content references.\n\nThe agent proposes a dry-run patch, then `contentrain_apply(mode: \"reuse\")` updates the source file on a review branch.\n\n## After\n\n```tsx\nimport { singleton } from '#contentrain'\n\nexport default function Hero() {\n const hero = singleton('hero-section').locale('en').get()\n\n return (\n
\n

{hero.title}

\n

{hero.subtitle}

\n \n
\n )\n}\n```\n\n## Why this demo matters\n\nThis one flow shows Contentrain's full value:\n\n- **AI agent does the extraction work**\n- **Contentrain enforces schema and Git review**\n- **Your content becomes reusable and translatable**\n- **Your app consumes plain JSON or the typed SDK**\n\n## What to do next\n\n- Follow the full [Getting Started](/getting-started) guide\n- Read the full [Normalize Flow](/guides/normalize)\n- Use [Framework Integration](/guides/frameworks) for your stack\n\n## Ready for Team Collaboration?\n\nThis demo shows extraction and reuse locally. When your team needs role-based review, web-based collaboration, or CDN delivery for mobile and non-web platforms, connect your project to [Contentrain Studio](/studio):\n\n```bash\ncontentrain studio login\ncontentrain studio connect\n```\n\nStudio uses the same `.contentrain/` content model — no changes needed.", + "category": "getting-started", + "description": "See Contentrain's core value in one flow: extract hardcoded UI text, create structured content, and patch source files through review", + "order": 2, + "slug": "demo", + "title": "2-Minute Demo" +} diff --git a/.contentrain/client/data/docs-guide--ecosystem.mjs b/.contentrain/client/data/docs-guide--ecosystem.mjs new file mode 100644 index 0000000..bfc583f --- /dev/null +++ b/.contentrain/client/data/docs-guide--ecosystem.mjs @@ -0,0 +1,8 @@ +export default { + "body": "# Ecosystem Map\n\nContentrain has two product surfaces operating on the same `.contentrain/` contract:\n\n- **Contentrain AI** — the open-source, local-first operating core: MCP, CLI, SDK, rules, and skills\n- **Contentrain Studio** — the open-core team web surface: chat, review, media, forms, APIs, and CDN delivery\n\nThe format does not change. The operating surface does.\n\n## The Split\n\nUse **Contentrain AI** when you need:\n\n- local-first agent workflows\n- hardcoded-string rescue through normalize\n- direct Git-native content operations from an IDE or terminal\n- package-level adoption through `@contentrain/mcp`, `contentrain`, `@contentrain/query`, `@contentrain/rules`, and `@contentrain/skills`\n\nUse **Contentrain Studio** when you need:\n\n- authenticated team workflows in a web app\n- role-aware review and approval\n- project/workspace management\n- media, forms, conversation APIs, and CDN delivery\n- self-hosted or managed team collaboration on top of the same content model\n\n## Package Bridges\n\n| AI surface | Primary job | Studio bridge | Go deeper |\n|---|---|---|---|\n| `@contentrain/mcp` | Deterministic local content operations and normalize | Studio applies the same `.contentrain/` contract through governed web workflows and review surfaces | [MCP Tools](/packages/mcp), [Studio AI Chat](https://docs.contentrain.io/guide/ai-chat) |\n| `contentrain` CLI | Bootstrap, validate, diff, generate, serve, and local review | Studio takes over when teams need authenticated review, roles, and project management | [CLI](/packages/cli), [Studio Quickstart](https://docs.contentrain.io/guide/quickstart) |\n| `@contentrain/query` | Local typed consumption and CDN client transport | Studio adds remote delivery, API keys, and CDN publishing on top of the same content | [Query SDK](/packages/sdk), [Studio CDN](https://docs.contentrain.io/guide/cdn) |\n| `@contentrain/rules` | Quality standard and schema/content guardrails | Studio should match the same quality standard in chat, review, and content validation | [Rules & Skills](/packages/rules), [Studio Architecture](https://docs.contentrain.io/developer/architecture) |\n| `@contentrain/skills` | Agent playbooks, workflow hints, and normalize/review procedures | Studio mirrors these workflows in chat-led operations, onboarding, and promotion moments | [Rules & Skills](/packages/rules), [Studio AI Chat](https://docs.contentrain.io/guide/ai-chat) |\n\n## MCP Distribution Surfaces\n\n| Surface | What users see | Best fit |\n|---|---|---|\n| MCP Registry | `io.github.Contentrain/contentrain` | Discover and install the local `@contentrain/mcp` server from MCP-compatible clients |\n| OpenAI Codex | `codex mcp add`, `.codex/config.toml`, and future plugin packaging | Developers who want Contentrain tools inside Codex CLI, desktop, or IDE workflows |\n| Claude Desktop | `claude_desktop_config.json` | Local desktop users who want a checkout-scoped Contentrain server |\n| Claude Code | `claude mcp add`, `.mcp.json`, and plugin marketplaces | Coding-agent workflows with project-scoped MCP approval and skills |\n| Studio MCP Cloud | Hosted Streamable HTTP endpoint + API key | Teams that want external agents to operate a Studio project without sharing a local checkout |\n\n## Product Philosophy\n\n- **Governance, not generation** — Contentrain standardizes, validates, and reviews content. Your model provider still does the generation.\n- **Git-native, not database-first** — content stays in Git; branches, commits, diffs, and merge rules stay visible.\n- **Packages drive adoption** — the AI repo is the easiest entry point for developers and agents.\n- **Studio deepens operations** — once content exists, teams need permissions, review, shareable workflows, and delivery surfaces.\n\n## Marketing and Distribution Logic\n\nThe growth loop is intentional:\n\n1. **Normalize is the wedge** — most developers discover Contentrain by rescuing hardcoded strings in an existing codebase.\n2. **Packages create trust** — MCP, CLI, rules, skills, and query are adoptable independently.\n3. **Studio is the expansion surface** — teams move into Studio when review, collaboration, and delivery become operational needs.\n4. **CDN and team workflows monetize naturally** — after the content layer already exists.\n\nThat means AI docs should mention Studio at natural escalation points:\n\n- after `contentrain init`\n- after successful normalize extraction\n- when review branches accumulate\n- when multiple collaborators or locales appear\n- when content must ship beyond web runtime\n\nStudio docs should point back to AI packages whenever they explain:\n\n- where the `.contentrain/` contract comes from\n- how normalize starts the journey\n- how rules and skills shape agent behavior\n- how `@contentrain/query` relates to CDN delivery and consumer apps\n\n## Docs Map\n\n| Need | Canonical surface |\n|---|---|\n| Package contracts and local workflows | `ai.contentrain.io` |\n| Team workflows and product docs | `docs.contentrain.io` |\n| Studio deployment and self-hosting | `docs.contentrain.io/developer/self-hosting` |\n\n## Short Definition\n\nContentrain AI is the package surface that gets structured content into Git. Contentrain Studio is the team surface that helps people review, operate, and deliver that same governed content.", + "category": "getting-started", + "description": "How Contentrain AI packages and Contentrain Studio fit together across discovery, governance, collaboration, and delivery.", + "order": 2, + "slug": "ecosystem", + "title": "Ecosystem Map" +} diff --git a/.contentrain/client/data/docs-guide--getting-started.mjs b/.contentrain/client/data/docs-guide--getting-started.mjs new file mode 100644 index 0000000..8c8ef85 --- /dev/null +++ b/.contentrain/client/data/docs-guide--getting-started.mjs @@ -0,0 +1,8 @@ +export default { + "body": "# Getting Started\n\nContentrain AI is an open-source, repo-native content governance stack. Your AI agent creates, extracts, and updates content through [Model Context Protocol (MCP)](https://modelcontextprotocol.io) tools, while Contentrain ensures that content is structured, validated, reviewed, and deliverable to any platform.\n\n## Prerequisites\n\n- Node.js 22+\n- Git\n- An MCP-compatible AI agent (OpenAI Codex, Claude Desktop, Claude Code, Cursor, Windsurf, or similar)\n\n## Choose Your Path\n\n::: code-group\n\n```bash [Existing Project]\n# This is the main wedge: rescue hardcoded strings\nnpx contentrain init\n# Then tell your agent: \"Scan my project and extract all hardcoded strings\"\n```\n\n```bash [New Project]\n# Start with structured content from day one\nnpx contentrain init\n```\n\n:::\n\n## Quick Start: Existing Project\n\nAlready have hardcoded strings scattered across your codebase? Start here.\n\n```bash\nnpx contentrain init\nnpx contentrain serve --stdio\n```\n\nThen tell your agent:\n\n```text\nScan my project and extract all hardcoded UI strings into structured content.\n```\n\nTypical outcome:\n\n```text\nAgent scans 47 files → finds 523 strings → classifies → creates models → writes content → proposes source patches\n```\n\nReview the extracted content and branch diffs locally, then continue with the full [Normalize Flow](/guides/normalize).\n\n## Quick Start: New Project\n\n### 1. Initialize\n\n```bash\nnpx contentrain init\n```\n\nThis creates `.contentrain/` in your project root with configuration, model definitions, and content directories. If the directory is not a git repo, it runs `git init` automatically.\n\n### 2. Connect your AI agent\n\nThe `setup` command auto-configures the MCP server for your IDE:\n\n```bash\nnpx contentrain setup claude-code # → creates .mcp.json\nnpx contentrain setup cursor # → creates .cursor/mcp.json\nnpx contentrain setup vscode # → creates .vscode/mcp.json\nnpx contentrain setup windsurf # → creates .windsurf/mcp.json\nnpx contentrain setup --all # → configures all detected IDEs\n```\n\nOpenAI Codex and Claude Desktop can be connected manually:\n\n```bash\ncodex mcp add contentrain -- npx -y contentrain serve --stdio\n```\n\n```json\n{\n \"mcpServers\": {\n \"contentrain\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"contentrain\", \"serve\", \"--stdio\"]\n }\n }\n}\n```\n\n::: tip Auto-configured during init\nIf your IDE is detected during `contentrain init`, the MCP config is created automatically — you may already be set up.\n:::\n\n
\nManual configuration (all IDEs use the same config)\n\n```json\n{\n \"mcpServers\": {\n \"contentrain\": {\n \"command\": \"npx\",\n \"args\": [\"contentrain\", \"serve\", \"--stdio\"]\n }\n }\n}\n```\n\n| IDE | Config file |\n|-----|-------------|\n| OpenAI Codex | `.codex/config.toml` or `~/.codex/config.toml` |\n| Claude Desktop | `claude_desktop_config.json` |\n| Claude Code | `.mcp.json` (project root) |\n| Cursor | `.cursor/mcp.json` |\n| VS Code | `.vscode/mcp.json` |\n| Windsurf | `.windsurf/mcp.json` |\n\n
\n\n::: tip HTTP Transport\nIn addition to stdio, MCP also serves over HTTP at `POST /mcp`. Useful for Studio / CI runners / remote agents that drive Contentrain operations without a local IDE:\n\n```bash\nnpx contentrain serve --mcpHttp --authToken $(openssl rand -hex 32)\n```\n\nSee the [HTTP Transport guide](/guides/http-transport) for auth, deployment patterns, and programmatic embedding.\n:::\n\n::: tip Studio MCP Cloud\nWhen the project is connected to Studio, Workspace Settings → MCP Cloud creates a hosted Streamable HTTP endpoint and project-scoped API key. Use that endpoint for Codex, Claude Desktop, Claude Code, Cursor, or any MCP client that should operate the Studio project without a local checkout.\n:::\n\n### 3. Create a content model\n\nTell your agent:\n\n```\nCreate a hero section model with title, subtitle, and CTA fields\n```\n\nThe agent calls `contentrain_model_save` and creates:\n\n```\n.contentrain/\n models/hero-section.json\n content/marketing/hero-section/en.json\n```\n\n### 4. Add content\n\n```\nAdd content to the hero section: title is \"Build faster with AI\",\nsubtitle is \"Content governance for any platform\"\n```\n\n### 5. Use your content\n\nThe content is plain JSON — you can read it directly from any language or platform:\n\n```json\n{\n \"title\": \"Build faster with AI\",\n \"subtitle\": \"Content governance for any platform\"\n}\n```\n\nFor TypeScript projects, generate a typed SDK client for convenience:\n\n```bash\nnpx contentrain generate\n```\n\n```ts\nimport { singleton } from '#contentrain'\n\nconst hero = singleton('hero-section').locale('en').get()\nconsole.log(hero.title) // \"Build faster with AI\"\n```\n\n::: tip SDK is optional\nThe generated TypeScript SDK provides type-safe queries, but the content files are plain JSON and Markdown. Any platform that reads JSON — Go, Python, Swift, Kotlin, Rust — can consume your content directly.\n:::\n\n### 6. Review with the local UI\n\n```bash\nnpx contentrain serve\n```\n\nOpen `http://localhost:3333` to browse models, content, validation results, and pending branches.\n\n## Team Workflows\n\nWhen the local CLI and MCP flow are not enough, [Contentrain Studio](/studio) adds the team web layer:\n\n- workspace and project management\n- role-based access and review responsibilities\n- chat-first content operations\n- branch and diff review\n- media management\n- CDN delivery for non-web platforms\n\nConnect your local project to Studio with two commands:\n\n```bash\ncontentrain studio login\ncontentrain studio connect\n```\n\nThe `connect` command detects your git remote, verifies GitHub App installation, scans for `.contentrain/` configuration, and creates the project — all in one interactive flow. See [CLI Studio Integration](/packages/cli#connecting-a-repository) for details.\n\n## The Content Pipeline\n\nEvery operation follows the same governance pipeline:\n\n```\nAgent generates → MCP validates → Human reviews → Git commits → Content delivered\n```\n\n- **Agent** decides what to create (your AI, your choice — BYOA)\n- **MCP** enforces structure, validation, and canonical serialization\n- **Human** reviews and approves through the local UI or [Studio](/studio)\n- **Git** stores everything — full history, rollback, audit trail\n- **Content** is delivered as plain JSON/Markdown to any platform\n\n## Packages\n\nAll packages are published on npm:\n\n| Package | Description | Install |\n|---|---|---|\n| [`contentrain`](https://www.npmjs.com/package/contentrain) | CLI (init, serve, generate, validate) | `npx contentrain init` |\n| [`@contentrain/mcp`](https://www.npmjs.com/package/@contentrain/mcp) | 24 MCP tools for AI agents | `pnpm add @contentrain/mcp` |\n| [`@contentrain/query`](https://www.npmjs.com/package/@contentrain/query) | TypeScript query SDK (optional) | `pnpm add @contentrain/query` |\n| [`@contentrain/types`](https://www.npmjs.com/package/@contentrain/types) | Shared TypeScript types | `pnpm add @contentrain/types` |\n| [`@contentrain/rules`](https://www.npmjs.com/package/@contentrain/rules) | AI agent quality rules | `pnpm add @contentrain/rules` |\n| [`@contentrain/skills`](https://www.npmjs.com/package/@contentrain/skills) | AI agent workflow procedures | `pnpm add @contentrain/skills` |\n\n::: tip Updating Skills & Rules\nAfter upgrading packages, run `contentrain skills --update` to refresh IDE skills and rules. Use `contentrain skills --list` to check installation status.\n\nYou can also install skills directly via the [Agent Skills CLI](https://agentskills.io):\n\n```bash\nnpx skills add Contentrain/ai/packages/skills\n```\n\nThis works with Claude Code, Cursor, Windsurf, GitHub Copilot, OpenAI Codex, Gemini CLI, and 40+ other agents.\n:::\n\n## Starter Templates\n\nWant to skip setup? Start from a production-ready template with content models, SDK client, and framework patterns pre-configured:\n\n| Template | Framework | Use Case |\n|---|---|---|\n| [astro-blog](https://github.com/Contentrain/contentrain-starter-astro-blog) | Astro | Blog / editorial |\n| [astro-landing](https://github.com/Contentrain/contentrain-starter-astro-landing) | Astro | Landing page |\n| [next-commerce](https://github.com/Contentrain/contentrain-starter-next-commerce) | Next.js | E-commerce |\n| [next-saas-dashboard](https://github.com/Contentrain/contentrain-starter-next-saas-dashboard) | Next.js | SaaS dashboard |\n| [nuxt-saas](https://github.com/Contentrain/contentrain-starter-nuxt-saas) | Nuxt | SaaS marketing |\n| [sveltekit-editorial](https://github.com/Contentrain/contentrain-starter-sveltekit-editorial) | SvelteKit | Editorial |\n| [vitepress-docs](https://github.com/Contentrain/contentrain-starter-vitepress-docs) | VitePress | Documentation |\n\n[See all 10 templates on GitHub](https://github.com/orgs/Contentrain/repositories?q=contentrain-starter&type=template)\n\n## What's Next?\n\n- [Core Concepts](/concepts) — Models, content kinds, domains, and the governance architecture\n- [Ecosystem Map](/ecosystem) — How AI packages and Studio fit together\n- [MCP Tools](/packages/mcp) — All 24 tools available to your agent\n- [Normalize Flow](/guides/normalize) — Extract hardcoded strings from existing code\n- [i18n Workflow](/guides/i18n) — Add languages to your content\n- [Framework Integration](/guides/frameworks) — Platform-specific setup patterns\n\n::: info Contentrain Studio\n[Contentrain Studio](/studio) is the open-core team operations surface for Git-native structured content. Teams can self-host the AGPL core or use a managed Pro/Enterprise offering when they want web-based collaboration, review, media, and CDN delivery on top of the same content model.\n:::", + "category": "getting-started", + "description": "Set up structured content governance in under 5 minutes — start with an existing codebase rescue or a new content layer", + "order": 1, + "slug": "getting-started", + "title": "Getting Started" +} diff --git a/.contentrain/client/data/docs-guide--index.mjs b/.contentrain/client/data/docs-guide--index.mjs new file mode 100644 index 0000000..451de73 --- /dev/null +++ b/.contentrain/client/data/docs-guide--index.mjs @@ -0,0 +1,31 @@ +export default { + "alt": "Deliver", + "body": "## Start With the Mess You Already Have\n\nAI made you faster at producing code. It did not solve what happens to the content inside that code.\n\n**Already shipped?** Hardcoded UI text lives across components, pages, and docs. No translation path, no single source of truth:\n\n- **Monday:** Founder says \"we need Turkish\"\n- **Tuesday:** Marketer wants to change the hero headline — it's in 7 files\n- **Wednesday:** Mobile team asks for the same content via API\n- **Friday:** You're still doing grep-and-replace\n\nThis is the first wedge for Contentrain:\n\n**take the content chaos already living in your codebase and turn it into a governed, reviewable content layer.**\n\n**Starting fresh?** That still matters. You can begin with a real content layer from day one. But the most urgent use case is rescue, not prevention.\n\n**This is not mainly a CMS problem. It's a governance problem.** AI can produce content quickly; Contentrain governs how that content gets structured, reviewed, translated, and delivered.\n\n## Three Core Use Cases\n\n::: code-group\n\n```bash [Hardcoded Strings Rescue]\n# Existing app? This is the main entry point\nnpx contentrain init\n# Then tell your agent: \"Scan my project and extract hardcoded UI strings\"\n```\n\n```bash [Day-One Content Layer]\n# New project? Start with structured content instead of inline strings\nnpx contentrain init\n# Then tell your agent: \"Create a hero section model with title, subtitle, and CTA\"\n```\n\n```bash [Cross-Platform Delivery]\n# Same content for web, docs, mobile, and backend consumers\nnpx contentrain generate\n# Use plain JSON/Markdown or the typed SDK in TypeScript apps\n```\n\n:::\n\n## Start Here\n\nIf you only want to understand the product once before going deeper:\n\n1. Read the [2-Minute Demo](/demo)\n2. Use the full [Normalize Flow](/guides/normalize) on a real repo\n3. Follow [Getting Started](/getting-started)\n4. Use the [Ecosystem Map](/ecosystem) once review, team ops, or CDN delivery matter\n\n**Before** (hardcoded, untranslatable, unmanageable):\n```vue\n

Build faster with AI-powered content

\n

Ship your next project in days, not weeks

\n\n```\n\n**After** (structured, translatable, platform-independent):\n```vue\n

{{ t('hero.title') }}

\n

{{ t('hero.subtitle') }}

\n\n```\n\nContent lives in plain JSON — any platform can read it:\n\n```json\n{\n \"hero.title\": \"Build faster with AI-powered content\",\n \"hero.subtitle\": \"Ship your next project in days, not weeks\",\n \"cta.get_started\": \"Get started free\"\n}\n```\n\n## Start From a Template\n\nPick a production-ready starter with Contentrain content models, generated SDK client, and framework best practices:\n\n| Template | Framework | Use Case |\n|---|---|---|\n| [astro-blog](https://github.com/Contentrain/contentrain-starter-astro-blog) | Astro | Blog / editorial |\n| [astro-landing](https://github.com/Contentrain/contentrain-starter-astro-landing) | Astro | Landing page |\n| [next-commerce](https://github.com/Contentrain/contentrain-starter-next-commerce) | Next.js | E-commerce |\n| [next-multi-surface-saas](https://github.com/Contentrain/contentrain-starter-next-multi-surface-saas) | Next.js | Marketing + app + docs |\n| [next-saas-dashboard](https://github.com/Contentrain/contentrain-starter-next-saas-dashboard) | Next.js | SaaS dashboard |\n| [next-white-label-portal](https://github.com/Contentrain/contentrain-starter-next-white-label-portal) | Next.js | White-label portal |\n| [nuxt-admin-console](https://github.com/Contentrain/contentrain-starter-nuxt-admin-console) | Nuxt | Admin console |\n| [nuxt-saas](https://github.com/Contentrain/contentrain-starter-nuxt-saas) | Nuxt | SaaS marketing |\n| [sveltekit-editorial](https://github.com/Contentrain/contentrain-starter-sveltekit-editorial) | SvelteKit | Editorial |\n| [vitepress-docs](https://github.com/Contentrain/contentrain-starter-vitepress-docs) | VitePress | Documentation |\n\nEach is a GitHub template — click **\"Use this template\"** to create your own repo.\n\n## Who It Is For\n\n### AI-assisted product teams\n\nYou already use Claude Code, Cursor, or another agent and want content changes to go through a real reviewable system instead of landing as random string edits.\n\n### Teams with hardcoded UI copy or docs in source\n\nYou need to extract strings, make i18n possible, and stop treating copy updates like grep-and-replace work.\n\n### Builders who want Git-native content, not dashboard-first content\n\nYou want plain JSON and Markdown in Git, optional SDK ergonomics, and a content layer your stack can consume directly.\n\n## Who It Is Not For\n\n- Teams looking for a database-first or dashboard-first CMS without Git in the workflow\n- Teams that do not want Git in the content workflow\n- Teams that do not use AI agents and do not need a governed extraction/review layer\n\n## Platform Independent\n\nContentrain outputs plain JSON and Markdown files. **Any platform that can read JSON can consume your content:**\n\n- Web frameworks (Vue, React, Nuxt, Next.js, Astro, SvelteKit)\n- Mobile (React Native, Flutter, Swift, Kotlin)\n- Backend (Node.js, Go, Python, Rust, .NET)\n- Desktop (Electron, Tauri)\n- Game engines, IoT, email templates, CLI tools\n\nThe TypeScript SDK (`@contentrain/query`) provides type-safe queries as a convenience — but it's optional. The content files are the product, not the SDK.\n\n## Bring Your Own Agent\n\nContentrain does not ship its own model. Your agent (Claude Code, Cursor, Windsurf, or any MCP-compatible tool) **is** the intelligence layer. Contentrain provides deterministic MCP tools and CLI workflows that enforce consistency:\n\n```\nAgent decides what to extract → MCP validates and writes → Human reviews → Git commits\n```\n\nNo AI markup in your code. No proprietary syntax. No vendor lock-in. If you stop using Contentrain, your content files are still plain JSON in your Git repo.\n\n::: info Contentrain Studio\n[Contentrain Studio](/studio) is the open-core team web layer for Contentrain: chat-first operations, role-based collaboration, branch review, media management, and CDN delivery on top of the same Git-native content model. Teams can self-host the AGPL core or use a managed Pro/Enterprise offering.\n:::", + "category": "getting-started", + "description": "Extract, govern, and ship structured content from your codebase.", + "details": "Plain JSON and Markdown files — consumable by any platform, any language. TypeScript SDK for convenience, but the files are the product.", + "features": [ + "icon:" + ], + "height": 24, + "hero": { + "actions": [ + "theme: brand" + ], + "image": { + "alt": "Contentrain AI Pipeline — Agent generates, MCP validates, Human reviews, Git commits, content delivered", + "src": "/hero-pipeline.png" + }, + "link": "/guides/normalize", + "name": "Contentrain AI", + "tagline": "Turn hardcoded UI text, docs, and structured content into a governed, reviewable content layer. Agents do the extraction work, Contentrain enforces schema, Git review, and portable output.", + "text": "Start the Normalize Flow" + }, + "layout": "home", + "link": "/packages/sdk", + "order": 0, + "slug": "index", + "src": "/icons/deliver.svg", + "title": "Deliver", + "width": 24 +} diff --git a/.contentrain/client/data/docs-guide--studio.mjs b/.contentrain/client/data/docs-guide--studio.mjs new file mode 100644 index 0000000..ecfa619 --- /dev/null +++ b/.contentrain/client/data/docs-guide--studio.mjs @@ -0,0 +1,8 @@ +export default { + "body": "# Contentrain Studio\n\nContentrain Studio is the open-core team operations and delivery surface for Git-native structured content.\n\nIn short:\n\n> Contentrain AI gives developers local-first packages and agent workflows. Studio gives teams an authenticated web surface for review, collaboration, media, APIs, and CDN delivery on top of the same `.contentrain/` contract.\n\nTeams can self-host the AGPL core or use a managed Pro/Enterprise offering, depending on how much infrastructure and operations they want to own.\n\nIt is not a separate content format and it is not a different storage engine. Studio sits on top of the same Contentrain model:\n\n- content lives in Git\n- content is schema-based\n- changes are written through branches, commits, and diffs\n- agents and humans operate on the same governed content layer\n- the same content can later be delivered through files, SDK queries, or CDN endpoints\n\n## What Studio Is\n\nStudio is the team-facing web application in the Contentrain ecosystem.\n\nIt combines three layers:\n\n- **Management layer** — workspaces, projects, members, permissions, and AI configuration\n- **Content execution layer** — chat-driven and UI-driven content operations backed by Git\n- **Delivery layer** — CDN publishing, API access, media, and external conversation endpoints\n\nIf the open-source stack is the local operating core, Studio is the collaboration and delivery surface.\n\n## What Studio Handles\n\nBased on the current product surface, Studio covers these categories:\n\n1. authentication and session management\n2. workspace and project management\n3. chat-based content operations\n4. structured content writing and review workflows\n5. branch and diff management\n6. media management\n7. CDN-based content delivery\n8. external conversation API\n9. form submission management\n10. client-side content cache and search\n\n## Operating Model\n\nStudio is not database-first. It is Git-first.\n\nThe core flow looks like this:\n\n1. a user works inside a workspace and project\n2. the project is connected to a Git repo\n3. the user requests a change\n4. the request reaches the server through chat or an API route\n5. permissions are checked\n6. the content engine validates, serializes, opens a branch, and creates a commit\n7. the workflow either auto-merges or leaves a review branch open\n8. the UI exposes the result through conversations, branches, diffs, and snapshots\n\nThat means Studio is not just a dashboard. It is the governed web operating surface for reviewable content operations.\n\n## Package Bridges\n\nStudio is where the AI package surfaces meet team operations:\n\n| AI surface | What starts in AI | What continues in Studio |\n|---|---|---|\n| `@contentrain/mcp` | deterministic local content operations, normalize, validation | governed web workflows, review, approval, project-level operations |\n| `contentrain` CLI | init, serve, diff, generate, validate | authenticated review, role-based collaboration, project/workspace management |\n| `@contentrain/rules` | quality and schema guardrails | shared content quality expectations across chat, review, and validation |\n| `@contentrain/skills` | workflow playbooks and promotion hints | mirrored chat-led workflows, onboarding moments, and review handoffs |\n| `@contentrain/query` | local typed consumption and CDN transport | remote delivery, API key management, CDN publishing, and non-web distribution |\n\n## Core Concepts\n\n### Workspace\n\nThe workspace is the team, billing, and permission boundary.\n\nStudio can:\n\n- list available workspaces\n- create new workspaces\n- update workspace settings\n- manage members\n- enforce workspace-level roles\n\n### Project\n\nA project is a repo-connected unit inside a workspace.\n\nStudio can:\n\n- list accessible projects\n- connect new projects to repositories\n- fetch project details\n- manage project-level access\n\n### Structured Content\n\nStudio uses the same four model kinds as Contentrain:\n\n- collection\n- singleton\n- document\n- dictionary\n\nThe point is not to create a separate Studio-only model. The point is to operate the same structured content layer through a team web app.\n\n## Auth, Roles, and Permissions\n\nStudio is an authenticated application with role-aware access.\n\nSupported auth flows:\n\n- GitHub OAuth\n- Google OAuth\n- magic link\n\nPermissions are layered:\n\n- workspace roles: `owner`, `admin`, `member`\n- project roles: `editor`, `reviewer`, `viewer`\n- model-level restrictions where needed\n\nThis matters because Studio is not just “can the user log in?” It is “who can change which project, which models, and which workflows?”\n\n## Chat-First Content Operations\n\nOne of Studio's defining features is chat.\n\nBut this is not an open-ended chatbot. It is a bounded, tool-driven content operations interface:\n\n- user message enters with project context\n- permissions and phase rules are applied\n- the system builds the right agent/tool context\n- tool calls are executed through the governed content path\n- results stream back to the UI\n\nSo the chat surface is an operations UI, not a generic assistant shell.\n\n## Content Engine\n\nThe content engine is the execution core behind Studio. It is a [`@contentrain/mcp`](/packages/mcp) server driven over HTTP (`POST /mcp`) with a `GitHubProvider` or `GitLabProvider` pointed at the team's content repository. Studio never talks to Git directly — every operation flows through MCP's `RepoProvider` contract and lands as an atomic commit on a `cr/*` feature branch.\n\nThe engine is responsible for:\n\n- reading model definitions\n- loading current content\n- validating data\n- resolving content paths\n- serializing canonically\n- creating branches and commits\n- producing diffs\n\nSupported operations include:\n\n- content save\n- document save\n- content delete\n- model save\n- entry status update\n- locale copy\n- project init\n- branch merge / reject\n\nStudio uses the same tool contract the stdio-driven IDE agents use. A new git host support added to MCP (today: Local / GitHub / GitLab; Bitbucket coming) becomes a Studio connector automatically.\n\nSee [Providers & Transports](/guides/providers) for the capability matrix and [HTTP Transport](/guides/http-transport) for the deployment pattern.\n\n## Review, Branches, and Diffs\n\nStudio is built around reviewable content changes.\n\nIt provides:\n\n- pending branch listings\n- branch diff inspection\n- merge and reject flows\n- conversation-linked review context\n\nThis is one of the clearest places where Studio stops being “content admin UI” and becomes “team operations panel.”\n\n## Delivery, CDN, and APIs\n\nStudio is not only about editing content. It also handles delivery.\n\nThat includes:\n\n- CDN publishing\n- CDN keys and access control\n- build history and triggers\n- public delivery routes\n- external conversation API keys and scoped access\n\nThis makes Studio useful for:\n\n- mobile apps\n- desktop apps\n- game engines\n- other platforms that cannot read from Git directly at runtime\n\n## Media and Content-In Flows\n\nStudio also extends the ecosystem with adjacent operational capabilities:\n\n- media upload and asset management\n- metadata and variants\n- forms and submission review\n- client-side cached snapshots and search\n\nThese are not side details. They matter because team content operations rarely end at “edit a JSON file.”\n\n## Relationship to the Open-Source Stack\n\nThe open-source stack remains the foundation:\n\n- `@contentrain/mcp`\n- `contentrain`\n- `@contentrain/query`\n- `@contentrain/rules`\n- `@contentrain/skills`\n\nStudio is the open-core web layer on top:\n\n- team access\n- web workflows\n- review UX\n- media\n- delivery\n\nSo the split is:\n\n- **open-source Contentrain** = local, Git-native content governance core\n- **Contentrain Studio** = team operations and delivery layer for the same content contract\n\n## Connecting from the CLI\n\nThe fastest way to create a Studio project from an existing repository is the CLI `connect` command:\n\n```bash\ncontentrain studio login\ncontentrain studio connect\n```\n\nThis single interactive flow handles workspace selection, GitHub App installation, repository detection, `.contentrain/` scanning, and project creation. See the [CLI Studio Integration](/packages/cli#connecting-a-repository) section for the full step-by-step.\n\n## Go Deeper\n\n- [Ecosystem Map](/ecosystem)\n- [Studio AI Chat](https://docs.contentrain.io/guide/ai-chat)\n- [Studio Branches & Review](https://docs.contentrain.io/guide/branches-and-review)\n- [Studio CDN](https://docs.contentrain.io/guide/cdn)\n- [Studio Self-Hosting](https://docs.contentrain.io/developer/self-hosting)\n\n## Short Definition\n\nContentrain Studio is a web application that lets teams manage structured content living in Git through chat, visual workflows, branch review, and delivery APIs.", + "category": "getting-started", + "description": "Open-core team operations and delivery for the same Git-native content contract used by Contentrain AI", + "order": 3, + "slug": "studio", + "title": "Contentrain Studio" +} diff --git a/.contentrain/client/data/docs-guides--embedding-mcp.mjs b/.contentrain/client/data/docs-guides--embedding-mcp.mjs new file mode 100644 index 0000000..fcefd10 --- /dev/null +++ b/.contentrain/client/data/docs-guides--embedding-mcp.mjs @@ -0,0 +1,6 @@ +export default { + "body": "# Embedding MCP in a Host Application\n\n`@contentrain/mcp` is distributed as a standalone package so it can be embedded in any Node.js host — a hosted CMS, a CI runner, a custom agent driver, an internal tool. This guide walks through the shapes that integration can take and the primitives you'll touch.\n\nStudio (`contentrain.io`) is the canonical consumer; the patterns below describe what Studio does and what third parties should do to match it.\n\n## What you're embedding\n\n`@contentrain/mcp` ships three pieces you plug together:\n\n1. **A `RepoProvider`** — Local / GitHub / GitLab (or your own). Wraps whatever git backend you're targeting.\n2. **An `McpServer`** — the MCP JSON-RPC surface with every Contentrain tool the provider can satisfy registered (19 core + 5 media on media-capable providers).\n3. **A transport** — stdio (for IDE agents) or HTTP (for hosted / remote drivers).\n\nThe three are orthogonal. Mix them freely.\n\n## Installation\n\n```bash\npnpm add @contentrain/mcp @contentrain/types\n```\n\nRemote providers ship as optional peers — install only the ones you'll use:\n\n```bash\n# For GitHub-backed sessions\npnpm add @octokit/rest\n\n# For GitLab-backed sessions\npnpm add @gitbeaker/rest\n```\n\nA pure-LocalProvider embedding (just wrapping a working tree) needs neither peer.\n\n## Construction recipes\n\n### 1. Stdio + LocalProvider (IDE agents)\n\n```ts\nimport { createServer } from '@contentrain/mcp/server'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\n\nconst server = createServer('/path/to/project')\nconst transport = new StdioServerTransport()\nawait server.connect(transport)\n```\n\nThis is what `contentrain serve --stdio` does internally. Every IDE that speaks MCP (Claude Code, Cursor, Windsurf) talks to this shape.\n\n### 2. HTTP + LocalProvider (local CI, Studio-like hosting pointed at a working tree)\n\n```ts\nimport { startHttpMcpServer } from '@contentrain/mcp/server/http'\n\nconst handle = await startHttpMcpServer({\n projectRoot: '/path/to/project',\n port: 3333,\n host: '0.0.0.0',\n authToken: process.env.MCP_BEARER_TOKEN,\n})\n\n// handle.url — \"http://0.0.0.0:3333/mcp\"\n// handle.close() — shuts down when you're done\n```\n\nCLI equivalent: `contentrain serve --mcpHttp --authToken $TOKEN`.\n\n### 3. HTTP + Remote Provider (three patterns)\n\n**a. Factory with GitHub App credentials.** Simplest for one-off scripts and CI runners — the factory signs the JWT, exchanges it for an installation token, and hands Octokit a bearer. The returned token lasts ~1 hour; at that point the factory must be re-called.\n\n```ts\nimport { createGitHubProvider } from '@contentrain/mcp/providers/github'\nimport { startHttpMcpServerWith } from '@contentrain/mcp/server/http'\n\nconst provider = await createGitHubProvider({\n auth: {\n type: 'app',\n appId: Number(process.env.GITHUB_APP_ID),\n privateKey: process.env.GITHUB_APP_PRIVATE_KEY!,\n installationId: Number(process.env.GITHUB_INSTALLATION_ID),\n },\n repo: { owner: 'acme', name: 'site' },\n})\n\nconst handle = await startHttpMcpServerWith({\n provider,\n port: 3333,\n authToken: workspaceBearerToken,\n})\n```\n\n**b. `exchangeInstallationToken` helper for external token caching.** When you want to pin the token lifecycle yourself (cache across requests, refresh on a schedule, share across workers), call the helper directly and pass the opaque bearer to `createGitHubProvider({ auth: { type: 'pat', token } })`.\n\n```ts\nimport {\n createGitHubProvider,\n exchangeInstallationToken,\n} from '@contentrain/mcp/providers/github'\n\nconst { token, expiresAt } = await exchangeInstallationToken({\n appId: Number(process.env.GITHUB_APP_ID),\n privateKey: process.env.GITHUB_APP_PRIVATE_KEY!,\n installationId: Number(process.env.GITHUB_INSTALLATION_ID),\n})\n// cache { token, expiresAt } in redis / your KV of choice\n\nconst provider = await createGitHubProvider({\n auth: { type: 'pat', token },\n repo: { owner: 'acme', name: 'site' },\n})\n```\n\n**c. Inject your own Octokit with `@octokit/auth-app` (recommended for hosted / long-lived providers).** This is Studio's pattern. The Octokit SDK auto-refreshes installation tokens for the lifetime of the instance, so your provider never has to think about expiry.\n\n```ts\nimport { Octokit } from '@octokit/rest'\nimport { createAppAuth } from '@octokit/auth-app'\nimport { GitHubProvider } from '@contentrain/mcp/providers/github'\nimport { startHttpMcpServerWith } from '@contentrain/mcp/server/http'\n\nconst octokit = new Octokit({\n authStrategy: createAppAuth,\n auth: {\n appId: Number(process.env.GITHUB_APP_ID),\n privateKey: process.env.GITHUB_APP_PRIVATE_KEY!,\n installationId: Number(process.env.GITHUB_INSTALLATION_ID),\n },\n})\n\nconst provider = new GitHubProvider(octokit, { owner: 'acme', name: 'site' })\n\nconst handle = await startHttpMcpServerWith({\n provider,\n port: 3333,\n authToken: workspaceBearerToken,\n})\n```\n\n**Trade-offs:**\n\n| Pattern | Best for | Auto-refresh | Deps |\n|---|---|---|---|\n| a — factory `auth.type: 'app'` | Short-lived scripts, CI | No (1-hour TTL) | `@octokit/rest` |\n| b — `exchangeInstallationToken` + PAT | External token cache (redis, KV) | You decide | `@octokit/rest` |\n| c — Octokit injection + `@octokit/auth-app` | Long-lived hosted providers (Studio) | Yes | `@octokit/rest` + `@octokit/auth-app` |\n\nFor **multi-tenant** deployments where each request targets a different project, see the **per-request resolver** section below.\n\nSwap in `createGitLabProvider({ auth, project })` for GitLab. Self-hosted GitLab instances pass `project.host`.\n\n### 3a. HTTP + per-request provider resolver (multi-tenant)\n\nWhen one HTTP endpoint serves many projects (Studio's MCP Cloud), pass a `resolveProvider` function instead of a single provider. The resolver is invoked once per MCP session; subsequent requests with the same `Mcp-Session-Id` header reuse the same server + transport pair. Idle sessions are cleaned up after `sessionTtlMs` (default 15 minutes).\n\n```ts\nimport { createGitHubProvider } from '@contentrain/mcp/providers/github'\nimport { startHttpMcpServerWith } from '@contentrain/mcp/server/http'\n\nconst handle = await startHttpMcpServerWith({\n resolveProvider: async (req) => {\n const projectId = req.headers['x-project-id'] as string\n const { repo, auth } = await lookupProjectFromDatabase(projectId)\n return createGitHubProvider({ auth, repo })\n },\n // Bind each session to its tenant: follow-up requests must produce the\n // same fingerprint or they get 404 and the client re-initializes.\n sessionFingerprint: (req) => req.headers['x-project-id'] as string,\n authToken: workspaceBearerToken,\n port: 3333,\n sessionTtlMs: 15 * 60 * 1000,\n})\n```\n\nThe single-provider shape (`{ provider }`) and the resolver shape (`{ resolveProvider }`) are mutually exclusive — pass one or the other.\n\n**Session tenant binding.** A session's provider is resolved once, at `initialize`. Without `sessionFingerprint`, any caller that presents a known `Mcp-Session-Id` reaches that session's provider — fine on trusted loopback, not across tenants. Set `sessionFingerprint` to derive a stable tenant identity from each request (e.g. the same headers `resolveProvider` uses); a mismatch answers `404 Session not found`, which per the Streamable HTTP spec makes the client transparently re-initialize its own session.\n\n### 4. Programmatic tool calls (no transport at all)\n\nIf you want to run a Contentrain tool inside your own Node.js process without MCP's JSON-RPC layer:\n\n```ts\nimport { planContentSave } from '@contentrain/mcp/core/ops'\nimport { OverlayReader } from '@contentrain/mcp/core/overlay-reader'\nimport { buildContextChange } from '@contentrain/mcp/core/context'\nimport { validateProject } from '@contentrain/mcp/core/validator'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\n\nconst plan = await planContentSave(provider, { model, entries, config, vocabulary })\nif (plan.result.some(r => r.error)) throw new Error('plan invalid')\n\nconst overlay = new OverlayReader(provider, plan.changes)\nconst contextChange = await buildContextChange(overlay, {\n tool: 'save_content',\n model: model.id,\n locale: entries[0].locale,\n})\n\nconst allChanges = [...plan.changes, contextChange]\n .toSorted((a, b) => a.path.localeCompare(b.path))\n\nconst commit = await provider.applyPlan({\n branch: 'cr/content/blog/2026-04-17-abcd',\n changes: allChanges,\n message: 'content: save blog',\n author: { name: 'Your Bot', email: 'bot@example.com' },\n // base omitted → defaults to CONTENTRAIN_BRANCH\n})\n\nconst validation = await validateProject(overlay, { model: model.id })\n```\n\nUse this shape when you want tool-level control without the JSON-RPC envelope.\n\n## Critical primitives\n\nThree primitives matter when you build a non-local write path. Studio ran into all three during integration; get them right up front.\n\n### `CONTENTRAIN_BRANCH` is the fork point, always\n\nEvery feature branch (`cr/content/...`, `cr/model/...`, `cr/normalize/...`) forks from the singleton `contentrain` branch. NOT from the repo's default branch (`main` / `master` / `trunk`). The content-tracking branch is the SSOT; the default branch is downstream.\n\n`provider.applyPlan({ ..., base })` defaults to `CONTENTRAIN_BRANCH` when `base` is omitted. That's the contract. Pass `base` explicitly only when you know you're opting out of the invariant.\n\n### `OverlayReader` for post-commit consistency\n\n`buildContextChange` and `validateProject` take a reader. If you pass the raw provider, they read the pre-change base branch — so your committed `context.json` reports stale entry counts and post-save validation evaluates the wrong state.\n\nWrap the reader with `OverlayReader(reader, plan.changes)`. It layers pending `FileChange`s on top: adds become visible, deletes look missing, everything else falls through. The resulting context + validation reflect the state your commit is about to produce.\n\nThis is only needed for remote / reader-based flows. `LocalProvider`'s transaction writes the worktree before context/validation run, so the filesystem itself is the overlay.\n\n### `capability_required` is a structured error\n\nTools whose requirements can never be met by the session's provider are not registered at all (see [Capability gating](#capability-gating)), so most capability mismatches never reach a handler. The structured error remains for the two input-dependent cases — `validate` with `fix: true` and `apply` in `reuse` mode:\n\n```json\n{\n \"error\": \"contentrain_validate requires local filesystem access.\",\n \"capability_required\": \"localWorktree\",\n \"hint\": \"This tool is unavailable when MCP is driven by a remote provider. Use a LocalProvider or the stdio transport.\"\n}\n```\n\nTreat `capability_required` as a retry signal at the client. Typical fallback: prompt the user to switch to a local checkout, or downgrade the request (e.g. `validate` without `fix`).\n\nSee [Providers & Transports](/guides/providers) for the full capability matrix.\n\n## Authentication\n\n- **Stdio** — no authentication. Transport is localhost pipes; security boundary is the OS.\n- **HTTP** — optional Bearer token. Set `authToken` on `startHttpMcpServer` / `startHttpMcpServerWith`, and require clients to send `Authorization: Bearer `. Missing or mismatched tokens get `401` before any MCP session initialises.\n- **Upstream git hosts** — provider auth (PAT / OAuth / GitHub App installation token / GitLab job token) is scoped per-provider. See the provider's factory docstring.\n\nRotate Bearer tokens regularly. MCP does not support per-tool ACLs; a valid token is full project access.\n\n## Capability gating\n\nEach provider advertises a `ProviderCapabilities` manifest. `createServer` consults it (together with `projectRoot`) at registration time: tools whose requirements can never be met by the session's provider are **not registered**, so `tools/list` only shows what can actually run. The declarative requirement map is exported as `TOOL_REQUIREMENTS` from `@contentrain/mcp/tools/availability`, alongside an `isToolAvailable(name, provider, projectRoot)` helper for embedders that want to reason about the effective surface without spinning up a server.\n\n| Capability | Local | GitHub | GitLab | Gated tools |\n|---|---|---|---|---|\n| `localWorktree` | ✓ | — | — | `validate --fix`, `submit`, `merge`, `branch_list`, `branch_delete` |\n| `sourceRead` | ✓ | — | — | `apply` (extract) |\n| `sourceWrite` | ✓ | — | — | `apply` (reuse) |\n| `astScan` | ✓ | — | — | `scan` |\n| `pushRemote` | ✓ | ✓ | ✓ | `submit` |\n| `branchProtection` | — | ✓ | ✓ | merge fallback |\n| `pullRequestFallback` | — | ✓ | ✓ | merge fallback |\n\n`init`, `scaffold`, `doctor`, and `bulk` additionally require a local `projectRoot` on disk. Input-dependent checks (`validate --fix`, `apply` reuse) stay as call-time guards and return the structured `capability_required` error above.\n\nThe five `contentrain_media_*` tools gate on the provider's optional **media facet** (`RepoProvider.media` — an object implementing `MediaProvider` from `@contentrain/types`, not a boolean flag). Implement it on a custom provider to get the media tools registered; leave it absent and they never appear.\n\nRead-only tools (`status`, `describe`, `describe_format`, `content_list`, `validate` without `--fix`) work on every provider — they use only the reader surface.\n\n## Extension: custom providers\n\nIf your host doesn't use Local / GitHub / GitLab, implement `RepoProvider` directly:\n\n```ts\nimport type { RepoProvider, ProviderCapabilities } from '@contentrain/types'\n\nclass MyProvider implements RepoProvider {\n readonly capabilities: ProviderCapabilities = {\n localWorktree: false,\n sourceRead: false,\n sourceWrite: false,\n pushRemote: true,\n branchProtection: false,\n pullRequestFallback: false,\n astScan: false,\n }\n\n async readFile(path, ref?) { /* your backend */ }\n async listDirectory(path, ref?) { /* your backend */ }\n async fileExists(path, ref?) { /* your backend */ }\n\n async applyPlan(input) {\n // Single atomic commit. Honour input.base (default CONTENTRAIN_BRANCH).\n }\n\n async listBranches(prefix?) { /* ... */ }\n async createBranch(name, fromRef?) { /* ... */ }\n async deleteBranch(name) { /* ... */ }\n async getBranchDiff(branch, base?) { /* ... */ }\n async mergeBranch(branch, into) { /* ... */ }\n async isMerged(branch, into?) { /* ... */ }\n async getDefaultBranch() { /* ... */ }\n}\n\nconst server = createServer({ provider: new MyProvider() })\n```\n\nThe reference implementations under `packages/mcp/src/providers/{local,github,gitlab}/` are ~500 lines each and mirror the same structure. Start there.\n\nFor the full contract see [RepoProvider Reference](/reference/providers).\n\n## Reference integrations\n\n### Contentrain Studio\n\nStudio is the canonical hosted integration. It hosts an HTTP MCP server per workspace, backed by `GitHubProvider` or `GitLabProvider` pointing at the customer's content repo. Bearer tokens are managed per workspace; quota and plan gates sit in a thin middleware in front of MCP. Studio never runs local-only tools (normalize, submit, etc.) — those delegate to the customer's own local checkout.\n\nSee [Studio Overview](/studio) for the product surface and `.internal/refactor/02-studio-handoff.md` in the monorepo for the detailed Studio-side integration plan (Studio-repo-specific).\n\n### CI runners\n\nA GitHub Actions job:\n\n1. `actions/checkout@v4`\n2. `pnpm install`\n3. Start `contentrain serve --mcpHttp --authToken $CI_TOKEN &` (LocalProvider under the hood)\n4. Drive it with an MCP client\n5. Let `contentrain_submit` push the `cr/*` branch\n\nAll 19 core tools are available because the runner has `LocalProvider` (media tools need a media-capable provider).\n\n### Scripted automation\n\nA nightly script that regenerates translation stubs via `contentrain_content_save` over GitHub:\n\n```ts\nconst provider = await createGitHubProvider({\n auth: { type: 'pat', token: process.env.GH_TOKEN! },\n repo: { owner: 'acme', name: 'site' },\n})\n// run planContentSave + commit directly (recipe 4 above)\n```\n\nNo HTTP, no MCP JSON-RPC — just the core primitives.\n\n## Going deeper\n\n- [Providers & Transports](/guides/providers) — capability matrix, when to use which provider\n- [HTTP Transport](/guides/http-transport) — deployment patterns, Bearer auth\n- [RepoProvider Reference](/reference/providers) — contract definitions\n- [MCP package reference](/packages/mcp) — full tool catalogue\n\n## Troubleshooting\n\n**`ERR_PACKAGE_PATH_NOT_EXPORTED`** — you're reaching into a subpath that isn't in `package.json#exports`. Known good subpaths: `/server`, `/server/http`, `/core/config`, `/core/context`, `/core/contracts`, `/core/model-manager`, `/core/content-manager`, `/core/validator`, `/core/ops`, `/core/overlay-reader`, `/core/scanner`, `/core/graph-builder`, `/core/apply-manager`, `/core/scan-config`, `/providers/local`, `/providers/github`, `/providers/gitlab`, `/util/detect`, `/util/fs`, `/git/transaction`, `/git/branch-lifecycle`, `/templates`.\n\n**Stale context.json stats after a remote commit** — you forgot `OverlayReader`. `buildContextChange(provider, op)` reads the pre-change branch; wrap with `new OverlayReader(provider, plan.changes)`.\n\n**Feature branch forked from main instead of contentrain** — you passed `base: config.repository?.default_branch` somewhere. Remove it; the default is `CONTENTRAIN_BRANCH`.\n\n**`@octokit/rest` / `@gitbeaker/rest` not found at runtime** — optional peer isn't installed. Add it to your host's dependencies; the factory throws with this hint.\n\n**Validation passes but the commit still contains bad data** — you're running `validateProject(provider, ...)` without the overlay on a remote path. Same fix: wrap the reader.", + "description": "How to consume @contentrain/mcp from inside another application — transports, providers, authentication, capability gating, and extension points. Includes Studio as a reference integration.", + "slug": "embedding-mcp", + "title": "Embedding MCP in a Host Application" +} diff --git a/.contentrain/client/data/docs-guides--frameworks.mjs b/.contentrain/client/data/docs-guides--frameworks.mjs new file mode 100644 index 0000000..833a19b --- /dev/null +++ b/.contentrain/client/data/docs-guides--frameworks.mjs @@ -0,0 +1,7 @@ +export default { + "body": "# Framework & Platform Integration\n\nContentrain content is plain JSON and Markdown — **any platform that reads files can consume it**. The `@contentrain/query` SDK adds TypeScript type safety and a query API as an optional convenience layer. This guide covers setup patterns for popular frameworks and platforms.\n\n## Starter Templates\n\nEach framework has production-ready starter templates with content models, generated SDK client, and best practices pre-configured:\n\n| Framework | Starters |\n|---|---|\n| **Astro** | [Blog](https://github.com/Contentrain/contentrain-starter-astro-blog), [Landing Page](https://github.com/Contentrain/contentrain-starter-astro-landing) |\n| **Next.js** | [Commerce](https://github.com/Contentrain/contentrain-starter-next-commerce), [Multi-Surface SaaS](https://github.com/Contentrain/contentrain-starter-next-multi-surface-saas), [SaaS Dashboard](https://github.com/Contentrain/contentrain-starter-next-saas-dashboard), [White-Label Portal](https://github.com/Contentrain/contentrain-starter-next-white-label-portal) |\n| **Nuxt** | [SaaS Marketing](https://github.com/Contentrain/contentrain-starter-nuxt-saas), [Admin Console](https://github.com/Contentrain/contentrain-starter-nuxt-admin-console) |\n| **SvelteKit** | [Editorial](https://github.com/Contentrain/contentrain-starter-sveltekit-editorial) |\n| **VitePress** | [Documentation](https://github.com/Contentrain/contentrain-starter-vitepress-docs) |\n\nEach is a GitHub template — click **\"Use this template\"** to create your own repo.\n\n## Universal Setup\n\nEvery framework follows the same initial steps:\n\n### Step 1. Install the SDK\n\n```bash\npnpm add @contentrain/query\n```\n\n### Step 2. Generate the typed client\n\n```bash\nnpx contentrain generate\n```\n\nThis reads `.contentrain/models/` and produces a typed client in `.contentrain/client/`. The generator automatically adds the subpath import to your `package.json`:\n\n```json\n{\n \"imports\": {\n \"#contentrain\": {\n \"types\": \"./.contentrain/client/index.d.ts\",\n \"import\": \"./.contentrain/client/index.mjs\",\n \"require\": \"./.contentrain/client/index.cjs\",\n \"default\": \"./.contentrain/client/index.mjs\"\n }\n }\n}\n```\n\n### Step 3. Import and query\n\n```ts\nimport { query, singleton, dictionary, document } from '#contentrain'\n```\n\n::: tip\nAll SDK queries are synchronous. Content is bundled as static generated modules — no runtime API calls, no `await` needed.\n:::\n\n### Step 4. Run in watch mode during development\n\n```bash\nnpx contentrain generate --watch\n```\n\nAny model or content change under `.contentrain/` triggers client regeneration automatically.\n\n## Nuxt 3\n\n### Setup\n\n```bash\npnpm add @contentrain/query\nnpx contentrain generate\n```\n\nRestart the Nuxt dev server after initial generation. Run watch mode alongside dev:\n\n```bash\nnpx contentrain generate --watch &\nnpx nuxt dev\n```\n\n### Server-only imports\n\nTreat `#contentrain` as **server-only** in Nuxt. Use it in Nitro server routes, server utilities, and route middleware. Do not import it directly in client-rendered Vue components.\n\n### Page data loading\n\nLoad content through a server route, consume with `useAsyncData`:\n\n```ts\n// server/api/blog-posts.get.ts\nimport { query } from '#contentrain'\nimport { getQuery } from 'h3'\n\nexport default defineEventHandler((event) => {\n const locale = getQuery(event).locale?.toString() ?? 'en'\n return query('blog-post').locale(locale).sort('publishedAt', 'desc').all()\n})\n```\n\n```vue\n\n```\n\n### Dynamic routes\n\n```ts\n// server/api/blog-post/[slug].get.ts\nimport { document } from '#contentrain'\nimport { getRouterParam } from 'h3'\n\nexport default defineEventHandler((event) => {\n const slug = getRouterParam(event, 'slug')\n return document('blog-article').locale('en').bySlug(slug ?? '')\n})\n```\n\n### i18n with @nuxtjs/i18n\n\n```vue\n\n```\n\n### Build and deploy\n\n```bash\nnpx nuxt generate\n```\n\nAll SDK calls resolve to static JSON reads — no runtime API calls during generation.\n\n## Next.js (App Router)\n\n### Setup\n\n```bash\npnpm add @contentrain/query\nnpx contentrain generate\n```\n\nRestart the Next.js dev server after initial generation.\n\n```bash\nnpx contentrain generate --watch &\nnpx next dev\n```\n\n### React Server Components\n\nSDK calls work directly in Server Components — no hooks, no client-side fetching:\n\n```tsx\n// app/blog/page.tsx\nimport { query } from '#contentrain'\n\nexport default async function BlogPage() {\n const posts = query('blog-post').locale('en').sort('publishedAt', 'desc').all()\n\n return (\n \n )\n}\n```\n\n### Dynamic routes with locale\n\n```tsx\n// app/[locale]/blog/[slug]/page.tsx\nimport { document } from '#contentrain'\n\ninterface Props {\n params: Promise<{ locale: string; slug: string }>\n}\n\nexport default async function PostPage({ params }: Props) {\n const { locale, slug } = await params\n const post = document('blog-article').locale(locale).bySlug(slug)\n\n return (\n
\n

{post.title}

\n
{post.body}
\n
\n )\n}\n```\n\n### Static params generation\n\n```tsx\nimport { query } from '#contentrain'\n\nexport async function generateStaticParams() {\n const posts = query('blog-post').locale('en').all()\n return posts.map(post => ({ slug: post.slug }))\n}\n```\n\n### Client Components\n\nClient Components cannot import `#contentrain` directly. Fetch data in a Server Component and pass as props:\n\n```tsx\n// Server Component\nimport { singleton } from '#contentrain'\nimport HeroClient from './HeroClient'\n\nexport default async function HeroSection() {\n const hero = singleton('hero').locale('en').get()\n return \n}\n```\n\n### Build and deploy\n\n```bash\nnpx next build\n```\n\nFor full static export, set `output: 'export'` in `next.config.js`.\n\n## Astro\n\n### Setup\n\n```bash\npnpm add @contentrain/query\nnpx contentrain generate\n```\n\n```bash\nnpx contentrain generate --watch &\nnpx astro dev\n```\n\n### Page components\n\nSDK calls go in the frontmatter:\n\n```astro\n---\nimport { query } from '#contentrain'\n\nconst posts = query('blog-post').locale('en').sort('publishedAt', 'desc').all()\n---\n\n\n```\n\n### Dynamic routes\n\n```astro\n---\nimport { query } from '#contentrain'\n\nexport function getStaticPaths() {\n const posts = query('blog-post').locale('en').all()\n return posts.map(post => ({\n params: { slug: post.slug },\n props: { post }\n }))\n}\n\nconst { post } = Astro.props\n---\n\n
\n

{post.title}

\n
\n```\n\n### i18n with Astro built-in routing\n\n```astro\n---\nimport { dictionary } from '#contentrain'\n\nconst locale = Astro.currentLocale ?? 'en'\nconst t = dictionary('ui-labels').locale(locale).get()\n---\n\n\n

{t.welcome_message}

\n```\n\n### Islands architecture\n\nContent fetched in static Astro component, passed to interactive islands:\n\n```astro\n---\nimport { query } from '#contentrain'\nimport SearchWidget from '../components/SearchWidget.tsx'\n\nconst posts = query('blog-post').locale('en').all()\n---\n\n

Blog

\n\n```\n\n### Build and deploy\n\n```bash\nnpx astro build\n```\n\nOutput goes to `dist/`. Content is baked into the HTML — zero runtime dependencies.\n\n## SvelteKit\n\n### Setup\n\n```bash\npnpm add @contentrain/query\nnpx contentrain generate\n```\n\n```bash\nnpx contentrain generate --watch &\nnpx vite dev\n```\n\n### Server load functions\n\nContent loading belongs in `+page.server.ts` or `+layout.server.ts`:\n\n```ts\n// src/routes/blog/+page.server.ts\nimport { query } from '#contentrain'\nimport type { PageServerLoad } from './$types'\n\nexport const load: PageServerLoad = () => {\n const posts = query('blog-post').locale('en').sort('publishedAt', 'desc').all()\n return { posts }\n}\n```\n\n```svelte\n\n\n\n\n```\n\n### Dynamic routes\n\n```ts\n// src/routes/blog/[slug]/+page.server.ts\nimport { document } from '#contentrain'\nimport { error } from '@sveltejs/kit'\nimport type { PageServerLoad } from './$types'\n\nexport const load: PageServerLoad = ({ params }) => {\n const post = document('blog-article').locale('en').bySlug(params.slug)\n if (!post) throw error(404, 'Post not found')\n return { post }\n}\n```\n\n### Prerendering\n\n```ts\n// src/routes/blog/[slug]/+page.server.ts\nimport { query } from '#contentrain'\n\nexport const prerender = true\n\nexport function entries() {\n const posts = query('blog-post').locale('en').all()\n return posts.map(post => ({ slug: post.slug }))\n}\n```\n\n### Build and deploy\n\n```bash\nnpx vite build\n```\n\nUse the appropriate SvelteKit adapter for your platform.\n\n## Vue 3 + Vite (SPA)\n\n### Setup\n\n```bash\npnpm add @contentrain/query\nnpx contentrain generate\n```\n\nRestart the Vite dev server after generation.\n\n### SFC usage\n\nIn a Vue SPA, direct imports work in any component:\n\n```vue\n\n\n\n```\n\n## React + Vite (SPA)\n\n### Setup\n\n```bash\npnpm add @contentrain/query\nnpx contentrain generate\n```\n\nRestart Vite after generation.\n\n### Component usage\n\n```tsx\nimport { query, singleton, dictionary } from '#contentrain'\n\nexport function HomePage() {\n const hero = singleton('hero').locale('en').get()\n const labels = dictionary('ui-labels').locale('en').get()\n const posts = query('blog-post').locale('en').all()\n\n return (\n
\n

{hero.title}

\n \n
    \n {posts.map(post => (\n
  • {post.title}
  • \n ))}\n
\n
\n )\n}\n```\n\n## Expo / React Native\n\n### Setup\n\n```bash\npnpm add @contentrain/query\nnpx contentrain generate\n```\n\n### Bootstrap pattern\n\nExpo uses a CJS bootstrap with async initialization:\n\n```ts\n// app/content.ts\nlet clientPromise: Promise | null = null\n\nexport function getContentrainClient() {\n if (!clientPromise) {\n clientPromise = require('#contentrain').init()\n }\n return clientPromise\n}\n```\n\n```tsx\nimport { useEffect, useState } from 'react'\nimport { Text, View } from 'react-native'\nimport { getContentrainClient } from './content'\n\nexport function HomeScreen() {\n const [title, setTitle] = useState('')\n\n useEffect(() => {\n getContentrainClient().then((client) => {\n setTitle(client.singleton('hero').locale('en').get().title)\n })\n }, [])\n\n return (\n \n {title}\n \n )\n}\n```\n\n::: info\nCache the client promise. Do not initialize on every render. Re-run `contentrain generate` after content or model changes.\n:::\n\n## tsconfig.json Paths\n\nThe `#contentrain` import uses Node.js native subpath imports (defined in `package.json`). No tsconfig paths are needed for runtime resolution.\n\nFor TypeScript IDE support, ensure your `tsconfig.json` includes:\n\n```json\n{\n \"compilerOptions\": {\n \"module\": \"Node16\",\n \"moduleResolution\": \"Node16\"\n }\n}\n```\n\nOr with bundler resolution:\n\n```json\n{\n \"compilerOptions\": {\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Bundler\"\n }\n}\n```\n\nBoth resolve `#contentrain` from the `imports` field in `package.json` automatically.\n\n## SSR Considerations\n\n::: warning\n**Server-side only.** The SDK reads from the file system. In SSR frameworks (Nuxt, Next.js, SvelteKit), use it only in server contexts:\n\n- **Nuxt:** Nitro server routes, server middleware\n- **Next.js:** React Server Components, route handlers, `generateStaticParams`\n- **SvelteKit:** `+page.server.ts`, `+layout.server.ts`, `+server.ts`\n- **Astro:** Component frontmatter (server by default)\n\nFor client components, fetch data server-side and pass it as props.\n:::\n\n## Build Verification\n\nAfter setting up any framework, verify the build works:\n\n::: code-group\n\n```bash [Nuxt]\nnpx contentrain generate && npx nuxt build\n```\n\n```bash [Next.js]\nnpx contentrain generate && npx next build\n```\n\n```bash [Astro]\nnpx contentrain generate && npx astro build\n```\n\n```bash [SvelteKit]\nnpx contentrain generate && npx vite build\n```\n\n```bash [Vue/React Vite]\nnpx contentrain generate && npx vite build\n```\n\n:::\n\nIf the build succeeds with no import errors, the integration is working correctly.\n\n## Type Safety\n\nThe generated client includes full TypeScript types for every model:\n\n```ts\nimport { query } from '#contentrain'\n\n// Fully typed — IDE autocomplete for field names and return types\nconst posts = query('blog-post').locale('en').all()\nposts[0].title // string\nposts[0].author // Author type when using .include('author')\n```\n\nKeep the generator running in watch mode during development to stay in sync with model changes.\n\n## Deployment Flow\n\nAll frameworks share the same deployment pattern:\n\n1. Content changes are committed to Git via MCP tools\n2. Push triggers platform rebuild (Vercel, Netlify, Cloudflare Pages)\n3. Build runs, SDK reads `.contentrain/` content\n4. Static or server output is produced with embedded content\n\nNo external API dependency. No database. Content lives in Git.\n\n::: info Non-Web Platforms\nFor mobile (React Native, Expo, Flutter) and desktop apps that can't read from Git at build time, [Contentrain Studio](/studio) publishes merged content to a CDN — same JSON, delivered over HTTP.\n:::", + "description": "Setup and usage patterns for Nuxt, Next.js, Astro, SvelteKit, Vue, React, Expo, and any platform that reads JSON", + "order": 2, + "slug": "frameworks", + "title": "Framework Integration" +} diff --git a/.contentrain/client/data/docs-guides--http-transport.mjs b/.contentrain/client/data/docs-guides--http-transport.mjs new file mode 100644 index 0000000..d790d75 --- /dev/null +++ b/.contentrain/client/data/docs-guides--http-transport.mjs @@ -0,0 +1,6 @@ +export default { + "body": "# HTTP Transport\n\nThe MCP server ships two transports. The default — stdio — is for IDE integration. The HTTP transport serves tool calls at `POST /mcp` so agents running on a different machine can drive Contentrain operations against a project.\n\nTypical drivers:\n\n- **Contentrain Studio** — hosts an agent that talks to MCP over HTTP, backed by a GitHubProvider or GitLabProvider pointed at a team's content repo.\n- **CI runners** — deterministic content operations as part of a pipeline (scaffold, validate, submit).\n- **Remote agents** — any MCP client that wants to operate a Contentrain project without a local checkout.\n\nAll three tunnel the same tool surface (19 core + 5 media on media-capable providers) through the same `RepoProvider` contract. Which backend answers depends on how the server is wired — see [Providers & Transports](/guides/providers) for the capability matrix.\n\n## Starting the HTTP server\n\nThe simplest path is the CLI:\n\n```bash\ncontentrain serve --mcpHttp --authToken $(openssl rand -hex 32)\n```\n\nThis binds to `localhost:3333` by default, uses the current working directory as the project root, and wraps it in a `LocalProvider`. Flags:\n\n- `--port ` (`CONTENTRAIN_PORT`) — listen port\n- `--host ` (`CONTENTRAIN_HOST`) — bind address. Default `localhost`; set to `0.0.0.0` to accept remote connections\n- `--authToken ` (`CONTENTRAIN_AUTH_TOKEN`) — Bearer token required for every request\n- `--root ` (`CONTENTRAIN_PROJECT_ROOT`) — project root when not the cwd\n\nMCP tool calls land at `POST :/mcp`. Any other path returns 404.\n\n## Authentication\n\nWhen `--authToken` is set (or `CONTENTRAIN_AUTH_TOKEN` is exported), every request must carry `Authorization: Bearer `. Missing or mismatched tokens get `401 Unauthorized` before the MCP session initialises.\n\n```bash\ncurl -X POST http://localhost:3333/mcp \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -H \"Accept: application/json, text/event-stream\" \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"contentrain_describe_format\",\"arguments\":{}}}'\n```\n\nAuth is optional in dev — omit `--authToken` when binding to `localhost`. Production deployments should always set one.\n\n## Programmatic embedding\n\nCLI-wrapped HTTP always uses `LocalProvider`. To run HTTP against a remote provider (GitHub, GitLab) embed the server programmatically:\n\n```ts\nimport { createGitHubProvider } from '@contentrain/mcp/providers/github'\nimport { startHttpMcpServerWith } from '@contentrain/mcp/server/http'\n\nconst provider = await createGitHubProvider({\n auth: { type: 'pat', token: process.env.GITHUB_TOKEN! },\n repo: { owner: 'acme', name: 'site' },\n})\n\nconst handle = await startHttpMcpServerWith({\n provider,\n port: 3333,\n host: '0.0.0.0',\n authToken: process.env.MCP_BEARER_TOKEN,\n})\n\n// handle.url contains the fully-qualified URL, e.g. http://0.0.0.0:3333/mcp\n// handle.close() stops the server\n```\n\nThe same pattern works for `createGitLabProvider` with a `GitLabProvider`. Both require their respective optional peers (`@octokit/rest`, `@gitbeaker/rest`).\n\nMulti-tenant deployments (`{ resolveProvider }`) should also set `sessionFingerprint` to bind each MCP session to the tenant it was created for — follow-up requests whose fingerprint doesn't match the session's get `404` and the client re-initializes. See the [embedding guide](/guides/embedding-mcp#3a-http--per-request-provider-resolver-multi-tenant) for the full pattern.\n\nNote that the tool list is capability-aware: a session backed by a remote provider only advertises the tools it can actually run (no `init`/`scaffold`/`doctor`/`bulk`/`submit`/`merge`/branch lifecycle/normalize tools without a local worktree).\n\n## Deployment patterns\n\n### Studio MCP Cloud\n\nStudio MCP Cloud is the hosted version of the HTTP transport for connected Studio projects. It exposes:\n\n```text\nhttps://studio.contentrain.io/api/mcp/v1/{projectId}/mcp\n```\n\nAuthentication uses a project-scoped MCP Cloud API key:\n\n```http\nAuthorization: Bearer \n```\n\nStudio handles the gates around that endpoint: project matching, plan access, per-key rate limits, optional tool allowlists, monthly call quotas, usage metering, and branch reconciliation. External clients such as OpenAI Codex, Claude Desktop, Claude Code, Cursor, and custom MCP drivers connect to the same Streamable HTTP shape as a self-hosted `contentrain serve --mcpHttp` server.\n\n### Studio (hosted agent)\n\nStudio's agent builds a GitHubProvider or GitLabProvider per tenant, points it at the tenant's content repo, and talks to an embedded MCP server over HTTP+LocalProvider-style wiring but with a remote provider. Each session is ephemeral.\n\n### CI\n\nA GitHub Actions job can:\n\n1. Check out the repository\n2. `pnpm install`\n3. Start `contentrain serve --mcpHttp --authToken $CI_TOKEN &`\n4. Drive it with an MCP client (Claude, Cursor-headless, or a custom JSON-RPC client)\n5. Let `contentrain_submit` push the review branch\n\nBecause `contentrain serve --mcpHttp` uses `LocalProvider`, every tool — including normalize and submit — is available.\n\n### Remote agent\n\nAn agent running on a laptop can drive a Contentrain project that lives on a server by connecting to the server's HTTP MCP endpoint (over a VPN or behind a reverse proxy with TLS + Bearer auth). The agent sees the full tool surface; capability gates still apply based on the backing provider.\n\n## Capability gates over HTTP\n\nNot every tool works on every provider. A tool driven by an HTTP client against a remote provider returns a structured capability error when the required capability is missing:\n\n```json\n{\n \"error\": \"contentrain_scan requires local filesystem access.\",\n \"capability_required\": \"astScan\",\n \"hint\": \"This tool is unavailable when MCP is driven by a remote provider. Use a LocalProvider or the stdio transport.\"\n}\n```\n\nAgent drivers should treat `capability_required` as a retry signal — prompt the user to switch transports, or fall back to a local-checkout session for that specific tool.\n\n## Security notes\n\n- Never expose HTTP MCP without `--authToken` on a non-`localhost` bind.\n- Rotate tokens regularly; MCP does not support ACLs at the tool level, so a token is full project access.\n- When Studio connects, the token is managed per workspace — see the Studio docs for the rotation workflow.\n- All writes create feature branches from `contentrain`; the singleton source-of-truth branch is protected from direct pushes in team configurations.\n\n## Next steps\n\n- [Providers & Transports](/guides/providers) — deeper reference for each backend.\n- [MCP package docs](/packages/mcp) — tool catalogue and response shapes.\n- [Contentrain Studio](/studio) — the hosted surface that drives HTTP MCP.", + "description": "Run the Contentrain MCP server over HTTP for Studio, CI runners, and remote agent drivers — with Bearer authentication.", + "slug": "http-transport", + "title": "HTTP Transport" +} diff --git a/.contentrain/client/data/docs-guides--i18n.mjs b/.contentrain/client/data/docs-guides--i18n.mjs new file mode 100644 index 0000000..c635c5f --- /dev/null +++ b/.contentrain/client/data/docs-guides--i18n.mjs @@ -0,0 +1,7 @@ +export default { + "body": "# i18n Workflow\n\nContentrain handles internationalization through per-locale JSON files and dictionary models. Each locale gets its own file — any platform that reads JSON can load the right locale. The TypeScript SDK adds a convenience API, but the files are the source of truth.\n\n## How Contentrain Handles i18n\n\nEvery piece of content in Contentrain can be localized. The system stores one file per locale per model, with the same keys across all locales.\n\n### Locale configuration\n\nLocales are defined in `.contentrain/config.json`:\n\n```json\n{\n \"locales\": [\n { \"code\": \"en\", \"label\": \"English\", \"default\": true },\n { \"code\": \"tr\", \"label\": \"Turkish\" },\n { \"code\": \"de\", \"label\": \"German\" }\n ]\n}\n```\n\nThe default locale is the source of truth. All other locales are translations of the default.\n\n### Locale strategies\n\nEach model can use a different file organization strategy:\n\n| Strategy | File pattern | Best for |\n|---|---|---|\n| `file` (default) | `{model-id}/{locale}.json` | Most models |\n| `suffix` | `{model-id}.{locale}.json` | Single-directory layouts |\n| `directory` | `{locale}/{model-id}.json` | Locale-first organization |\n| `none` | `{model-id}/data.json` | Single-language projects |\n\nFor document models, the patterns use markdown files:\n\n| Strategy | File pattern |\n|---|---|\n| `file` | `{slug}/{locale}.md` |\n| `suffix` | `{slug}.{locale}.md` |\n| `directory` | `{locale}/{slug}.md` |\n\n## Dictionary Model for UI Strings\n\nDictionaries are the primary model for UI strings — button labels, form messages, navigation items, error messages.\n\n### Structure\n\nA dictionary is a flat key-value JSON file where all values are strings:\n\n```json\n{\n \"auth.login.button\": \"Sign in\",\n \"auth.login.error\": \"Invalid email or password\",\n \"auth.register.button\": \"Create account\",\n \"nav.home\": \"Home\",\n \"nav.blog\": \"Blog\",\n \"nav.pricing\": \"Pricing\",\n \"cta.get_started\": \"Get started free\",\n \"cta.learn_more\": \"Learn more\"\n}\n```\n\n::: tip\nKeys are semantic dot-separated addresses. Use a consistent naming convention: `{section}.{subsection}.{element}`. Keys are the identities — no `id` or `slug` field.\n:::\n\n### Creating a dictionary\n\n> \"Create a dictionary model called ui-labels for all UI strings\"\n\nThe agent creates the model and populates it with extracted strings. Each locale gets its own file:\n\n```\n.contentrain/content/ui/ui-labels/\n en.json # Source locale\n tr.json # Turkish translation\n de.json # German translation\n```\n\n### Querying dictionaries\n\n::: code-group\n\n```vue [Vue / Nuxt]\n\n```\n\n```tsx [React / Next.js]\nimport { dictionary } from '#contentrain'\n\n// All labels:\nconst labels = dictionary('ui-labels').locale('en').get()\n\n// Single key:\nconst loginBtn = dictionary('ui-labels').locale('en').get('auth.login.button')\n```\n\n```astro [Astro]\n---\nimport { dictionary } from '#contentrain'\n\nconst locale = Astro.currentLocale ?? 'en'\nconst t = dictionary('ui-labels').locale(locale).get()\n---\n\n\n```\n\n:::\n\n## Collection and Singleton Locale Files\n\nCollections and singletons also support per-locale files.\n\n### Collection example\n\nA `testimonials` collection with i18n enabled:\n\n```\n.contentrain/content/marketing/testimonials/\n en.json\n tr.json\n```\n\n`en.json`:\n```json\n{\n \"a1b2c3d4e5f6\": {\n \"author\": \"Jane Smith\",\n \"role\": \"CTO at Acme\",\n \"quote\": \"Contentrain transformed our content workflow\"\n },\n \"f6e5d4c3b2a1\": {\n \"author\": \"John Doe\",\n \"role\": \"Lead Developer\",\n \"quote\": \"Finally, content that developers can manage\"\n }\n}\n```\n\n`tr.json`:\n```json\n{\n \"a1b2c3d4e5f6\": {\n \"author\": \"Jane Smith\",\n \"role\": \"Acme CTO\",\n \"quote\": \"Contentrain içerik iş akışımızı dönüştürdü\"\n },\n \"f6e5d4c3b2a1\": {\n \"author\": \"John Doe\",\n \"role\": \"Baş Geliştirici\",\n \"quote\": \"Sonunda, geliştiricilerin yönetebileceği içerik\"\n }\n}\n```\n\n::: info\nEntry IDs are identical across locales. Only field values change. The `author` field may or may not be translated depending on context.\n:::\n\n### Singleton example\n\nA `hero-section` singleton:\n\n```\n.contentrain/content/marketing/hero-section/\n en.json\n tr.json\n```\n\n`en.json`:\n```json\n{\n \"title\": \"Build faster with AI-powered content\",\n \"subtitle\": \"Ship your next project in days, not weeks\",\n \"cta_text\": \"Get started free\"\n}\n```\n\n`tr.json`:\n```json\n{\n \"title\": \"Yapay zeka destekli içerikle daha hızlı geliştirin\",\n \"subtitle\": \"Projenizi haftalar değil günler içinde gönderin\",\n \"cta_text\": \"Ücretsiz başlayın\"\n}\n```\n\n## Translation Workflow\n\nThe complete i18n pipeline follows four steps: extract, copy locale, translate, generate.\n\n### Step 1. Extract content (Phase 1 of normalize)\n\nFirst, extract hardcoded strings into Contentrain content. See the [Normalize Flow](./normalize) guide for the full process.\n\nAfter extraction, you have content in the default locale (e.g., `en.json`).\n\n### Step 2. Copy locale\n\nCreate target locale files by copying the source:\n\n> \"Copy all English content to Turkish\"\n\nThe agent calls:\n\n```\ncontentrain_bulk({\n operation: \"copy_locale\",\n model: \"ui-labels\",\n source_locale: \"en\",\n target_locale: \"tr\"\n})\n```\n\nThis creates `tr.json` with the same structure as `en.json`, values still in English. Repeat for each model that needs translation.\n\n::: tip\nCopy locale preserves all keys and structure. The agent translates values in the next step without risk of missing keys.\n:::\n\n### Step 3. Translate\n\nAsk the agent to translate the copied content:\n\n> \"Translate all Turkish content. Keep the tone professional and concise. Preserve all placeholders like {name} and {count}.\"\n\nThe agent reads each `tr.json` file, translates every value, and saves using `contentrain_content_save`.\n\nFor dictionaries:\n```json\n{\n \"auth.login.button\": \"Giriş yap\",\n \"auth.login.error\": \"Geçersiz e-posta veya şifre\",\n \"auth.register.button\": \"Hesap oluştur\",\n \"nav.home\": \"Ana Sayfa\",\n \"nav.blog\": \"Blog\",\n \"nav.pricing\": \"Fiyatlandırma\"\n}\n```\n\n::: warning\nThe agent must preserve all placeholder tokens like `{name}`, `{count}`, `{price}` during translation. These are runtime-replaced values.\n:::\n\n### Step 4. Regenerate the SDK client\n\n```bash\nnpx contentrain generate\n```\n\nThe application now serves localized content from the new locale files.\n\n## Parameterized Templates\n\nContentrain supports `{placeholder}` syntax for dynamic values in strings:\n\n```json\n{\n \"welcome.greeting\": \"Welcome back, {name}!\",\n \"cart.item_count\": \"You have {count} items in your cart\",\n \"pricing.monthly\": \"{price}/month\"\n}\n```\n\nPlaceholders are preserved across all locales:\n\n```json\n{\n \"welcome.greeting\": \"Tekrar hoş geldin, {name}!\",\n \"cart.item_count\": \"Sepetinizde {count} ürün var\",\n \"pricing.monthly\": \"{price}/ay\"\n}\n```\n\n### Rendering parameterized strings\n\nThe SDK returns raw strings with placeholders. Your application replaces them at runtime:\n\n::: code-group\n\n```vue [Vue / Nuxt]\n\n\n\n```\n\n```tsx [React / Next.js]\nimport { dictionary } from '#contentrain'\n\nconst labels = dictionary('ui-labels').locale('en').get()\n\nfunction t(key: string, params: Record = {}) {\n let value = labels[key] ?? key\n for (const [k, v] of Object.entries(params)) {\n value = value.replace(`{${k}}`, v)\n }\n return value\n}\n\nexport function Welcome({ name }: { name: string }) {\n return

{t('welcome.greeting', { name })}

\n}\n```\n\n```astro [Astro]\n---\nimport { dictionary } from '#contentrain'\n\nconst labels = dictionary('ui-labels').locale(Astro.currentLocale ?? 'en').get()\n\nfunction t(key: string, params: Record = {}) {\n let value = labels[key] ?? key\n for (const [k, v] of Object.entries(params)) {\n value = value.replace(`{${k}}`, v)\n }\n return value\n}\n---\n\n

{t('welcome.greeting', { name: user.name })}

\n```\n\n:::\n\n::: tip\nIf you are already using a framework i18n library (vue-i18n, next-intl, paraglide), use the library's interpolation instead. Contentrain dictionaries can serve as the message source for these libraries.\n:::\n\n## Agent Prompt Examples\n\nHere are practical prompts for each stage of the i18n workflow:\n\n### Setting up locales\n\n> \"Add Turkish and German as supported locales in my Contentrain config\"\n\n### Creating dictionaries\n\n> \"Create a dictionary model for UI labels. Extract all button texts, form labels, and navigation items from my components.\"\n\n### Translating content\n\n> \"Translate all content in the ui-labels dictionary to Turkish. Use formal tone. Preserve all {placeholder} tokens.\"\n\n### Adding a new locale later\n\n> \"Add French as a new locale. Copy all existing content from English and translate it.\"\n\n### Reviewing translations\n\n> \"Check all Turkish translations for missing keys or untranslated values\"\n\nThe agent calls `contentrain_validate` which checks for i18n completeness — missing keys, mismatched placeholders, and untranslated values.\n\n## SDK Locale Selection\n\nEvery SDK query method accepts a `.locale()` call:\n\n```ts\nimport { query, singleton, dictionary, document } from '#contentrain'\n\n// Collections\nconst posts = query('blog-post').locale('tr').all()\n\n// Singletons\nconst hero = singleton('hero').locale('tr').get()\n\n// Dictionaries\nconst labels = dictionary('ui-labels').locale('tr').get()\n\n// Documents\nconst article = document('blog-article').locale('tr').bySlug('getting-started')\n```\n\n### Dynamic locale from request\n\n::: code-group\n\n```ts [Nuxt server route]\n// server/api/posts.get.ts\nimport { query } from '#contentrain'\nimport { getQuery } from 'h3'\n\nexport default defineEventHandler((event) => {\n const locale = getQuery(event).locale?.toString() ?? 'en'\n return query('blog-post').locale(locale).all()\n})\n```\n\n```tsx [Next.js Server Component]\n// app/[locale]/blog/page.tsx\nimport { query } from '#contentrain'\n\nexport default async function BlogPage({ params }: { params: Promise<{ locale: string }> }) {\n const { locale } = await params\n const posts = query('blog-post').locale(locale).all()\n return
    {posts.map(p =>
  • {p.title}
  • )}
\n}\n```\n\n```astro [Astro]\n---\nimport { query } from '#contentrain'\n\nconst locale = Astro.currentLocale ?? 'en'\nconst posts = query('blog-post').locale(locale).all()\n---\n```\n\n```ts [SvelteKit]\n// src/routes/[locale]/blog/+page.server.ts\nimport { query } from '#contentrain'\nimport type { PageServerLoad } from './$types'\n\nexport const load: PageServerLoad = ({ params }) => {\n const posts = query('blog-post').locale(params.locale).all()\n return { posts }\n}\n```\n\n:::\n\n## Content Update Workflow\n\nWhen dictionary content is added or updated:\n\n1. Edit content via MCP (`contentrain_content_save`)\n2. Re-run `npx contentrain generate` to update the static data modules\n3. Rebuild the application\n\nFor development, use watch mode:\n\n```bash\nnpx contentrain generate --watch\n```\n\nChanges to `.contentrain/` content trigger automatic client regeneration, which Vite/webpack hot-reloads into the running dev server.\n\n::: tip Team Translation Workflow\nFor teams with dedicated translators or multilingual reviewers, [Contentrain Studio](/studio) provides a visual interface for reviewing translations side-by-side, with approval workflows and version tracking.\n:::", + "description": "Complete internationalization workflow — per-locale files, dictionary models, translation pipelines, and parameterized templates", + "order": 3, + "slug": "i18n", + "title": "i18n Workflow" +} diff --git a/.contentrain/client/data/docs-guides--normalize.mjs b/.contentrain/client/data/docs-guides--normalize.mjs new file mode 100644 index 0000000..4ffc776 --- /dev/null +++ b/.contentrain/client/data/docs-guides--normalize.mjs @@ -0,0 +1,7 @@ +export default { + "body": "# Normalize Flow\n\nThe normalize flow is Contentrain's **primary value proposition** — the fastest path from \"500 hardcoded strings scattered across my codebase\" to \"structured, translatable, manageable content.\" It runs in three phases: **Extract**, **Reuse**, and **Translate**.\n\n::: warning LocalProvider required\nNormalize (`contentrain_scan` and `contentrain_apply`) requires local disk access — AST scanners walk the source tree and patch files in place. It runs only on a `LocalProvider` (stdio or HTTP+LocalProvider).\n\nRemote providers (`GitHubProvider`, `GitLabProvider`) reject these calls with `capability_required: astScan / sourceRead / sourceWrite`. Run normalize in a local checkout, then push the extracted content branch. See [Providers & Transports](/guides/providers) for the capability matrix.\n:::\n\n## The Problem\n\nA typical SaaS landing page has 40-60 components with 300-800 hardcoded strings. Nobody notices until someone asks for a second language or a copy change across 12 pages.\n\n```vue\n\n```\n\nThese strings are scattered across dozens of files. Translating means grep-and-replace. Updating copy means hunting through templates. There is no single source of truth, no way to hand off content to a non-developer, and no translation path that doesn't involve touching every component.\n\n**The normalize flow extracts all of this in minutes, not days.** The agent scans your components, classifies every string, creates structured content models, and patches your source files — all reviewable, all through Git.\n\n## Three Phases Overview\n\n| Phase | What happens | Source files modified? | Branch pattern |\n|---|---|---|---|\n| 1. Extract | Strings pulled into `.contentrain/content/` | No | `cr/normalize/extract/{ts}` |\n| 2. Reuse | Source files patched to reference content | Yes | `cr/normalize/reuse/{model}/{ts}` |\n| 3. Translate | Content copied to new locales and translated | No | Standard content branch |\n\n::: tip\nPhase 1 is valuable on its own. Extracted content can be managed in [Contentrain Studio](/studio), translated, and published without touching source code.\n:::\n\n## Phase 1: Extract\n\nPull hardcoded strings from your codebase into Contentrain content files.\n\n### Step 1. Check project state\n\nAsk your agent to verify the project is initialized:\n\n> \"Check if Contentrain is initialized and show me the project status\"\n\nThe agent calls `contentrain_status` to confirm `.contentrain/` exists, locales are configured, and there are no pending changes.\n\n### Step 2. Build the project graph\n\nThe agent needs to understand your project structure before scanning:\n\n> \"Scan my project structure so you understand the component hierarchy\"\n\nThe agent calls `contentrain_scan(mode: \"graph\")` to build an import/component dependency graph. This reveals which components are shared, which are page-specific, and how they relate.\n\n### Step 3. Find candidate strings\n\nNow scan for hardcoded user-visible strings:\n\n> \"Find all hardcoded strings in my landing page components\"\n\nThe agent calls `contentrain_scan(mode: \"candidates\")` iteratively, file by file. Each scan returns candidates with file paths, line numbers, string values, and context.\n\n### Step 4. Evaluate candidates\n\nThis is where agent intelligence matters. The agent filters, classifies, and structures the candidates:\n\n- **Filters false positives:** CSS values, technical identifiers, import paths, variable names, log messages\n- **Assigns domains:** Groups strings by domain (`marketing`, `ui`, `blog`)\n- **Determines model types:**\n\n| Model kind | Use when | Example |\n|---|---|---|\n| Dictionary | Short UI strings, labels, buttons | `\"Get started\"`, `\"Submit\"` |\n| Singleton | Page-specific structured content | Hero section with title + subtitle + CTA |\n| Collection | Repeating items with shared schema | Testimonials, FAQs, feature cards |\n| Document | Long-form with markdown body | Blog posts, documentation pages |\n\n### Step 5. Write the normalize plan\n\nThe agent writes `.contentrain/normalize-plan.json`:\n\n```json\n{\n \"version\": 1,\n \"status\": \"pending\",\n \"created_at\": \"2026-03-16T12:00:00.000Z\",\n \"agent\": \"claude\",\n \"scan_stats\": {\n \"files_scanned\": 12,\n \"raw_strings\": 87,\n \"candidates_sent\": 34,\n \"extracted\": 18,\n \"skipped\": 16\n },\n \"models\": [\n {\n \"id\": \"hero-section\",\n \"kind\": \"singleton\",\n \"domain\": \"marketing\",\n \"i18n\": true,\n \"fields\": {\n \"title\": { \"type\": \"string\", \"required\": true },\n \"subtitle\": { \"type\": \"string\" },\n \"cta_text\": { \"type\": \"string\" }\n }\n }\n ],\n \"extractions\": [\n {\n \"value\": \"Build faster with AI-powered content\",\n \"file\": \"src/components/Hero.vue\",\n \"line\": 3,\n \"model\": \"hero-section\",\n \"field\": \"title\"\n }\n ]\n}\n```\n\n### Step 6. Review in Serve UI\n\nStart the review UI:\n\n```bash\nnpx contentrain serve\n```\n\nNavigate to `http://localhost:3333/normalize` to visually inspect the plan. You can:\n\n- Review each extraction grouped by model\n- See the original source location for each string\n- Preview the patch changes\n- **Approve** or **Reject** the plan\n\n::: warning\nNormalize operations always use the review workflow. The agent never auto-merges normalize branches.\n:::\n\n### Step 7. Apply extraction\n\nAfter approval, the agent executes the extraction:\n\n> \"Apply the normalize plan\"\n\nThe agent calls `contentrain_apply(mode: \"extract\", dry_run: true)` first to preview, then `contentrain_apply(mode: \"extract\", dry_run: false)` to execute.\n\nThis creates model definitions and content files in `.contentrain/` on a dedicated branch. **Source files are NOT modified.**\n\n### Step 8. Validate and submit\n\n> \"Validate the extracted content and submit for review\"\n\nThe agent calls `contentrain_validate` then `contentrain_submit` to push the branch.\n\n### Step 9. Merge Phase 1 branch\n\nBefore starting Phase 2, the extraction branch must be merged:\n\n- **Browser:** `http://localhost:3333/branches` → click Merge\n- **MCP Tool:** The agent calls `contentrain_merge(branch: \"cr/normalize/extract/...\", confirm: true)`\n- **Git platform:** Create PR → review → merge\n\n## Phase 2: Reuse\n\nReplace hardcoded strings in source files with content references. Start only after Phase 1 is reviewed and merged.\n\n### Step 1. Choose scope\n\nProcess one model at a time to keep diffs small:\n\n> \"Replace the hero section hardcoded strings with content references\"\n\n### Step 2. Determine replacement patterns\n\nThe agent determines the correct expression based on your framework:\n\n::: code-group\n\n```vue [Vue / Nuxt]\n\n

Build faster with AI-powered content

\n\n\n

{{ $t('hero.title') }}

\n```\n\n```tsx [React / Next.js]\n// Before\n

Build faster with AI-powered content

\n\n// After\n

{t('hero.title')}

\n```\n\n```astro [Astro]\n---\nimport { dictionary } from '#contentrain'\nconst t = dictionary('ui-labels').locale('en').get()\n---\n\n\n

Build faster with AI-powered content

\n\n\n

{t.hero_title}

\n```\n\n```svelte [SvelteKit]\n\n

Build faster with AI-powered content

\n\n\n

{$t('hero.title')}

\n```\n\n:::\n\n::: info\nThe agent determines the replacement expression based on your tech stack. MCP only does exact string replacement — it is framework-agnostic.\n:::\n\n### Step 3. Preview the patch\n\nThe agent runs a dry-run first:\n\n> \"Show me what the source file changes will look like\"\n\nThe agent calls `contentrain_apply(mode: \"reuse\", scope: { model: \"hero-section\" }, patches: [...], dry_run: true)` and presents the diff.\n\n### Step 4. Apply the patch\n\nAfter your confirmation:\n\n> \"Apply the reuse patches\"\n\nThe agent calls `contentrain_apply(mode: \"reuse\", ..., dry_run: false)`. This patches source files and creates a `cr/normalize/reuse/hero-section/{timestamp}` branch.\n\n### Step 5. Validate, submit, and repeat\n\n> \"Validate and submit, then move to the next model\"\n\nRepeat for each model until all extracted content is referenced in source code.\n\n## Phase 3: Translate\n\nAfter extraction and reuse are complete, add new languages.\n\n### Step 1. Copy locale\n\n> \"Copy all English content to Turkish\"\n\nThe agent calls:\n\n```\ncontentrain_bulk({\n operation: \"copy_locale\",\n model: \"ui-labels\",\n source_locale: \"en\",\n target_locale: \"tr\"\n})\n```\n\nThis creates `tr.json` files with the same keys as `en.json`, values still in English.\n\n### Step 2. Translate\n\n> \"Translate all Turkish content, keeping the tone professional\"\n\nThe agent translates each value while preserving keys, placeholders, and formatting.\n\n### Step 3. Regenerate the SDK client\n\n```bash\nnpx contentrain generate\n```\n\nThe application now serves localized content.\n\n## When Studio Enters\n\nNormalize is the wedge, not the final surface.\n\nUse local AI packages first when you need to scan code, extract hardcoded strings, patch source files, and validate everything in git. Move into [Contentrain Studio](/studio) when the same structured content now needs:\n\n- authenticated review and approval\n- editor, reviewer, and viewer roles\n- web-based media, forms, and conversation workflows\n- CDN delivery and API distribution for other clients\n\nThe package bridge looks like this:\n\n- `@contentrain/mcp` handles deterministic extract and reuse locally\n- `@contentrain/rules` and `@contentrain/skills` keep agent behavior consistent\n- `@contentrain/query` consumes the resulting content in apps\n- Studio becomes the team-facing web surface on top of the same `.contentrain/` contract\n\nSee the full [Ecosystem Map](/ecosystem) and the Studio docs [Ecosystem page](https://docs.contentrain.io/guide/ecosystem).\n\n## SDK Direct Import Pattern\n\nInstead of using an i18n library, you can import content directly via the SDK:\n\n::: code-group\n\n```vue [Vue]\n\n\n\n```\n\n```tsx [React]\nimport { singleton } from '#contentrain'\n\nexport function Hero() {\n const hero = singleton('hero-section').locale('en').get()\n return (\n
\n

{hero.title}

\n

{hero.subtitle}

\n
\n )\n}\n```\n\n```astro [Astro]\n---\nimport { singleton } from '#contentrain'\n\nconst hero = singleton('hero-section').locale('en').get()\n---\n\n

{hero.title}

\n

{hero.subtitle}

\n```\n\n:::\n\n## Real Example: Normalizing a Vue Landing Page\n\nHere is a complete walkthrough normalizing a Vue 3 landing page.\n\n### Before: hardcoded strings everywhere\n\n```vue\n\n\n```\n\n### Agent prompt sequence\n\n1. > \"Scan my project and find all hardcoded strings in the landing page components\"\n\n2. > \"Classify them into appropriate models — use a singleton for the hero, a dictionary for button labels\"\n\n3. > \"Write the normalize plan and open the review UI\"\n\n4. After approval in UI: > \"Apply the extraction\"\n\n5. After merge: > \"Now replace the hardcoded strings with vue-i18n references\"\n\n6. After merge: > \"Copy English content to Turkish and translate it\"\n\n### After: clean, manageable content\n\n```vue\n\n\n```\n\nContent now lives in `.contentrain/content/marketing/hero-section/en.json`:\n\n```json\n{\n \"title\": \"Build faster with AI-powered content\",\n \"subtitle\": \"Ship your next project in days, not weeks. Contentrain manages your content so you can focus on code.\"\n}\n```\n\nAnd UI labels in `.contentrain/content/ui/cta-labels/en.json`:\n\n```json\n{\n \"cta.get_started\": \"Get started free\",\n \"cta.view_docs\": \"View documentation\"\n}\n```\n\n## Why Normalize is the Starting Point\n\nMost developers discover Contentrain through normalize. The pain is immediate and universal:\n\n- You've been vibe-coding with AI for weeks — 50+ generated files, strings everywhere\n- The founder asks for Turkish. Or the marketer wants to change the CTA on 12 pages.\n- You realize there's no content layer — just hardcoded text in components\n\nNormalize solves this in minutes. And once your content is extracted and structured, the rest of Contentrain's value (SDK queries, i18n, Studio review, CDN delivery) becomes available naturally.\n\n```\nnormalize → content exists → SDK queries work → i18n is possible → Studio review makes sense\n```\n\n::: tip Ready for Team Collaboration?\nAfter extracting content, connect your project to [Contentrain Studio](/studio) for team review, CDN delivery, and collaboration:\n\n```bash\ncontentrain studio login\ncontentrain studio connect\n```\n\nSee [CLI Studio Integration](/packages/cli#connecting-a-repository) for the full setup flow.\n:::\n\n## Important Rules\n\n::: warning\n- **Always dry-run before apply.** Both extract and reuse require a preview step.\n- **Normalize branches always use review mode.** Never auto-merge.\n- **Phase 2 requires Phase 1 to be merged.** Content must exist before patching source.\n- **Process reuse one model at a time.** Keep diffs small and reviewable.\n- **MCP is framework-agnostic.** The agent provides all stack-specific replacement expressions.\n- **Do not change component structure.** Only replace string literals with content references.\n:::", + "description": "Extract hardcoded strings from your codebase, replace them with content references, and translate — a complete three-phase guide with real examples", + "order": 1, + "slug": "normalize", + "title": "Normalize Flow" +} diff --git a/.contentrain/client/data/docs-guides--providers.mjs b/.contentrain/client/data/docs-guides--providers.mjs new file mode 100644 index 0000000..91b3fbb --- /dev/null +++ b/.contentrain/client/data/docs-guides--providers.mjs @@ -0,0 +1,6 @@ +export default { + "body": "# Providers & Transports\n\nContentrain MCP runs the same tool surface (19 core + 5 media on media-capable providers) over three backends:\n\n- **LocalProvider** — simple-git + a temporary worktree on your disk. Default for `npx contentrain serve --stdio` and the HTTP transport when driven by the CLI.\n- **GitHubProvider** — Octokit over the GitHub Git Data + Repos APIs. No clone, no worktree.\n- **GitLabProvider** — gitbeaker over the GitLab REST API. No clone, no worktree. Works with gitlab.com and self-hosted CE / EE.\n\nAll three implement the same `RepoProvider` contract from `@contentrain/types`. Tool handlers route through the contract, never the concrete provider, so a change of backend never changes the tool surface an agent sees.\n\nBitbucket support is on the roadmap — see the README for the current status.\n\n## Transport matrix\n\n| Transport | Typical driver | Provider used | Notes |\n|---|---|---|---|\n| `stdio` | Claude Code, Cursor, Windsurf, any MCP client | LocalProvider | Ships inside `contentrain` CLI (`contentrain serve --stdio`) |\n| HTTP (`POST /mcp`) with LocalProvider | Local CI runner, Studio when pointed at a working tree | LocalProvider | `contentrain serve --mcpHttp --authToken …` |\n| HTTP with GitHubProvider | Studio's hosted agent, CI against a GitHub repo | GitHubProvider | Embedders construct the provider with `createGitHubProvider({ auth, repo })` |\n| HTTP with GitLabProvider | Studio's hosted agent, CI against a GitLab repo | GitLabProvider | Embedders construct the provider with `createGitLabProvider({ auth, project })` |\n\n## Capability matrix\n\nSome tools need more than a git provider can offer — normalize has to walk your source tree with an AST parser, submit has to invoke `git push`. Each provider advertises a capability set; tools gate on the capabilities they need and reject over HTTP with a uniform `capability_required` error when the active provider can't satisfy them.\n\n| Capability | LocalProvider | GitHubProvider | GitLabProvider | Tools that require it |\n|---|---|---|---|---|\n| `localWorktree` | ✓ | — | — | `validate --fix`, `submit`, `merge`, `branch_list`, `branch_delete` |\n| `sourceRead` | ✓ | — | — | `apply` (extract mode) |\n| `sourceWrite` | ✓ | — | — | `apply` (reuse mode) |\n| `astScan` | ✓ | — | — | `scan` |\n| `pushRemote` | ✓ | ✓ | ✓ | `submit` |\n| `branchProtection` | — | ✓ | ✓ | merge fallback detection |\n| `pullRequestFallback` | — | ✓ | ✓ | merge fallback creation |\n\nA separate requirement — a local `projectRoot` on disk, not a capability flag — gates `init`, `scaffold`, `doctor`, and `bulk`. Tool listing is capability-aware: `tools/list` only advertises tools the resolved provider + `projectRoot` pair can satisfy, so a remote-provider session simply doesn't show the tools it couldn't run (see [MCP Tools](/packages/mcp) and `TOOL_REQUIREMENTS` in `@contentrain/mcp/tools/availability`).\n\nThe five `contentrain_media_*` tools gate on a separate optional facet, `RepoProvider.media` (an object, not a capability flag). They appear only on providers that expose a media stack — Studio MCP Cloud — and never on Local / GitHub / GitLab.\n\nRead-only tools (`status`, `describe`, `describe_format`, `content_list`, `validate` without `--fix`) work on every provider. Write tools (`content_save`, `content_delete`, `model_save`, `model_delete`) work over any provider too — a remote provider posts the changes as a single atomic commit and always returns `action: pending-review` so Studio (or whoever orchestrates the server) drives the merge.\n\n`content_list` with `resolve: true` requires local filesystem access today because relation hydration walks other models' content files. The reader-backed path rejects it with a descriptive error.\n\n## When to use which provider\n\n- **`LocalProvider`** — Day-to-day development from an IDE. Offline-capable, zero API keys, full source-tree access for normalize.\n- **`GitHubProvider`** — CI-driven content operations, Studio's hosted agent, or any automation that should push directly to a GitHub repository without a clone. Requires `@octokit/rest` (optional peer dependency) and a personal access token or GitHub App installation.\n- **`GitLabProvider`** — Same as above for GitLab (SaaS or self-hosted). Requires `@gitbeaker/rest` (optional peer dependency) and a PAT / OAuth / job token.\n\nThe choice is operational, not commercial. All three providers live in MIT; enterprise features are on top of Contentrain Studio, not behind provider gates. See [Ecosystem Map](/ecosystem) for the full package-to-product relationship.\n\n## Installation\n\nBase install:\n\n```bash\npnpm add @contentrain/mcp\n```\n\nRemote provider peers — install only when the backend is used:\n\n```bash\n# GitHub\npnpm add @octokit/rest\n\n# GitLab\npnpm add @gitbeaker/rest\n```\n\nstdio + `LocalProvider` flows (the default) need neither peer.\n\n## Wiring a remote provider\n\n```ts\nimport { createServer } from '@contentrain/mcp/server'\nimport { createGitHubProvider } from '@contentrain/mcp/providers/github'\n// or: import { createGitLabProvider } from '@contentrain/mcp/providers/gitlab'\nimport { startHttpMcpServerWith } from '@contentrain/mcp/server/http'\n\nconst provider = await createGitHubProvider({\n auth: { type: 'pat', token: process.env.GITHUB_TOKEN! },\n repo: { owner: 'acme', name: 'site' },\n})\n\nconst handle = await startHttpMcpServerWith({\n provider,\n port: 3333,\n authToken: process.env.MCP_BEARER_TOKEN,\n})\n\nconsole.log(`MCP server at ${handle.url}`)\n```\n\nThe server is MCP-compliant — any MCP client (including Studio) can talk to it over Streamable HTTP.\n\n## Next steps\n\n- See the [HTTP Transport guide](/guides/http-transport) for deployment patterns, auth, and the Studio use case.\n- The [MCP package reference](/packages/mcp) documents every tool's parameters and return shape.\n- [Normalize Flow](/guides/normalize) explains why normalize is local-only.", + "description": "How Contentrain MCP's provider-agnostic engine works — LocalProvider, GitHubProvider, GitLabProvider — and which capabilities each exposes.", + "slug": "providers", + "title": "Providers & Transports" +} diff --git a/.contentrain/client/data/docs-guides--serve-ui.mjs b/.contentrain/client/data/docs-guides--serve-ui.mjs new file mode 100644 index 0000000..8a5ff91 --- /dev/null +++ b/.contentrain/client/data/docs-guides--serve-ui.mjs @@ -0,0 +1,7 @@ +export default { + "body": "# Serve UI\n\nContentrain Serve is a local web UI that acts as the bridge between your AI agent and your review process. It provides monitoring, approval, and prompt surfaces for all content operations.\n\n## What Serve UI Is\n\nServe UI is a **monitoring + approval surface**, not an editor. The philosophy:\n\n- **Monitoring:** Browse models, content, validation results, history, and branches — read-only\n- **Approval:** Approve or reject normalize plans, merge or delete branches — human decisions\n- **Agent prompts:** Every page shows copyable prompts that you paste into your AI agent to trigger actions\n\n::: info\nAll mutations (create, edit, delete, scan, normalize, fix) are **agent-driven via MCP tools**. The UI never triggers these directly. You review what the agent produced and approve or reject it.\n:::\n\n## Starting Serve\n\nFrom your project root (where `.contentrain/` lives):\n\n```bash\nnpx contentrain serve\n```\n\nThe server starts on `http://localhost:3333` by default.\n\n### Options\n\n| Flag | Default | Description |\n|---|---|---|\n| `--port` | `3333` | Port number |\n| `--host` | `localhost` | Host address |\n| `--open=false` | `true` | Prevent auto-opening browser |\n| `--demo` | disabled | Temporary demo project (no setup required) |\n| `--stdio` | disabled | MCP stdio transport for IDE integration (no web UI) |\n| `--mcpHttp` | disabled | MCP HTTP transport at `POST /mcp` (secure-by-default Bearer auth) |\n| `--authToken` | — | Bearer token required on non-localhost HTTP binds |\n\n### Check if already running\n\nBefore starting a new instance:\n\n```bash\nlsof -ti:3333\n```\n\nIf a process is running, serve is already up — navigate directly to the URL.\n\n## Pages Overview\n\n### Dashboard (`/`)\n\nThe landing page shows a project overview:\n\n- Total models and entries\n- Content status breakdown (draft, published, in review)\n- Recent agent activity from `context.json`\n- Pending branches waiting for review\n- Quick links to each section\n\n**Agent prompt hint shown in UI:**\n\n> \"Show me the project status and any pending changes\"\n\n### Models (`/models`)\n\nBrowse all model definitions:\n\n- Model list with kind badges (collection, singleton, dictionary, document)\n- Field schema details per model\n- Content path and locale strategy\n- Entry count per locale\n\n### Content (`/content`)\n\nBrowse and inspect content entries:\n\n- Filter by model, locale, and status\n- View entry details with all field values\n- See metadata (status, source, timestamps)\n- Locale switcher to compare translations side by side\n\n::: tip\nThe content page is read-only. To edit content, copy the agent prompt from the UI and paste it into your AI agent.\n:::\n\n### Validate (`/validate`)\n\nInspect validation results:\n\n- Schema compliance errors\n- i18n completeness warnings (missing keys, untranslated values)\n- Duplicate entry detection\n- Vocabulary alignment checks\n\n### Branches (`/branches`)\n\nManage content branches:\n\n- List all `cr/*` branches\n- See diff summary for each branch\n- **Merge** approved branches\n- **Delete** rejected branches\n- View commit history per branch\n- Click a branch to see a detailed preview with merge conflict detection\n\n### Branch Detail (`/branches/:name`)\n\nWhen you click a branch in the list, you see a detailed **merge preview** fetched from `/api/preview/merge`:\n\n- Fast-forward status (can the branch merge cleanly?)\n- Already-merged check (is this branch already in the content-tracking branch?)\n- Conflict detection (best-effort list of conflicting paths via `git merge-tree`)\n- Diff summary and file-by-file changes\n- Sync-warning panel surfacing any files the last selective sync skipped\n\nThis preview is generated without running the actual merge — a safe way to answer \"what happens if I approve this?\" before committing to the action.\n\n### Doctor (`/doctor`)\n\nProject health dashboard wrapping the `contentrain_doctor` MCP tool:\n\n- Environment checks (Node ≥22, git available)\n- `.contentrain/` structure validation\n- Model definition parsing\n- Orphan content detection\n- Branch pressure (warning at 50 `cr/*`, blocked at 80)\n- SDK client freshness (comparing mtimes)\n- Optional **usage analysis** toggle: unused keys, duplicate dictionary values, missing locale keys\n\nEach check renders with severity (error / warning / info). The \"Run\" button re-fetches `/api/doctor?usage=...`.\n\n**Agent prompt hint:**\n\n> \"Run a full project health check and fix any errors\"\n\n### Format Reference (`/format`)\n\nA read-only reference of how Contentrain stores content — renders the output of the `contentrain_describe_format` tool via `/api/describe-format`:\n\n- Models schema (JSON structure of `.contentrain/models/`)\n- Content layout (directory and file structure)\n- Metadata organization (SEO, entry-level metadata)\n- Vocabulary format\n- Locale strategies\n\nUseful as a living spec for developers and integrators who need the physical file format without reading docs.\n\n### Normalize (`/normalize`)\n\nThe normalize page is active when a normalize plan exists (`.contentrain/normalize-plan.json`):\n\n- **Extraction review panel:** Candidates grouped by model with field mappings\n- **Source trace panel:** Click any extraction to see its original file and line number\n- **Patch preview panel:** See the exact source file changes that will happen in Phase 2\n- **Approve & Apply** button: Executes the extraction\n- **Reject** button: Deletes the plan\n\nThis page appears only during the [normalize flow](/guides/normalize). When no plan exists, it shows a prompt to start normalization.\n\n## WebSocket Real-time Updates\n\nServe UI uses WebSocket connections for real-time updates. You do not need to manually refresh the page.\n\n### WebSocket Events\n\nThe serve server broadcasts the following events over the WebSocket connection:\n\n| Event | Emitted when | Payload |\n|---|---|---|\n| `connected` | Client connects | (none) |\n| `config:changed` | `.contentrain/config.json` changes | (none) |\n| `context:changed` | `.contentrain/context.json` changes | `{ context }` |\n| `model:changed` | A model definition is created/updated | `{ modelId }` |\n| `content:changed` | Content entries are created/updated | `{ modelId, locale }` |\n| `meta:changed` | SEO or model metadata is written | `{ modelId, entryId?, locale }` |\n| `normalize:plan-updated` | Normalize plan is created/updated/deleted | (none) |\n| `validation:updated` | Validation results change | (none) |\n| `branch:created` | A new `cr/*` branch is created | `{ branch }` |\n| `branch:merged` | A branch is merged into `contentrain` | `{ branch }` |\n| `branch:rejected` | A branch is deleted | `{ branch }` |\n| `branch:merge-conflict` | Branch merge fails | `{ branch, message }` |\n| `sync:warning` | Merge succeeds but skips dirty working-tree files | `{ branch, skippedCount }` |\n| `file-watch:error` | chokidar file watcher encounters an error | `{ message, timestamp }` |\n\n## HTTP API Surface\n\nThe serve backend exposes REST endpoints at `http://localhost:3333/api/*`. Key routes:\n\n| Route | Method | Purpose |\n|---|---|---|\n| `/api/status` | GET | Wraps `contentrain_status` |\n| `/api/capabilities` | GET | Provider + transport + branch health in one call |\n| `/api/doctor?usage=...` | GET | Wraps `contentrain_doctor` (optional usage analysis) |\n| `/api/describe-format` | GET | Wraps `contentrain_describe_format` |\n| `/api/describe/:modelId` | GET | Wraps `contentrain_describe` |\n| `/api/content/:modelId` | GET | List content entries |\n| `/api/content/:modelId/:entryId` | GET | Single entry read |\n| `/api/content/:modelId/:entryId/fix` | POST | Quick fix (content save) |\n| `/api/validate?fix=true` | GET | Wraps `contentrain_validate` |\n| `/api/branches` | GET | List `cr/*` branches |\n| `/api/branches/diff?name=cr/...` | GET | Branch diff against `contentrain` |\n| `/api/branches/approve` | POST | Approve & merge a branch |\n| `/api/branches/reject` | POST | Delete a branch |\n| `/api/branches/:name/sync-status` | GET | Cached sync warnings from last merge |\n| `/api/preview/merge?branch=cr/...` | GET | Side-effect-free merge preview (FF / conflicts / already-merged) |\n| `/api/history` | GET | Recent contentrain-related commits |\n| `/api/context` | GET | `.contentrain/context.json` |\n| `/api/normalize/*` | various | Scan / plan / approve / reject / apply / approve-branch |\n\nEvery write route validates its body through Zod schemas (`parseOrThrow`) before reaching the MCP tool layer.\n\n## Approval Flow\n\nThe typical workflow between agent and developer:\n\n1. **Agent creates content** via `contentrain_content_save` (or similar write tool), landing on a `cr/*` branch.\n2. **Developer reviews in UI** at `/branches/:name` — sees the merge preview, per-file diff, sync warnings if any.\n3. **Approve or Reject** — click **Merge** or **Delete**.\n4. **Agent detects the decision** by re-reading the filesystem/context state.\n\n::: warning\nFor normalize operations, the workflow always uses review mode. Even if the project is configured for auto-merge, normalize branches require explicit approval.\n:::\n\n## Workflow Configuration\n\nThe approval flow behavior is controlled by the workflow setting in `.contentrain/config.json`:\n\n```json\n{\n \"workflow\": \"review\"\n}\n```\n\n| Mode | Behavior |\n|---|---|\n| `review` | All content changes create branches for review |\n| `auto-merge` | Content changes merge into `contentrain` branch, baseBranch is advanced via update-ref |\n\n::: info\nNormalize operations always use review mode regardless of this setting.\n:::\n\n## IDE Integration (stdio mode)\n\nFor IDE-embedded use (VS Code, Cursor, Claude Code), serve can run in stdio mode:\n\n```bash\nnpx contentrain serve --stdio\n```\n\nThis starts the MCP server with stdio transport instead of the web UI.\n\n## HTTP MCP Transport\n\nFor Studio, CI, or remote agents, serve can expose MCP over Streamable HTTP:\n\n```bash\nnpx contentrain serve --mcpHttp --authToken $(openssl rand -hex 32)\n```\n\n**Secure-by-default:** binding to a non-localhost address (`0.0.0.0` / specific IPs) without `--authToken` (or `CONTENTRAIN_AUTH_TOKEN` env var) is a hard error — the HTTP MCP endpoint exposes full project write access.\n\nSee the [HTTP Transport guide](/guides/http-transport) for deployment patterns.\n\n## Troubleshooting\n\n### Port already in use\n\n```bash\nlsof -ti:3333 | xargs kill\n# or\nnpx contentrain serve --port 4444\n```\n\n### UI not updating\n\n1. Check that the serve process is still running\n2. Look for a `file-watch:error` banner at the top of the UI — this indicates the chokidar watcher stopped (e.g. OS inotify limit). Restart `contentrain serve` to reattach.\n3. Verify that `.contentrain/` files are being written (check `context.json` timestamp)\n\n### Content not appearing\n\nIf content created by the agent does not appear:\n\n1. Check if the content is on a feature branch (not yet merged into the `contentrain` branch)\n2. Navigate to the Branches page to see pending branches\n3. Click the branch for a detailed merge preview\n4. Merge the branch to make content visible on the Content page\n\n### Merge conflicts when approving\n\nWhen clicking **Merge** on a branch, the UI performs a real merge using the MCP `mergeBranch` helper. Conflicts can occur when:\n\n1. The branch and `contentrain` branch have diverged\n2. You've made manual edits to `.contentrain/` files outside the agent workflow\n\nThe UI surfaces the conflict message. Resolve it manually or ask the agent to create a fresh branch from the current `contentrain` state.\n\n## Beyond Local: Contentrain Studio\n\n`contentrain serve` is designed for single-developer, local workflows. When your team grows, [Contentrain Studio](/studio) extends this with:\n\n- **GitHub-connected review** — PRs created from agent branches, reviewable in a web UI\n- **Team roles** — invite editors and reviewers with scoped permissions\n- **Chat-first agent** — talk to your agent through a web interface with full MCP access\n- **Content CDN** — publish approved content for mobile and non-web platforms\n- **Audit trail** — track who created, reviewed, and approved every content change\n\nConnect your local project to Studio with [`contentrain studio connect`](/packages/cli#connecting-a-repository) — it detects your repo, installs the GitHub App, and creates the project in one interactive flow.", + "description": "Use the local review UI for monitoring content, approving changes, reviewing normalize plans, and managing branches", + "order": 4, + "slug": "serve-ui", + "title": "Serve UI" +} diff --git a/.contentrain/client/data/docs-packages--cli.mjs b/.contentrain/client/data/docs-packages--cli.mjs new file mode 100644 index 0000000..f123639 --- /dev/null +++ b/.contentrain/client/data/docs-packages--cli.mjs @@ -0,0 +1,7 @@ +export default { + "body": "# CLI\n\n[![npm version](https://img.shields.io/npm/v/contentrain)](https://www.npmjs.com/package/contentrain) [![npm downloads](https://img.shields.io/npm/dm/contentrain)](https://www.npmjs.com/package/contentrain)\n\nThe `contentrain` CLI is the human-facing companion to `@contentrain/mcp`. While MCP handles deterministic tool execution for AI agents, the CLI gives you direct terminal access to initialize projects, inspect health, generate typed SDK clients, review pending changes, and run the local review UI.\n\n## Why a CLI?\n\nYou might wonder: if the AI agent handles everything through MCP, why do you need a CLI?\n\n- **Bootstrapping** — `contentrain init` sets up `.contentrain/`, git hooks, and IDE rules before the agent is even connected\n- **Visibility** — `contentrain status` and `contentrain doctor` give you instant project health without asking an agent\n- **SDK generation** — `contentrain generate` creates the typed client your application code imports\n- **Review workflow** — `contentrain diff` and `contentrain serve` let you review agent-created content in a proper UI\n- **CI/CD integration** — `contentrain validate` runs in pipelines to catch schema violations before deploy\n\n::: tip Agent + CLI = Complete Workflow\nThe agent creates content through MCP tools. The CLI helps you verify, review, and consume that content. They are complementary, not redundant.\n:::\n\n## Install\n\nRun directly with `npx` (no install required):\n\n```bash\nnpx contentrain init\n```\n\nOr install globally:\n\n```bash\nnpm install -g contentrain\ncontentrain status\n```\n\nRequirements:\n- Node.js 22+\n- Git available in `PATH`\n\n## Global Options\n\nThese flags are available on every command:\n\n| Flag | Environment Variable | Description |\n|------|----------------------|-------------|\n| `--debug` | `CONTENTRAIN_DEBUG=1` | Enable debug logging to stderr (useful for troubleshooting) |\n\nExample: `contentrain --debug status` or `CONTENTRAIN_DEBUG=1 contentrain status`.\n\n## Commands\n\n| Command | Purpose |\n|---------|--------|\n| `contentrain init` | Initialize `.contentrain/`, git workflow, templates, and IDE rules |\n| `contentrain status` | Show project overview, models, branch pressure, and validation summary |\n| `contentrain doctor` | Check setup health, SDK freshness, orphan content, local branch limits, and remote `cr/*` count |\n| `contentrain validate` | Validate content against schemas, optionally create review-branch fixes |\n| `contentrain generate` | Generate `.contentrain/client/` and `#contentrain` package imports |\n| `contentrain describe` | Display full model schema and sample data |\n| `contentrain describe-format` | Show file format specification and storage conventions |\n| `contentrain scaffold` | Apply starter templates (blog, landing, docs, SaaS, ...) |\n| `contentrain diff` | Review and merge or reject pending `cr/*` branches interactively (deletes the remote copy on merge/reject) |\n| `contentrain merge ` | Merge one pending `cr/*` branch non-interactively (deletes the remote copy) |\n| `contentrain prune` | Delete merged `cr/*` branches locally and on the remote (drain the backlog) |\n| `contentrain setup` | Configure MCP server and AI rules for your IDE |\n| `contentrain skills` | Install, update, or list AI skills and rules for your IDE |\n| `contentrain serve` | Start the local review UI, the MCP stdio server, or the MCP HTTP server |\n| `contentrain studio connect` | Connect a repository to a Studio project |\n| `contentrain studio login` | Authenticate with Contentrain Studio |\n| `contentrain studio logout` | Log out from Studio |\n| `contentrain studio whoami` | Show current authentication status |\n| `contentrain studio status` | Show project overview from Studio |\n| `contentrain studio activity` | Show recent activity feed |\n| `contentrain studio usage` | Show workspace usage metrics |\n| `contentrain studio branches` | Manage remote content branches |\n| `contentrain studio cdn-init` | Set up CDN for content delivery |\n| `contentrain studio cdn-build` | Trigger a CDN rebuild |\n| `contentrain studio webhooks` | Manage webhooks |\n| `contentrain studio submissions` | Manage form submissions |\n\n---\n\n### `contentrain init`\n\nBootstraps a Contentrain project in your repository.\n\n```bash\ncontentrain init # Interactive\ncontentrain init --yes # Skip prompts\ncontentrain init --root /path # Different project root\n```\n\n| Flag | Description |\n|------|-------------|\n| `--yes` | Skip prompts, use defaults |\n| `--root ` | Project root path |\n\nCreates `.contentrain/config.json`, `.contentrain/models/`, `.contentrain/content/`, plus IDE rules and Agent Skills. Runs `git init` if not already a repo.\n\n---\n\n### `contentrain status`\n\n```bash\ncontentrain status\ncontentrain status --json # CI-friendly JSON\ncontentrain status --root /path\n```\n\nOutputs config, models, active `cr/*` branches and their health, validation summary, and last operation context.\n\n---\n\n### `contentrain doctor`\n\nRuns a health check on your project.\n\n```bash\ncontentrain doctor\ncontentrain doctor --usage # Analyze content key usage in source\ncontentrain doctor --json # CI-friendly JSON; exits non-zero on failures\n```\n\n| Flag | Description |\n|------|-------------|\n| `--usage` | Analyze unused keys, duplicate values, locale coverage gaps |\n| `--json` | Machine-readable output; non-zero exit on check failures |\n| `--root ` | Project root path |\n\nChecks: `.contentrain/config.json` shape, SDK client freshness (mtime comparison), orphan content, branch limits (warning at 50, blocked at 80), Node/git versions.\n\n---\n\n### `contentrain validate`\n\n```bash\ncontentrain validate\ncontentrain validate --fix # Auto-fix and create review branch\ncontentrain validate --interactive # Choose fixes interactively\ncontentrain validate --watch # Re-run on .contentrain/ changes (read-only)\ncontentrain validate --model blog-posts\ncontentrain validate --json # CI-friendly JSON\n```\n\n| Flag | Description |\n|------|-------------|\n| `--fix` | Auto-fix and create a `cr/fix/*` review branch |\n| `--interactive` | Choose which issues to fix interactively |\n| `--watch` | Re-run when `.contentrain/` changes (read-only polling mode) |\n| `--model ` | Validate one model instead of all |\n| `--json` | CI-friendly JSON |\n\n::: tip Watch mode for development\nRun `contentrain validate --watch` in one terminal alongside your editing workflow. It re-runs on `.contentrain/` changes without creating branches (read-only). Rapid feedback while editing content.\n:::\n\n---\n\n### `contentrain generate`\n\nGenerates the typed SDK client from model definitions.\n\n```bash\ncontentrain generate\ncontentrain generate --watch # Regen on model/content changes\ncontentrain generate --json # CI-friendly JSON\ncontentrain generate --cdnBaseUrl https://cdn.example.com/api/cdn/v1/ # bake a media() resolver\n```\n\nWrites `.contentrain/client/` (ESM, CJS, types + per-model data modules) and adds `#contentrain` subpath to `package.json`.\n\n| Flag | Description |\n|------|-------------|\n| `--watch` | Regenerate on model/content changes. |\n| `--json` | Emit the result as JSON (silences pretty output). |\n| `--cdnBaseUrl ` | Public media delivery base. Bakes a [`media()`](/packages/sdk#media-delivery-urls) resolver into the client that turns relative `media/...` paths into absolute URLs. Overrides `cdn.url` in [`config.json`](/reference/config). |\n| `--root ` | Project root path. |\n\n---\n\n### `contentrain describe `\n\n```bash\ncontentrain describe blog-post\ncontentrain describe blog-post --sample --locale en\ncontentrain describe blog-post --json\n```\n\nShows full schema, field types, stats, sample data, and import snippet.\n\n---\n\n### `contentrain describe-format`\n\n```bash\ncontentrain describe-format\ncontentrain describe-format --json\n```\n\nPrints the Contentrain content-format specification (directory structure, JSON formats, markdown conventions, locale strategies).\n\n---\n\n### `contentrain scaffold`\n\n```bash\ncontentrain scaffold # Interactive picker\ncontentrain scaffold --template blog\ncontentrain scaffold --template saas --locales en,tr,de --no-sample\ncontentrain scaffold --template blog --json\n```\n\nTemplates: `blog`, `landing`, `docs`, `ecommerce`, `saas`, `i18n`, `mobile`.\n\n---\n\n### `contentrain diff`\n\n```bash\ncontentrain diff\ncontentrain diff --json # Summary of pending cr/* branches for CI\n```\n\nInteractive (default) review of pending review branches. `--json` emits `{ branches: [{name, base, filesChanged, insertions, deletions, stat}] }` without entering the interactive loop.\n\n---\n\n### `contentrain merge `\n\n```bash\ncontentrain merge cr/content/faq/1234-abcd\ncontentrain merge cr/content/faq/1234-abcd --yes # Skip confirm (CI)\n```\n\nNon-interactive single-branch sibling of `contentrain diff`. Delegates to MCP's `mergeBranch` so dirty-file protections + selective sync warnings behave identically. After a successful merge it also deletes the branch's copy on the remote — best-effort, so an offline or permission failure only prints a warning. Opt out with `remoteBranchCleanup: false` in `config.json`.\n\n---\n\n### `contentrain prune`\n\n```bash\ncontentrain prune --dry-run # Preview merged cr/* branches (local + remote)\ncontentrain prune # Confirm, then delete them\ncontentrain prune --yes # Skip confirm (CI)\ncontentrain prune --json # Machine-readable output (mutates only with --yes)\n```\n\nDrains already-merged `cr/*` branches: local ones past their retention period plus every merged copy left on the remote. This is the operator-facing cleanup for backlogs the per-merge deletion could not remove — e.g. branches merged before remote cleanup shipped, or after an offline/permission failure. Merged detection uses the same ancestry + patch-id classification as the rest of the toolchain, so branches orphaned by a base-history rewrite are still recognised. Governed by `remoteBranchCleanup` in `config.json`; exits non-zero if any remote deletion fails.\n\n---\n\n### `contentrain setup`\n\n```bash\ncontentrain setup claude-code # or: cursor, vscode, windsurf, copilot\ncontentrain setup --all # Configure every detected IDE\n```\n\nWrites the correct `.mcp.json` (or equivalent) for the target IDE and installs AI rules/skills if absent. Merges into existing config without overwriting other MCP servers.\n\n---\n\n### `contentrain skills`\n\n```bash\ncontentrain skills # Install / refresh\ncontentrain skills --update # Force update\ncontentrain skills --list # Show installed status per IDE\n```\n\nInstalls 15 Agent Skills + essential rules across detected IDEs (Claude Code, Cursor, Windsurf, GitHub Copilot).\n\n---\n\n### `contentrain serve`\n\nThree roles: web UI, MCP stdio, MCP HTTP.\n\n#### Web UI (default)\n\n```bash\ncontentrain serve\ncontentrain serve --port 8080 --host 0.0.0.0\ncontentrain serve --demo # Temporary demo project, no setup required\n```\n\nServes REST endpoints (`/api/status`, `/api/content`, `/api/branches`, `/api/doctor`, `/api/describe-format`, `/api/preview/merge`, `/api/normalize/*`, `/api/capabilities`, `/api/branches/:name/sync-status`), a WebSocket stream (`meta:changed`, `file-watch:error`, `sync:warning`, `branch:*` events), and the bundled Vue UI.\n\n#### MCP stdio\n\n```bash\ncontentrain serve --stdio\n```\n\nFor IDE agents. Uses `LocalProvider`, so it serves the 19 core tools over stdio (the `contentrain_media_*` tools need a media-capable provider such as Studio MCP Cloud).\n\n#### MCP HTTP\n\n```bash\ncontentrain serve --mcpHttp --authToken $(openssl rand -hex 32)\ncontentrain serve --mcpHttp --port 3334 --host 0.0.0.0 --authToken $TOKEN\n```\n\n| Flag | Env var | Description |\n|------|---------|-------------|\n| `--mcpHttp` | `CONTENTRAIN_MCP_HTTP=true` | Enable HTTP MCP at `POST /mcp` |\n| `--port ` | `CONTENTRAIN_PORT` | Port (default 3333) |\n| `--host ` | `CONTENTRAIN_HOST` | Bind address (default `localhost`) |\n| `--authToken ` | `CONTENTRAIN_AUTH_TOKEN` | Required Bearer token |\n\n**Secure-by-default:** non-localhost binds (`0.0.0.0` or specific IPs) **hard-error** without `--authToken` / `CONTENTRAIN_AUTH_TOKEN`. This is deliberate (OWASP Secure-by-Default) — the HTTP MCP endpoint exposes full project write access.\n\n## Studio Integration\n\nThe `studio` command group connects the CLI to [Contentrain Studio](/studio).\n\n### Authentication\n\n```bash\ncontentrain studio login\ncontentrain studio login --provider github\ncontentrain studio login --url https://studio.example.com\ncontentrain studio whoami\ncontentrain studio logout\n```\n\nCredentials stored in `~/.contentrain/credentials.json` (mode `0o600`). Use `CONTENTRAIN_STUDIO_TOKEN` for CI.\n\n### Connecting a Repository {#connecting-a-repository}\n\n```bash\ncontentrain studio connect\ncontentrain studio connect --workspace ws-123\ncontentrain studio connect --json\n```\n\nInteractive flow: workspace → GitHub App install → repo detection → `.contentrain/` scan → project creation. Defaults are cached for subsequent `studio` commands.\n\n### Project Monitoring\n\n```bash\ncontentrain studio status # Branches + CDN + team\ncontentrain studio activity --limit 10 # Recent activity feed\ncontentrain studio usage # AI messages, storage, bandwidth\n```\n\n### Branch Management\n\n```bash\ncontentrain studio branches\n```\n\nList pending branches, interactively merge or reject.\n\n### CDN Setup & Delivery\n\n```bash\ncontentrain studio cdn-init # First-time CDN setup\ncontentrain studio cdn-build --wait # Trigger + wait for build\n```\n\n### Webhooks & Submissions\n\n```bash\ncontentrain studio webhooks\ncontentrain studio submissions --form contact-form\ncontentrain studio submissions --form contact-form --status pending\n```\n\nEach Studio command accepts `--workspace `, `--project `, and `--json` for scripting.\n\n---\n\n## Typical Workflow\n\n```bash\n# 1. Initialize (auto-configures MCP for detected IDEs)\ncontentrain init\n\n# 2. Or set up a specific IDE later\ncontentrain setup claude-code\n\n# 3. Inspect project health\ncontentrain status\ncontentrain doctor\n\n# 4. Let the agent create models and content via MCP...\n\n# 5. Generate the typed SDK client\ncontentrain generate\n\n# 6. Validate everything (optionally --watch during dev)\ncontentrain validate\n\n# 7. Review agent-created branches\ncontentrain diff\n\n# 8. Open the local review UI\ncontentrain serve\n```\n\n::: tip From Local to Team\nThe CLI covers single-developer workflows. When you need workspace/project management, role-based collaboration, visual diff review, media operations, and content CDN delivery, [Contentrain Studio](/studio) extends the same Git-native model with an open-core team web surface.\n:::\n\n## Related Pages\n\n- [MCP Tools](/packages/mcp) — The deterministic tool layer the CLI wraps\n- [Query SDK](/packages/sdk) — The generated client that `contentrain generate` produces\n- [Rules & Skills](/packages/rules) — Agent behavior policies installed by `contentrain init`\n- [Contentrain Studio](/studio) — Team workspace, review, media, and CDN operations", + "description": "Complete reference for the contentrain CLI — initialize projects, validate content, generate SDK clients, and manage review workflows from the terminal", + "order": 2, + "slug": "cli", + "title": "CLI" +} diff --git a/.contentrain/client/data/docs-packages--mcp.mjs b/.contentrain/client/data/docs-packages--mcp.mjs new file mode 100644 index 0000000..d0d0d94 --- /dev/null +++ b/.contentrain/client/data/docs-packages--mcp.mjs @@ -0,0 +1,7 @@ +export default { + "body": "# MCP Tools\n\n[![npm version](https://img.shields.io/npm/v/@contentrain/mcp)](https://www.npmjs.com/package/@contentrain/mcp) [![npm downloads](https://img.shields.io/npm/dm/@contentrain/mcp)](https://www.npmjs.com/package/@contentrain/mcp)\n\nContentrain's MCP (Model Context Protocol) package is the deterministic execution layer that sits between your AI agent and your filesystem. While the agent makes intelligent content decisions, `@contentrain/mcp` enforces consistent file operations, canonical serialization, and git-backed safety.\n\n## Why MCP?\n\nTraditional content management relies on APIs, dashboards, and manual workflows. Contentrain inverts this:\n\n- **Agent produces** content decisions (what to write, where, in what structure)\n- **MCP applies** deterministic filesystem and git operations (how to write it safely)\n- **Humans review** and merge through git workflows\n- **The system guarantees** schema, locale, and serialization consistency\n\nThis separation means your AI agent never directly touches files. Every write goes through MCP's validation, canonical serialization, and git transaction pipeline.\n\n::: tip Why not just let the agent write files?\nAgents are non-deterministic. The same prompt can produce different file formats, inconsistent JSON ordering, or broken git state. MCP is the deterministic guardrail that makes AI-generated content safe for production.\n:::\n\n## Install\n\n```bash\npnpm add @contentrain/mcp\n```\n\nRequirements:\n- Node.js 22+\n- Git available on the machine\n\nOptional parser support for higher-quality source scanning:\n- `@vue/compiler-sfc` — Vue SFC parsing\n- `@astrojs/compiler` — Astro component parsing\n- `svelte` — Svelte component parsing\n\n## Discovery\n\n`@contentrain/mcp` is published for both package-based and registry-based MCP discovery:\n\n- **npm package:** [`@contentrain/mcp`](https://www.npmjs.com/package/@contentrain/mcp)\n- **MCP Registry name:** `io.github.Contentrain/contentrain`\n- **Local server binary:** `contentrain-mcp`\n- **CLI stdio entrypoint:** `contentrain serve --stdio`\n- **Hosted remote endpoint:** Contentrain Studio MCP Cloud at `https://studio.contentrain.io/api/mcp/v1/{projectId}/mcp`\n\nUse the local stdio server when the agent should work against a checkout on your machine. Use Studio MCP Cloud when an external agent should operate a connected Studio project through a project-scoped API key.\n\n## Tool Catalog\n\nThe MCP server exposes **24 tools** — 19 core + 5 media — organized by function. Each tool includes [MCP annotations](https://spec.modelcontextprotocol.io/specification/2025-03-26/server/tools/#annotations) (`readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint: false` everywhere except `contentrain_media_ingest`, which fetches a caller-supplied URL server-side) so clients can distinguish safe reads from writes and destructive operations.\n\n::: info Capability-aware listing\n`tools/list` is filtered per session: tools whose requirements (local project root, provider capabilities) cannot be met are not registered at all. A local stdio server lists the 19 core tools; a remote-provider session lists the remote-safe subset plus — on media-capable providers like Studio MCP Cloud — the 5 `contentrain_media_*` tools. Core remote-safe subset — `status`, `describe`, `describe_format`, `model_save`, `model_delete`, `content_save`, `content_delete`, `content_list`, `validate`. See `TOOL_REQUIREMENTS` in `@contentrain/mcp/tools/availability`.\n:::\n\n| Tool | Title | Read-only | Destructive |\n|------|-------|-----------|-------------|\n| `contentrain_status` | Project Status | Yes | — |\n| `contentrain_describe` | Describe Model | Yes | — |\n| `contentrain_describe_format` | Describe Format | Yes | — |\n| `contentrain_doctor` | Project Health Report | Yes | — |\n| `contentrain_init` | Initialize Project | — | — |\n| `contentrain_scaffold` | Scaffold Template | — | — |\n| `contentrain_model_save` | Save Model | — | — |\n| `contentrain_model_delete` | Delete Model | — | **Yes** |\n| `contentrain_content_save` | Save Content | — | — |\n| `contentrain_content_delete` | Delete Content | — | **Yes** |\n| `contentrain_content_list` | List Content | Yes | — |\n| `contentrain_validate` | Validate Project | — | — |\n| `contentrain_submit` | Submit Branches | — | — |\n| `contentrain_merge` | Merge Branch | — | — |\n| `contentrain_branch_list` | List Branches | Yes | — |\n| `contentrain_branch_delete` | Delete Branch | — | **Yes** |\n| `contentrain_scan` | Scan Source Code | Yes | — |\n| `contentrain_apply` | Apply Normalize | — | — |\n| `contentrain_bulk` | Bulk Operations | — | — |\n| `contentrain_media_list` | List Media Assets | Yes | — |\n| `contentrain_media_get` | Get Media Asset | Yes | — |\n| `contentrain_media_ingest` | Ingest Media From URL | — | — |\n| `contentrain_media_update` | Update Media Metadata | — | — |\n| `contentrain_media_delete` | Delete Media Asset | — | **Yes** |\n\n### Detailed Reference\n\n### Read Tools (Safe, No Side Effects)\n\n| Tool | Purpose | Description |\n|------|---------|-------------|\n| `contentrain_status` | Project overview | Config, models, branch health, context, validation summary |\n| `contentrain_describe` | Model deep-dive | Full schema, sample data, field types for any model |\n| `contentrain_describe_format` | Format reference | File structure, JSON formats, markdown conventions, locale strategies |\n| `contentrain_doctor` | Health diagnostics | Setup validation, SDK freshness, orphan content, local branch limits, remote `cr/*` count, unused keys, missing translations |\n| `contentrain_content_list` | Read content | List and filter content entries with optional relation resolution |\n\n### Write Tools (Git-Backed, Branch-Isolated)\n\n| Tool | Purpose | Description |\n|------|---------|-------------|\n| `contentrain_init` | Bootstrap project | Creates `.contentrain/` structure, config, and git setup |\n| `contentrain_scaffold` | Apply templates | Blog, docs, landing page, or SaaS starter templates |\n| `contentrain_model_save` | Define schemas | Create or update model definitions with field types and constraints |\n| `contentrain_model_delete` | Remove models | Delete a model definition and its content |\n| `contentrain_content_save` | Write content | Save entries for any model kind (collection, singleton, dictionary, document) |\n| `contentrain_content_delete` | Remove content | Delete specific content entries |\n| `contentrain_validate` | Check & fix | Validate content against schemas, optionally auto-fix structural issues |\n| `contentrain_submit` | Push branches | Push `cr/*` review branches to remote, then lazily prune merged local + remote leftovers |\n| `contentrain_merge` | Merge branches | Merge a review-mode branch into contentrain locally (by exact branch or model; no external platform needed); deletes the branch's remote copy |\n| `contentrain_branch_list` | Inspect branches | List pending `cr/*` branches with merge status and branch-health pressure (`remote: true` adds a remote view + remote-only leftovers) |\n| `contentrain_branch_delete` | Clean up branches | Delete a stale/failed `cr/*` branch locally and on the remote (the contentrain branch is protected; supports remote-only deletion) |\n\n### Normalize Tools (Scan + Apply)\n\n| Tool | Purpose | Description |\n|------|---------|-------------|\n| `contentrain_scan` | Find hardcoded strings | Graph-based component scan with candidate detection |\n| `contentrain_apply` | Extract or reuse | Two-phase normalize: extract content or patch source files |\n| `contentrain_bulk` | Batch operations | Bulk locale copy, status updates, and deletes |\n\n### Media Tools (Provider Media Facet)\n\nA deterministic passthrough to the provider's optional media stack (`RepoProvider.media`) — registered **only when the provider exposes one** (e.g. Studio MCP Cloud). The flow: list assets → pick a `media/...` path → reference it via `contentrain_content_save` (normalized to absolute delivery URLs when `mediaBaseUrl` is set).\n\n| Tool | Purpose | Description |\n|------|---------|-------------|\n| `contentrain_media_list` | Discover assets | Search, tag filter, cursor pagination — returns storage paths + delivery URLs |\n| `contentrain_media_get` | Inspect one asset | Path, URL, mime, size, alt, tags, provider metadata |\n| `contentrain_media_ingest` | Add from URL | Provider fetches the URL server-side under its own SSRF/MIME/size policy (MCP never fetches; the only `openWorldHint: true` tool) |\n| `contentrain_media_update` | Edit metadata | Alt text, tags, filename — never touches the binary |\n| `contentrain_media_delete` | Remove asset | Destructive; content references are NOT rewritten |\n\n## Key Principles\n\n### 1. Deterministic Infrastructure\n\nMCP is infrastructure, not intelligence. It does not decide what content to write — the agent does. MCP guarantees:\n\n- **Canonical JSON** — sorted keys, 2-space indent, trailing newline\n- **Consistent file paths** — locale strategy determines where files live\n- **Atomic git transactions** — every write is committed to a branch\n- **Schema enforcement** — content is validated against model definitions\n\n### 2. Dry-Run First\n\nEvery write operation supports `dry_run: true`. The pattern is always:\n\n1. Run with `dry_run: true` to preview changes\n2. Review the output\n3. Run with `dry_run: false` to commit\n\n::: warning Never Skip Preview\nAlways call write tools with `dry_run: true` first. This is not optional — it prevents accidental schema changes, content overwrites, and branch pollution.\n:::\n\n### 3. Git-Native Workflow\n\nAll write operations create or update `cr/*` branches:\n\n- Content changes go to isolated branches (`cr/{scope}/{target}[/{locale}]/{timestamp}-{suffix}`)\n- Humans review via `contentrain diff` or the serve UI\n- Approved changes merge into the `contentrain` branch, baseBranch is advanced via update-ref\n- Merging (or deleting) a branch also removes its copy on the remote, so merged branches don't pile up as phantom pending reviews — best-effort, opt out with `remoteBranchCleanup: false` in `config.json`. Drain an existing backlog with `contentrain prune`\n- Merged-branch detection survives base-history rewrites (ancestry check with a patch-id fallback)\n- Branch health is tracked and surfaced via `contentrain_status` (warning at 50, blocked at 80 active branches); `contentrain_doctor` adds a remote `cr/*` count\n- Legacy `contentrain/*` branches are auto-migrated on first init\n\n### 4. Local-First by Default, Remote Providers Opt-In\n\nThe default shape — stdio transport + `LocalProvider` — operates entirely on the local filesystem. No GitHub API, no cloud service, no external dependency.\n\nRemote providers (`GitHubProvider` via `@octokit/rest`, `GitLabProvider` via `@gitbeaker/rest`) are **optional peer dependencies**. They are installed only when Studio, CI, or a remote agent needs to drive MCP over an HTTP transport against a hosted git repo. A session that uses `LocalProvider` never loads these SDKs.\n\nThat means:\n\n- Default install works offline and needs no API keys\n- Optional remote backends ship on the same tool contract — see [Providers and transports](/guides/providers) for the full capability matrix\n- Normalize, scan, and apply always need a `LocalProvider` — they return a `capability_required` error on remote providers\n\n### 5. Capability Gates\n\nEvery tool declares the capabilities it needs. Tools that require `astScan`, `sourceRead`, `sourceWrite`, or `localWorktree` reject on providers that do not expose them with a uniform error:\n\n```json\n{\n \"error\": \"contentrain_scan requires local filesystem access.\",\n \"capability_required\": \"astScan\",\n \"hint\": \"This tool is unavailable when MCP is driven by a remote provider. Use a LocalProvider or the stdio transport.\"\n}\n```\n\nAgent drivers treat `capability_required` as a retry signal. See [Providers & Transports](/guides/providers) for the full capability matrix.\n\n## Transports\n\n- **stdio** — `contentrain serve --stdio` or `npx contentrain-mcp`. IDE agents (Claude Code, Cursor, Windsurf) connect over stdin/stdout.\n- **HTTP** — `contentrain serve --mcpHttp --authToken $TOKEN` or the programmatic `startHttpMcpServer({...})` / `startHttpMcpServerWith({ provider })` exports. Streamable HTTP at `POST /mcp` with secure-by-default Bearer auth. See the [HTTP Transport guide](/guides/http-transport).\n\nBoth transports serve the same tool surface and the same JSON response shapes.\n\n## Providers\n\n`@contentrain/mcp` ships three `RepoProvider` implementations behind a single contract:\n\n- **`LocalProvider`** — simple-git + temporary worktree on your disk\n- **`GitHubProvider`** — Octokit over the Git Data + Repos APIs (no clone)\n- **`GitLabProvider`** — gitbeaker over the GitLab REST API (no clone; supports self-hosted)\n\nBitbucket is on the roadmap. See [Providers & Transports](/guides/providers) for the capability matrix and [RepoProvider Reference](/reference/providers) for the interface definitions.\n\n## Studio Bridge\n\n`@contentrain/mcp` is the local execution layer for the ecosystem. It is where agents do deterministic work such as init, schema changes, validation, and normalize.\n\nWhen a project moves from solo or IDE-led work into team review and delivery, [Contentrain Studio](/studio) becomes the authenticated web surface on top of the same `.contentrain/` contract:\n\n- MCP keeps local writes deterministic and git-safe\n- Studio adds team roles, review UI, media, forms, APIs, and CDN delivery\n- both surfaces should describe the same models, locales, and branch semantics\n\nUse the [Ecosystem Map](/ecosystem) when you need the full package-to-product relationship, or jump to the Studio docs for the team-facing workflow:\n\n- [Studio Overview](/studio)\n- [Studio AI Chat](https://docs.contentrain.io/guide/ai-chat)\n- [Studio Branches & Review](https://docs.contentrain.io/guide/branches-and-review)\n\n## Usage Examples\n\n### Check Project Status\n\nAsk your agent: *\"What's the current state of my Contentrain project?\"* — triggers `contentrain_status`. Returns config, models list, branch health, pending changes, validation state.\n\n### Run Health Checks\n\nAsk your agent: *\"Is my Contentrain setup healthy?\"* — triggers `contentrain_doctor`. Returns structured checks (env, structure, models, orphans, branches, SDK freshness). With `usage: true`, also analyzes unused keys, duplicates, missing translations.\n\n### Create a Model\n\n```ts\n// Agent calls contentrain_model_save with dry_run: true first\n{\n \"id\": \"blog-post\",\n \"name\": \"Blog Posts\",\n \"kind\": \"collection\",\n \"domain\": \"content\",\n \"i18n\": true,\n \"fields\": {\n \"title\": { \"type\": \"string\", \"required\": true },\n \"excerpt\": { \"type\": \"text\" },\n \"author\": { \"type\": \"relation\", \"relation\": \"team-members\" },\n \"published\": { \"type\": \"boolean\" }\n }\n}\n```\n\nAsk your agent: *\"Create a blog post model with title, excerpt, author relation, and published flag\"*\n\n### Save Content\n\n```ts\n// Agent calls contentrain_content_save\n{\n \"model\": \"blog-post\",\n \"entries\": [{\n \"locale\": \"en\",\n \"data\": {\n \"title\": \"Getting Started with Contentrain\",\n \"excerpt\": \"Learn how to set up AI-powered content management\",\n \"published\": true\n }\n }]\n}\n```\n\nAsk your agent: *\"Add a new blog post about getting started\"*\n\n::: tip Media in cloud mode\nFor `image`/`video`/`file` fields (and `media/...` references in markdown bodies), pass either the media-library storage path (`media/...`) or a URL. When the server runs in cloud mode with a delivery base (Studio's MCP Cloud supplies one per project), `contentrain_content_save` normalizes those references to absolute public delivery URLs on save, so committed content renders anywhere with no SDK. In local mode the relative path is kept verbatim, and external URLs always pass through untouched.\n:::\n\n### Normalize Flow (Scan + Extract + Reuse)\n\n```ts\n// Phase 1: Scan for hardcoded strings\n// Agent calls contentrain_scan → gets candidates\n\n// Phase 2: Extract content\n// Agent calls contentrain_apply with mode: \"extract\"\n// Creates models, writes content entries, tracks sources (cr/normalize/extract/*)\n\n// Phase 3: Reuse in source\n// Agent calls contentrain_apply with mode: \"reuse\"\n// Patches source files with i18n function calls (cr/normalize/reuse/*)\n```\n\nAsk your agent: *\"Scan my landing page for hardcoded strings and extract them\"*\n\n## Agent Configuration\n\nThe fastest way to connect your IDE agent:\n\n```bash\nnpx contentrain setup claude-code # or: cursor, vscode, windsurf, copilot\n```\n\nThis auto-creates the correct MCP config file and installs AI rules/skills.\n\n
\nManual configuration (all IDEs use the same JSON)\n\n```json\n{\n \"mcpServers\": {\n \"contentrain\": {\n \"command\": \"npx\",\n \"args\": [\"contentrain\", \"serve\", \"--stdio\"]\n }\n }\n}\n```\n\n| IDE | Config file |\n|-----|-------------|\n| Claude Code | `.mcp.json` |\n| Cursor | `.cursor/mcp.json` |\n| VS Code | `.vscode/mcp.json` |\n| Windsurf | `.windsurf/mcp.json` |\n\n
\n\nOnce connected, the agent has access to the full MCP tool surface and can manage your content through natural language.\n\n### OpenAI Codex\n\nCodex uses `~/.codex/config.toml` or a trusted project-scoped `.codex/config.toml`.\n\n```bash\ncodex mcp add contentrain -- npx -y contentrain serve --stdio\n```\n\nEquivalent `config.toml`:\n\n```toml\n[mcp_servers.contentrain]\ncommand = \"npx\"\nargs = [\"-y\", \"contentrain\", \"serve\", \"--stdio\"]\n```\n\nFor Studio MCP Cloud, configure a Streamable HTTP server with the project endpoint and Bearer token:\n\n```toml\n[mcp_servers.contentrain]\nurl = \"https://studio.contentrain.io/api/mcp/v1/{projectId}/mcp\"\nhttp_headers = { Authorization = \"Bearer \" }\n```\n\n### Claude Desktop\n\nClaude Desktop reads `claude_desktop_config.json`. For a local checkout:\n\n```json\n{\n \"mcpServers\": {\n \"contentrain\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"contentrain\", \"serve\", \"--stdio\"]\n }\n }\n}\n```\n\nFor Studio MCP Cloud:\n\n```json\n{\n \"mcpServers\": {\n \"contentrain\": {\n \"type\": \"http\",\n \"url\": \"https://studio.contentrain.io/api/mcp/v1/{projectId}/mcp\",\n \"headers\": {\n \"Authorization\": \"Bearer \"\n }\n }\n }\n}\n```\n\n### Claude Code\n\nClaude Code can use the project `.mcp.json` generated by `contentrain setup claude-code`, or add the hosted endpoint directly:\n\n```bash\nclaude mcp add --transport http contentrain \\\n https://studio.contentrain.io/api/mcp/v1/{projectId}/mcp \\\n --header \"Authorization: Bearer \"\n```\n\n## Trust Model\n\n| Trust Level | Tools | Risk | Notes |\n|-------------|-------|------|-------|\n| **HIGH** (read-only) | `status`, `describe`, `describe_format`, `doctor`, `content_list` | None | Safe to call anytime, no side effects |\n| **MEDIUM** (git-isolated writes) | `model_save`, `content_save`, `content_delete`, `model_delete`, `validate`, `scaffold`, `bulk` | Low | Changes isolated to `cr/*` branches, reviewable |\n| **LOW** (source modification) | `scan`, `apply` | Medium | Normalize touches source files — always use dry_run first |\n| **MEDIUM** (remote push) | `submit`, `merge` | Medium | Pushes branches to remote or merges — requires network access and review |\n\n::: danger Source Modifications\nThe `contentrain_apply` tool with `mode: \"reuse\"` modifies your source code files. Always run with `dry_run: true` first, review the patches carefully, and use the review workflow before merging.\n:::\n\n## Typical Agent Workflow\n\n```\n1. contentrain_status → understand project state\n2. contentrain_doctor → validate setup health\n3. contentrain_init → bootstrap if needed\n4. contentrain_describe_format → understand storage contract\n5. contentrain_model_save → define content schemas\n6. contentrain_content_save → write content entries\n7. contentrain_validate → check everything is valid\n8. contentrain_submit → push for review\n```\n\n## Core Exports\n\nFor advanced integrations, the package exports low-level modules:\n\n```ts\nimport { createServer } from '@contentrain/mcp/server'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\n\nconst server = createServer(process.cwd())\nconst transport = new StdioServerTransport()\nawait server.connect(transport)\n```\n\nAvailable subpath exports:\n\n- `@contentrain/mcp/server` — MCP server factory and stdio setup\n- `@contentrain/mcp/server/http` — HTTP transport server factory\n- `@contentrain/mcp/core/config` — Config manager\n- `@contentrain/mcp/core/context` — Context JSON manager\n- `@contentrain/mcp/core/model-manager` — Model CRUD\n- `@contentrain/mcp/core/content-manager` — Content CRUD\n- `@contentrain/mcp/core/validator` — Validation engine\n- `@contentrain/mcp/core/scanner` — Source code scanner\n- `@contentrain/mcp/core/graph-builder` — Component graph\n- `@contentrain/mcp/core/apply-manager` — Normalize apply\n- `@contentrain/mcp/core/doctor` — Health check engine\n- `@contentrain/mcp/core/contracts` — RepoProvider interface types\n- `@contentrain/mcp/core/ops` — Git operation utilities\n- `@contentrain/mcp/core/overlay-reader` — Overlay file reading\n- `@contentrain/mcp/core/scan-config` — Scan configuration\n- `@contentrain/mcp/git/transaction` — Git transaction flow\n- `@contentrain/mcp/git/branch-lifecycle` — Branch health tracking\n- `@contentrain/mcp/templates` — Scaffold templates\n- `@contentrain/mcp/tools/annotations` — Tool metadata (TOOL_NAMES, TOOL_ANNOTATIONS)\n- `@contentrain/mcp/util/detect` — Framework detection\n- `@contentrain/mcp/util/fs` — File system utilities\n- `@contentrain/mcp/providers/local` — LocalProvider implementation\n- `@contentrain/mcp/providers/github` — GitHubProvider implementation\n- `@contentrain/mcp/providers/gitlab` — GitLabProvider implementation\n\n## Related Pages\n\n- [CLI](/packages/cli) — Human-facing companion for local operations\n- [Query SDK](/packages/sdk) — Generated runtime client for consuming content\n- [Rules & Skills](/packages/rules) — Agent behavior policies and workflow playbooks\n- [Contentrain Studio](/studio) — Hosted workspace, review, chat-first operations, and content CDN", + "description": "Complete reference for @contentrain/mcp — the provider-agnostic MCP engine powering AI content governance with 24 deterministic tools over stdio or HTTP", + "order": 1, + "slug": "mcp", + "title": "MCP Tools" +} diff --git a/.contentrain/client/data/docs-packages--rules.mjs b/.contentrain/client/data/docs-packages--rules.mjs new file mode 100644 index 0000000..a8d20c3 --- /dev/null +++ b/.contentrain/client/data/docs-packages--rules.mjs @@ -0,0 +1,7 @@ +export default { + "body": "# Rules & Skills\n\n[![rules npm](https://img.shields.io/npm/v/@contentrain/rules?label=rules)](https://www.npmjs.com/package/@contentrain/rules) [![skills npm](https://img.shields.io/npm/v/@contentrain/skills?label=skills)](https://www.npmjs.com/package/@contentrain/skills)\n\nContentrain splits agent guidance into two complementary packages:\n\n- **`@contentrain/rules`** — the policy layer (what is allowed, what is not)\n- **`@contentrain/skills`** — the procedural layer (step-by-step workflows)\n\nTogether, they ensure that any AI agent — whether Claude, GPT, or a custom model — follows the same quality standards and operational patterns when working with Contentrain content.\n\n## Why Separate Rules and Skills?\n\nConsider the difference between a traffic law and a driving manual:\n\n- **Rules** say \"speed limit is 60 km/h\" and \"stop at red lights\" — constraints that must always hold\n- **Skills** say \"to parallel park: signal, position your car, turn the wheel...\" — procedures for specific tasks\n\nAn agent needs both. Rules prevent it from producing invalid content or breaking schemas. Skills guide it through multi-step workflows like normalize, content creation, or SDK generation.\n\n::: tip The Agent-MCP-Rules Triangle\n- **MCP** = deterministic execution (how files are written)\n- **Rules** = behavioral constraints (what quality standards to meet)\n- **Skills** = workflow procedures (what steps to follow)\n:::\n\n## Studio Bridge\n\n`@contentrain/rules` and `@contentrain/skills` are not only for local IDE agents. They are also the package-level contract that Studio should mirror in its chat, validation, onboarding, and review flows.\n\nThe alignment model is simple:\n\n- local agents use rules and skills directly through IDE integrations\n- Studio translates the same standards into its authenticated web workflows\n- marketing and distribution should point users from the normalize wedge into Studio without changing the underlying quality model\n\nSee [Ecosystem Map](/ecosystem) for the full package map, then compare the Studio surface here:\n\n- [Contentrain Studio](/studio)\n- [Studio Ecosystem Map](https://docs.contentrain.io/guide/ecosystem)\n- [Studio Architecture](https://docs.contentrain.io/developer/architecture)\n\n## Rules (`@contentrain/rules`)\n\n### What Rules Govern\n\nRules define non-negotiable constraints across several domains:\n\n| Domain | File | What It Covers |\n|--------|------|---------------|\n| Content Quality | `content-quality.md` | Writing standards, tone, accuracy, completeness |\n| Schema Design | `schema-rules.md` | Field types, naming conventions, model structure |\n| i18n Quality | `i18n-quality.md` | Locale consistency, translation completeness, key naming |\n| SEO | `seo-rules.md` | Meta descriptions, titles, structured data |\n| Accessibility | `accessibility-rules.md` | Alt text, ARIA labels, semantic markup |\n| Security | `security-rules.md` | Input sanitization, sensitive data handling |\n| Media | `media-rules.md` | Image optimization, file naming, asset organization |\n| Content Conventions | `content-conventions.md` | Markdown formatting, frontmatter patterns |\n| MCP Usage | `mcp-usage.md` | Tool calling patterns, dry-run protocol, trust levels |\n| Workflow Rules | `workflow-rules.md` | Branch management, review process, merge criteria |\n| Normalize Rules | `normalize-rules.md` | Extraction patterns, reuse expressions, scope safety |\n\n### Prompt Layers\n\nRules include mode-specific prompt layers that agents load based on their current task:\n\n| Prompt | Purpose |\n|--------|---------|\n| `common.md` | Base context loaded for all operations |\n| `generate-mode.md` | Additional context for SDK generation tasks |\n| `normalize-mode.md` | Additional context for normalize (scan/extract/reuse) |\n| `review-mode.md` | Additional context for content review and approval |\n\n### Context Bridge\n\nThe `context-bridge.md` file defines how agents should read and interpret `.contentrain/context.json` — the metadata file that MCP updates after every write operation. This ensures agents understand project state without making redundant tool calls.\n\n### Programmatic Access\n\n```ts\nimport {\n FIELD_TYPES,\n MODEL_KINDS,\n MCP_TOOLS,\n ESSENTIAL_RULES_FILE,\n STACKS,\n} from '@contentrain/rules'\n\n// Check if a tool exists\nconsole.log(MCP_TOOLS.length) // 24\nconsole.log(MCP_TOOLS.includes('contentrain_validate')) // true\nconsole.log(MCP_TOOLS.includes('contentrain_merge')) // true\nconsole.log(MCP_TOOLS.includes('contentrain_doctor')) // true\n\n// Path to essential guardrails markdown\nconsole.log(ESSENTIAL_RULES_FILE) // 'essential/contentrain-essentials.md'\n\n// All 27 field types\nconsole.log(FIELD_TYPES.length) // 27\n```\n\n## Skills (`@contentrain/skills`)\n\n### Agent Skills (Standard Format)\n\nSkills follow the [Agent Skills standard](https://agentskills.io) with progressive disclosure: each skill has a `SKILL.md` (loaded on activation, < 500 lines) and optional `references/` (loaded on demand).\n\n| Skill | Directory | When to Use |\n|-------|-----------|------------|\n| Contentrain | `skills/contentrain/` | Core architecture, MCP tools, content formats |\n| Normalize | `skills/contentrain-normalize/` | Extract hardcoded strings, patch source files |\n| Quality | `skills/contentrain-quality/` | Content quality, SEO, accessibility, media |\n| SDK | `skills/contentrain-sdk/` | @contentrain/query usage (local + CDN) |\n| Content | `skills/contentrain-content/` | Add/update content entries |\n| Model | `skills/contentrain-model/` | Create/modify model definitions |\n| Init | `skills/contentrain-init/` | Initialize Contentrain project |\n| Bulk | `skills/contentrain-bulk/` | Batch operations |\n| Validate | `skills/contentrain-validate-fix/` | Validate and auto-fix |\n| Review | `skills/contentrain-review/` | Review content changes |\n| Translate | `skills/contentrain-translate/` | Multi-locale translation |\n| Generate | `skills/contentrain-generate/` | Generate SDK client |\n| Serve | `skills/contentrain-serve/` | Local review/normalize UI |\n| Diff | `skills/contentrain-diff/` | Branch content diffs |\n| Doctor | `skills/contentrain-doctor/` | Project health check |\n\nEach skill directory contains:\n```\nskills/{name}/\n├── SKILL.md # Instructions (< 500 lines, < 5000 tokens)\n└── references/ # Detailed reference docs (loaded on demand)\n └── *.md\n```\n\n### Framework Guides\n\nSkills include framework-specific guides that teach agents how Contentrain integrates with popular stacks:\n\n| Framework | File | Key Topics |\n|-----------|------|------------|\n| Vue | `vue.md` | Composition API, `