contentrain_init
Initialize the .contentrain/ directory structure for AI content governance, with automatic git commits. Configure framework stack, locales, and content domains.
Instructions
Initialize .contentrain/ structure. Changes are auto-committed to git — do NOT manually create .contentrain/ files.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| stack | No | Framework stack (nuxt, next, astro, svelte, react-vite, other). Auto-detected if omitted. | |
| locales | No | Supported locales. Default: ["en"] | |
| domains | No | Content domains. Default: auto-suggested |
Implementation Reference
- packages/mcp/src/tools/setup.ts:18-157 (handler)The handler function for the `contentrain_init` tool, which initializes the .contentrain/ project directory, detects the stack, sets up git, and writes the initial configuration files.
server.tool( 'contentrain_init', 'Initialize .contentrain/ structure. Changes are auto-committed to git — do NOT manually create .contentrain/ files.', { stack: z.string().optional().describe('Framework stack (nuxt, next, astro, svelte, react-vite, other). Auto-detected if omitted.'), locales: z.array(z.string()).optional().describe('Supported locales. Default: ["en"]'), domains: z.array(z.string()).optional().describe('Content domains. Default: auto-suggested'), }, async ({ stack, locales, domains }) => { const crDir = contentrainDir(projectRoot) // Already initialized? if (await pathExists(join(crDir, 'config.json'))) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Already initialized', suggestion: 'Use contentrain_status to see current state', }) }], isError: true, } } // Detect stack const detectedStack = stack ?? await detectStack(projectRoot) const supportedLocales = locales ?? ['en'] const suggestedDomains = domains ?? suggestDomains(projectRoot) // Ensure .git exists const hasGit = await pathExists(join(projectRoot, '.git')) if (!hasGit) { await simpleGit(projectRoot).init() } // Branch health gate const initHealth = await checkBranchHealth(projectRoot) if (initHealth.blocked) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: initHealth.message, action: 'blocked', hint: 'Merge or delete old contentrain/* branches before creating new ones.', }, null, 2) }], isError: true, } } // Create git transaction const branch = buildBranchName('new', 'init') const tx = await createTransaction(projectRoot, branch) try { await tx.write(async (wt) => { const wtCrDir = join(wt, '.contentrain') // Create directories with .gitkeep so git tracks them await Promise.all(['models', 'content', 'meta'].map(async (dir) => { await ensureDir(join(wtCrDir, dir)) await writeFile(join(wtCrDir, dir, '.gitkeep'), '', 'utf-8') })) // Write config const config: ContentrainConfig = { version: 1, stack: detectedStack as ContentrainConfig['stack'], workflow: 'auto-merge', locales: { default: supportedLocales[0] ?? 'en', supported: supportedLocales, }, domains: suggestedDomains, } await writeJson(join(wtCrDir, 'config.json'), config) // Write empty vocabulary const vocabulary: Vocabulary = { version: 1, terms: {} } await writeJson(join(wtCrDir, 'vocabulary.json'), vocabulary) // Write initial context await writeJson(join(wtCrDir, 'context.json'), { version: '1', lastOperation: { tool: 'contentrain_init', model: '', locale: supportedLocales[0] ?? 'en', timestamp: new Date().toISOString(), source: process.env['CONTENTRAIN_SOURCE'] === 'mcp-studio' ? 'mcp-studio' : 'mcp-local', }, stats: { models: 0, entries: 0, locales: supportedLocales, lastSync: new Date().toISOString() }, }) // Update .gitignore await updateGitignore(wt) }) await tx.commit('[contentrain] init: project setup') const gitResult = await tx.complete({ tool: 'contentrain_init', model: '', locale: supportedLocales[0] ?? 'en', }) return { content: [{ type: 'text' as const, text: JSON.stringify({ status: 'committed', message: 'Project initialized and committed to git. Do NOT manually edit .contentrain/ files.', config_created: '.contentrain/config.json', detected_stack: detectedStack, detected_locales: supportedLocales, suggested_domains: suggestedDomains, files_created: [ '.contentrain/config.json', '.contentrain/vocabulary.json', '.contentrain/context.json', ], directories_created: [ '.contentrain/models/', '.contentrain/content/', '.contentrain/meta/', ], gitignore_updated: true, git: { branch, action: gitResult.action, commit: gitResult.commit }, next_steps: [ 'Create models with contentrain_model_save or contentrain_scaffold', 'Use contentrain_scan to find hardcoded strings', ], }, null, 2) }], } } catch (error) { await tx.cleanup() return { content: [{ type: 'text' as const, text: JSON.stringify({ error: `Init failed: ${error instanceof Error ? error.message : String(error)}`, }) }], isError: true, } } finally { await tx.cleanup() } }, )