Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
stackNoFramework stack (nuxt, next, astro, svelte, react-vite, other). Auto-detected if omitted.
localesNoSupported locales. Default: ["en"]
domainsNoContent domains. Default: auto-suggested

Implementation Reference

  • 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()
        }
      },
    )
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It successfully discloses the critical side effect of automatic git commits and warns against manual file manipulation. Missing idempotency details (what happens if already initialized) and prerequisite requirements (e.g., git repo must exist).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences total, front-loaded with purpose ('Initialize .contentrain/ structure') followed by critical behavioral warning. Every sentence earns its place with zero waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter initialization tool with no output schema, the description adequately covers the primary safety concerns (git integration). Minor gap: doesn't mention prerequisites (git initialization required) or idempotency behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the baseline is 3. The description adds no parameter-specific guidance beyond what the schema already documents (stack auto-detection, locale defaults, domain suggestions).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Initialize') with a specific resource ('.contentrain/ structure'), clearly distinguishing this setup tool from siblings like content_save or model_delete which manipulate existing data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides critical negative guidance ('do NOT manually create .contentrain/ files') and discloses git auto-commit behavior. However, lacks explicit comparison to sibling tools like contentrain_scaffold to clarify when to use init versus other setup tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Contentrain/ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server