Skip to content

Configuration

Contentrain uses a set of JSON configuration files stored in the .contentrain/ directory at your project root. This page covers every configuration file, its schema, and how they interact.

Directory Layout

The .contentrain/ directory is the central hub for all Contentrain data:

.contentrain/
├── config.json          # Project configuration
├── context.json         # Last operation metadata (auto-written by MCP)
├── vocabulary.json      # Shared terms for consistency (optional)
├── models/
│   ├── blog-posts.json  # Model schema definitions
│   ├── site-settings.json
│   └── ui-labels.json
├── content/
│   ├── blog/            # Domain directory
│   │   └── blog-posts/  # Model content directory
│   │       ├── en.json  # English entries
│   │       └── tr.json  # Turkish entries
│   └── system/
│       ├── site-settings/
│       │   ├── en.json
│       │   └── tr.json
│       └── ui-labels/
│           ├── en.json
│           └── tr.json
└── meta/
    ├── blog-posts/
    │   ├── en.json      # Entry metadata per locale
    │   └── tr.json
    ├── site-settings/
    │   ├── en.json
    │   └── tr.json
    └── ui-labels/
        ├── en.json
        └── tr.json

INFO

Models with a content_path override store their content files outside .contentrain/content/ — for example, directly in content/blog/ or locales/. The meta files always remain in .contentrain/meta/.

config.json

The primary project configuration file. Created by contentrain init and updated by MCP tools.

Full Schema

ts
interface ContentrainConfig {
  version: number          // Config version (currently 1)
  platform?: Platform      // Target platform
  stack: StackType         // Framework/stack identifier
  workflow: WorkflowMode   // Content workflow mode
  repository?: {           // Git repository info (optional)
    provider: 'github'
    owner: string
    name: string
    default_branch: string
  }
  locales: {
    default: string        // Default locale code (e.g., "en")
    supported: string[]    // All supported locale codes
  }
  domains: string[]        // Content domain names
  assets_path?: string     // Path for media assets
  branchRetention?: number // Auto-cleanup branch count
}

Example

json
{
  "version": 1,
  "platform": "web",
  "stack": "nuxt",
  "workflow": "review",
  "repository": {
    "provider": "github",
    "owner": "my-org",
    "name": "my-website",
    "default_branch": "main"
  },
  "locales": {
    "default": "en",
    "supported": ["en", "tr", "de"]
  },
  "domains": ["marketing", "blog", "system"],
  "assets_path": "public/uploads",
  "branchRetention": 10
}

Field Reference

FieldTypeRequiredDescription
versionnumberYesConfig schema version. Currently 1.
platformPlatformNoTarget platform: web, mobile, api, desktop, static, other
stackStackTypeYesFramework identifier. See Supported Stacks below.
workflowWorkflowModeYesauto-merge or review. See Workflow Modes.
repositoryobjectNoGitHub repository connection details.
locales.defaultstringYesDefault locale code (e.g., en).
locales.supportedstring[]YesAll supported locale codes. Must include default.
domainsstring[]YesContent domain names for organizing models.
assets_pathstringNoDirectory for media assets relative to project root.
branchRetentionnumberNoNumber of merged content branches to keep before auto-cleanup.

Supported Stacks

Contentrain supports a wide range of frameworks and platforms:

CategoryStack Types
Meta-frameworksnuxt, next, astro, sveltekit, remix, analog
Frontend frameworksvue, react, svelte, solid, angular
Mobilereact-native, expo, flutter
Backendnode, express, fastify, nestjs, django, rails, laravel, go, rust, dotnet
Static site generatorshugo, jekyll, eleventy
Desktopelectron, tauri
Catch-allother

Workflow Modes

The workflow field controls how content changes are integrated:

ModeBehaviorBest For
auto-mergeContent branches are automatically merged to the default branch after commitDevelopment, single-author projects, rapid iteration
reviewContent branches remain open for human review before mergingProduction, multi-author teams, quality gates

WARNING

Normalize operations (extraction and reuse) always use the review workflow regardless of this setting. This ensures human oversight for code-modifying changes.

context.json

Automatically maintained by MCP after every write operation. This file provides last-operation tracking for IDE integrations and the Serve UI.

Full Schema

ts
interface ContextJson {
  version: string
  lastOperation: {
    tool: string           // MCP tool name that performed the operation
    model: string          // Model ID that was affected
    locale: string         // Locale of the operation
    entries?: string[]     // Entry IDs affected (collection only)
    timestamp: string      // ISO 8601 timestamp
    source: ContextSource  // Who triggered: 'mcp-local' | 'mcp-studio' | 'studio-ui'
  }
  stats: {
    models: number         // Total number of models
    entries: number        // Total number of content entries
    locales: string[]      // Active locale codes
    lastSync: string       // ISO 8601 timestamp of last sync
  }
}

Example

json
{
  "version": "1.0.0",
  "lastOperation": {
    "tool": "contentrain_content_save",
    "model": "blog-posts",
    "locale": "en",
    "entries": ["a1b2c3", "d4e5f6"],
    "timestamp": "2026-03-15T10:30:00Z",
    "source": "mcp-local"
  },
  "stats": {
    "models": 5,
    "entries": 42,
    "locales": ["en", "tr"],
    "lastSync": "2026-03-15T10:30:00Z"
  }
}

TIP

Do not manually edit context.json. It is auto-written by MCP after every write operation. IDE extensions and the Serve UI read this file to stay synchronized.

Context Sources

