contentrain_scaffold
Generate project structures using templates for blogs, landing pages, documentation, ecommerce sites, SaaS applications, internationalization setups, and mobile projects with automatic git commits.
Instructions
Template-based project setup. Available templates: blog, landing, docs, ecommerce, saas, i18n, mobile. Changes are auto-committed to git.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| template | Yes | Template ID: blog, landing, docs, ecommerce, saas, i18n, mobile | |
| locales | No | Override locales | |
| with_sample_content | No | Include sample content (default: true) |
Implementation Reference
- packages/mcp/src/tools/setup.ts:160-271 (handler)The `contentrain_scaffold` tool implementation, which handles template-based project setup, model creation, and initial content seeding within a git transaction.
server.tool( 'contentrain_scaffold', `Template-based project setup. Available templates: ${listTemplates().join(', ')}. Changes are auto-committed to git.`, { template: z.string().describe('Template ID: blog, landing, docs, ecommerce, saas, i18n, mobile'), locales: z.array(z.string()).optional().describe('Override locales'), with_sample_content: z.boolean().optional().default(true).describe('Include sample content (default: true)'), }, async ({ template: templateId, locales, with_sample_content }) => { const config = await readConfig(projectRoot) if (!config) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.', }) }], isError: true, } } const tmpl = getTemplate(templateId) if (!tmpl) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown template: "${templateId}". Available: ${listTemplates().join(', ')}`, }) }], isError: true, } } // Branch health gate const scaffoldHealth = await checkBranchHealth(projectRoot) if (scaffoldHealth.blocked) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: scaffoldHealth.message, action: 'blocked', hint: 'Merge or delete old contentrain/* branches before creating new ones.', }, null, 2) }], isError: true, } } const effectiveLocales = locales ?? config.locales.supported const defaultLocale = effectiveLocales[0] ?? 'en' const branch = buildBranchName('new', `scaffold-${templateId}`, defaultLocale) const tx = await createTransaction(projectRoot, branch) try { const modelsCreated: Array<{ id: string; kind: string; fields: number }> = [] let contentCreated = 0 let vocabAdded = 0 await tx.write(async (wt) => { // Write models await Promise.all(tmpl.models.map(async (model) => { await writeModel(wt, model) modelsCreated.push({ id: model.id, kind: model.kind, fields: model.fields ? Object.keys(model.fields).length : 0, }) })) // Write sample content via content pipeline (handles content_path, locale_strategy, meta) if (with_sample_content && tmpl.sample_content) { contentCreated = await writeSampleContent(wt, tmpl, effectiveLocales, config) } // Merge vocabulary if (tmpl.vocabulary) { vocabAdded = await mergeVocabulary(wt, tmpl.vocabulary, effectiveLocales) } }) await tx.commit(`[contentrain] scaffold: ${templateId} (${defaultLocale})`) const gitResult = await tx.complete({ tool: 'contentrain_scaffold', model: tmpl.models.map(m => m.id).join(', '), locale: defaultLocale, }) return { content: [{ type: 'text' as const, text: JSON.stringify({ status: 'committed', message: 'Scaffold applied and committed to git. Do NOT manually edit .contentrain/ files.', models_created: modelsCreated, content_created: contentCreated, vocabulary_terms_added: vocabAdded, git: { branch, action: gitResult.action, commit: gitResult.commit }, context_updated: true, next_steps: [ 'Customize models with contentrain_model_save', 'Add content with contentrain_content_save', 'Run contentrain_submit when ready', ], }, null, 2) }], } } catch (error) { await tx.cleanup() return { content: [{ type: 'text' as const, text: JSON.stringify({ error: `Scaffold failed: ${error instanceof Error ? error.message : String(error)}`, }) }], isError: true, } } finally { await tx.cleanup() } }, )