SourceDescription
mcp-localOperation triggered via local MCP (CLI/IDE)
mcp-studioOperation triggered via Contentrain Studio MCP
studio-uiOperation triggered via the Studio web UI

vocabulary.json

An optional file for maintaining shared terms across all content. Ensures consistency when multiple agents or authors work on the same project.

Full Schema

ts
interface Vocabulary {
  version: number
  terms: Record<string, Record<string, string>>
  // terms[term_key][locale] = translated_value
}

Example

json
{
  "version": 1,
  "terms": {
    "product_name": {
      "en": "Contentrain",
      "tr": "Contentrain"
    },
    "company_name": {
      "en": "Contentrain Inc.",
      "tr": "Contentrain A.Ş."
    },
    "cta_primary": {
      "en": "Get Started",
      "tr": "Başla"
    },
    "support_email": {
      "en": "[email protected]",
      "tr": "[email protected]"
    }
  }
}

Usage

  • Agents reference vocabulary terms to ensure brand names, CTAs, and product terminology are consistent across all content.
  • Validation can flag content that deviates from established vocabulary.
  • Terms are locale-aware — each term has translations for all supported locales.

INFO

Vocabulary is optional but recommended for projects with multiple content authors or AI agents. It prevents inconsistencies like "Get Started" vs "Start Now" vs "Begin Here" across different pages.

Model Definition Files

Stored in .contentrain/models/{model-id}.json. Each file defines one model's schema.

Full Schema

ts
interface ModelDefinition {
  id: string                      // Unique model identifier
  name: string                    // Human-readable name
  kind: ModelKind                 // 'collection' | 'singleton' | 'document' | 'dictionary'
  domain: string                  // Organizational domain
  i18n: boolean                   // Whether content is localized
  description?: string            // Optional description
  fields?: Record<string, FieldDef> // Field definitions (not used for dictionary)
  content_path?: string           // Framework-relative path override
  locale_strategy?: LocaleStrategy // How locale is encoded in file paths
}

See the Model Kinds page for detailed kind-specific documentation and the Field Types page for field definition details.

Meta Files

Stored in .contentrain/meta/{model-id}/{locale}.json. Track the lifecycle state of content entries.

Entry Metadata Schema

ts
interface EntryMeta {
  status: ContentStatus    // 'draft' | 'in_review' | 'published' | 'rejected' | 'archived'
  source: ContentSource    // 'agent' | 'human' | 'import'
  updated_by: string       // Author identifier
  approved_by?: string     // Approver identifier (optional)
  version?: string         // Version string (optional)
  publish_at?: string      // ISO 8601 scheduled publish (optional)
  expire_at?: string       // ISO 8601 scheduled expiry (optional)
}

Content Status Lifecycle

StatusDescriptionTransitions To
draftInitial state, work in progressin_review, published
in_reviewSubmitted for reviewpublished, rejected
publishedLive and activearchived, draft
rejectedReview rejecteddraft
archivedNo longer activedraft

Content Sources

SourceDescription
agentCreated or modified by an AI agent via MCP
humanCreated or modified by a human via Studio UI or direct edit
importImported from an external system

Scheduled Publishing

Use publish_at and expire_at for time-based content lifecycle:

json
{
  "status": "draft",
  "source": "agent",
  "updated_by": "contentrain-mcp",
  "publish_at": "2026-04-01T00:00:00Z",
  "expire_at": "2026-06-30T23:59:59Z"
}

WARNING

expire_at must be after publish_at. The MCP validation tool will flag invalid date ranges.

Domain Organization

Domains group related models and are declared in config.json's domains array.

Common Patterns

DomainTypical ModelsDescription
marketingLanding pages, CTAs, testimonials, pricingPublic-facing marketing content
blogBlog posts, authors, categories, tagsBlog and editorial content
systemSite settings, navigation, footer, labelsSystem-level configuration
docsGuides, reference, tutorialsDocumentation content
productFeatures, changelog, roadmapProduct-related content
legalPrivacy policy, terms of serviceLegal documents

Adding a Domain

Domains are declared in config.json and used as the domain field in model definitions:

json
// config.json
{
  "domains": ["marketing", "blog", "system", "docs"]
}

// .contentrain/models/blog-posts.json
{
  "id": "blog-posts",
  "domain": "blog",
  "kind": "collection"
}

Content files for this model would be stored at .contentrain/content/blog/blog-posts/ (unless content_path is set).

Locale Configuration

Default Locale

The locales.default value determines:

  • Which locale is used when no locale is specified in MCP calls
  • The fallback locale for missing translations
  • The primary locale for the content editing experience

Supported Locales

The locales.supported array lists all active locale codes:

json
{
  "locales": {
    "default": "en",
    "supported": ["en", "tr", "de", "fr", "es"]
  }
}

TIP

The supported array must include the default locale. Use standard BCP 47 locale codes (e.g., en, tr, de, fr, pt-BR).

Per-Model i18n

Each model independently controls whether it uses localization:

i18n ValueFile PatternUse Case
trueSeparate file per locale (en.json, tr.json)Translatable content
falseSingle data.json fileLanguage-independent content (e.g., color codes, API keys)

Canonical Serialization

All JSON files written by Contentrain follow strict serialization rules for clean git diffs:

RuleDescription
Sorted keysObject keys are sorted alphabetically
2-space indentConsistent indentation
Trailing newlineEvery file ends with \n
No trailing commasStandard JSON format
UTF-8 encodingUniversal character support

This ensures that content changes produce minimal, reviewable git diffs — critical for the review workflow.

Released under the MIT License.