@contentrain/mcp 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -25
- package/dist/annotations-Clhbw2lh.mjs +126 -0
- package/dist/annotations-Clhbw2lh.mjs.map +1 -0
- package/dist/{apply-manager-BkPMfpsN.mjs → apply-manager-D5ym5Jba.mjs} +7 -7
- package/dist/{apply-manager-BkPMfpsN.mjs.map → apply-manager-D5ym5Jba.mjs.map} +1 -1
- package/dist/{astro-parser-BJ-54JGe.mjs → astro-parser-B-nLl0zE.mjs} +3 -3
- package/dist/{astro-parser-BJ-54JGe.mjs.map → astro-parser-B-nLl0zE.mjs.map} +1 -1
- package/dist/{branch-lifecycle-wGJg25LT.mjs → branch-lifecycle-CFVpTpNT.mjs} +33 -3
- package/dist/{branch-lifecycle-wGJg25LT.mjs.map → branch-lifecycle-CFVpTpNT.mjs.map} +1 -1
- package/dist/{compiler-rIC4ETHv.mjs → compiler-k4yttlzM.mjs} +2 -2
- package/dist/{compiler-rIC4ETHv.mjs.map → compiler-k4yttlzM.mjs.map} +1 -1
- package/dist/{compiler-sfc.cjs-BbD04rn_.mjs → compiler-sfc.cjs-D0d_OicF.mjs} +2 -2
- package/dist/{compiler-sfc.cjs-BbD04rn_.mjs.map → compiler-sfc.cjs-D0d_OicF.mjs.map} +1 -1
- package/dist/config-BIl5Ybj0.mjs +40 -0
- package/dist/config-BIl5Ybj0.mjs.map +1 -0
- package/dist/content-manager-DqDLSNQ-.d.mts +63 -0
- package/dist/content-manager-DqDLSNQ-.d.mts.map +1 -0
- package/dist/context-J0OU9Nfp.mjs +102 -0
- package/dist/context-J0OU9Nfp.mjs.map +1 -0
- package/dist/contracts-DfL0BfrD.mjs +2 -0
- package/dist/core/apply-manager.mjs +10 -10
- package/dist/core/config.d.mts +13 -0
- package/dist/core/config.d.mts.map +1 -1
- package/dist/core/config.mjs +2 -3
- package/dist/core/content-manager.d.mts +2 -45
- package/dist/core/content-manager.mjs +3 -4
- package/dist/core/context.d.mts +32 -2
- package/dist/core/context.d.mts.map +1 -1
- package/dist/core/context.mjs +7 -7
- package/dist/core/contracts/index.d.mts +2 -0
- package/dist/core/contracts/index.mjs +2 -0
- package/dist/core/doctor.d.mts +76 -0
- package/dist/core/doctor.d.mts.map +1 -0
- package/dist/core/doctor.mjs +8 -0
- package/dist/core/graph-builder.mjs +3 -4
- package/dist/core/meta-manager.mjs +2 -3
- package/dist/core/model-manager.d.mts +34 -1
- package/dist/core/model-manager.d.mts.map +1 -1
- package/dist/core/model-manager.mjs +3 -4
- package/dist/core/ops/index.d.mts +137 -0
- package/dist/core/ops/index.d.mts.map +1 -0
- package/dist/core/ops/index.mjs +4 -0
- package/dist/core/overlay-reader.d.mts +42 -0
- package/dist/core/overlay-reader.d.mts.map +1 -0
- package/dist/core/overlay-reader.mjs +2 -0
- package/dist/core/scan-config.mjs +2 -3
- package/dist/core/scanner.mjs +4 -5
- package/dist/core/validator/index.d.mts +129 -0
- package/dist/core/validator/index.d.mts.map +1 -0
- package/dist/core/validator/index.mjs +6 -0
- package/dist/{detect-DinOQAmK.mjs → detect-DL9mFmaS.mjs} +2 -2
- package/dist/{detect-DinOQAmK.mjs.map → detect-DL9mFmaS.mjs.map} +1 -1
- package/dist/doctor-CgBoT6h1.mjs +338 -0
- package/dist/doctor-CgBoT6h1.mjs.map +1 -0
- package/dist/{fs-BnIQ4dKj.mjs → fs-DLbVB-Ek.mjs} +2 -2
- package/dist/fs-DLbVB-Ek.mjs.map +1 -0
- package/dist/git/branch-lifecycle.d.mts +30 -1
- package/dist/git/branch-lifecycle.d.mts.map +1 -1
- package/dist/git/branch-lifecycle.mjs +4 -5
- package/dist/git/transaction.d.mts +2 -2
- package/dist/git/transaction.d.mts.map +1 -1
- package/dist/git/transaction.mjs +8 -8
- package/dist/{graph-builder-tM7zj-SX.mjs → graph-builder-DXv2JEp-.mjs} +3 -3
- package/dist/{graph-builder-tM7zj-SX.mjs.map → graph-builder-DXv2JEp-.mjs.map} +1 -1
- package/dist/{id-DV_T9Ic8.mjs → id-CIO9s0x_.mjs} +1 -1
- package/dist/{id-DV_T9Ic8.mjs.map → id-CIO9s0x_.mjs.map} +1 -1
- package/dist/index-D0wQJSgs.d.mts +2 -0
- package/dist/index.mjs +22 -17
- package/dist/index.mjs.map +1 -1
- package/dist/local-DvP4Y4T-.mjs +182 -0
- package/dist/local-DvP4Y4T-.mjs.map +1 -0
- package/dist/{meta-manager--0IwAPCl.mjs → meta-manager-C900rEyG.mjs} +2 -2
- package/dist/{meta-manager--0IwAPCl.mjs.map → meta-manager-C900rEyG.mjs.map} +1 -1
- package/dist/{model-manager-DW_fOGRM.mjs → model-manager-BhLsUgaB.mjs} +241 -49
- package/dist/model-manager-BhLsUgaB.mjs.map +1 -0
- package/dist/{node-Cbq5eR5i.mjs → node-DN9hZeil.mjs} +1 -1
- package/dist/{node-Cbq5eR5i.mjs.map → node-DN9hZeil.mjs.map} +1 -1
- package/dist/ops-DjEWi5Wa.mjs +486 -0
- package/dist/ops-DjEWi5Wa.mjs.map +1 -0
- package/dist/overlay-reader-DOdyDRIv.mjs +74 -0
- package/dist/overlay-reader-DOdyDRIv.mjs.map +1 -0
- package/dist/paths-C3xNyc7z.mjs +52 -0
- package/dist/paths-C3xNyc7z.mjs.map +1 -0
- package/dist/paths-CYl5vCBD.mjs +54 -0
- package/dist/paths-CYl5vCBD.mjs.map +1 -0
- package/dist/providers/github/index.d.mts +144 -0
- package/dist/providers/github/index.d.mts.map +1 -0
- package/dist/providers/github/index.mjs +414 -0
- package/dist/providers/github/index.mjs.map +1 -0
- package/dist/providers/gitlab/index.d.mts +158 -0
- package/dist/providers/gitlab/index.d.mts.map +1 -0
- package/dist/providers/gitlab/index.mjs +361 -0
- package/dist/providers/gitlab/index.mjs.map +1 -0
- package/dist/providers/local/index.d.mts +106 -0
- package/dist/providers/local/index.d.mts.map +1 -0
- package/dist/providers/local/index.mjs +12 -0
- package/dist/{scan-config-QgnJ1-52.mjs → scan-config-jC6XmiqF.mjs} +2 -2
- package/dist/{scan-config-QgnJ1-52.mjs.map → scan-config-jC6XmiqF.mjs.map} +1 -1
- package/dist/{scanner-DUoXnmtI.mjs → scanner-DKMwc03k.mjs} +7 -7
- package/dist/{scanner-DUoXnmtI.mjs.map → scanner-DKMwc03k.mjs.map} +1 -1
- package/dist/serialization-B1CEzR4H.mjs +2 -0
- package/dist/server/http/index.d.mts +65 -0
- package/dist/server/http/index.d.mts.map +1 -0
- package/dist/server/http/index.mjs +137 -0
- package/dist/server/http/index.mjs.map +1 -0
- package/dist/{server-Dtq8vqbK.mjs → server-B1OJrUQ9.mjs} +486 -355
- package/dist/server-B1OJrUQ9.mjs.map +1 -0
- package/dist/server-WqGx6ait.d.mts +50 -0
- package/dist/server-WqGx6ait.d.mts.map +1 -0
- package/dist/server.d.mts +2 -7
- package/dist/server.mjs +22 -17
- package/dist/{source-map-generator-DpO-ejrt.mjs → source-map-generator-BWDva49I.mjs} +1 -1
- package/dist/{source-map-generator-DpO-ejrt.mjs.map → source-map-generator-BWDva49I.mjs.map} +1 -1
- package/dist/{svelte-parser-1bBcMx3T.mjs → svelte-parser-Dr9mmZuk.mjs} +3 -3
- package/dist/{svelte-parser-1bBcMx3T.mjs.map → svelte-parser-Dr9mmZuk.mjs.map} +1 -1
- package/dist/templates/index.mjs +1 -1
- package/dist/{templates-bRPzYq4M.mjs → templates-VrLVqfe5.mjs} +1 -1
- package/dist/{templates-bRPzYq4M.mjs.map → templates-VrLVqfe5.mjs.map} +1 -1
- package/dist/tools/annotations.d.mts +24 -0
- package/dist/tools/annotations.d.mts.map +1 -0
- package/dist/tools/annotations.mjs +2 -0
- package/dist/{transaction-C_Pvux78.mjs → transaction-DgkNgWSI.mjs} +61 -32
- package/dist/transaction-DgkNgWSI.mjs.map +1 -0
- package/dist/tsx-parser-BOMZE_yQ.mjs +2 -0
- package/dist/{tsx-parser-BWiMGAIR.mjs → tsx-parser-CQc6nr-o.mjs} +1 -1
- package/dist/{tsx-parser-BWiMGAIR.mjs.map → tsx-parser-CQc6nr-o.mjs.map} +1 -1
- package/dist/util/detect.mjs +2 -3
- package/dist/util/fs.mjs +1 -2
- package/dist/util/id.mjs +1 -1
- package/dist/validator-B0nNRFeU.mjs +906 -0
- package/dist/validator-B0nNRFeU.mjs.map +1 -0
- package/dist/{vue-parser-DTPTN3Yb.mjs → vue-parser-COgjArqv.mjs} +3 -3
- package/dist/{vue-parser-DTPTN3Yb.mjs.map → vue-parser-COgjArqv.mjs.map} +1 -1
- package/package.json +75 -21
- package/dist/config-B5kc2oeV.mjs +0 -27
- package/dist/config-B5kc2oeV.mjs.map +0 -1
- package/dist/context-DIDt_TxX.mjs +0 -46
- package/dist/context-DIDt_TxX.mjs.map +0 -1
- package/dist/core/content-manager.d.mts.map +0 -1
- package/dist/core/validator.d.mts +0 -23
- package/dist/core/validator.d.mts.map +0 -1
- package/dist/core/validator.mjs +0 -7
- package/dist/fs-BnIQ4dKj.mjs.map +0 -1
- package/dist/model-manager-DW_fOGRM.mjs.map +0 -1
- package/dist/serializer-CnHTtmc1.mjs +0 -2
- package/dist/server-Dtq8vqbK.mjs.map +0 -1
- package/dist/server.d.mts.map +0 -1
- package/dist/transaction-C_Pvux78.mjs.map +0 -1
- package/dist/tsx-parser-BFLv292Y.mjs +0 -2
- package/dist/util/serializer.d.mts +0 -2
- package/dist/util/serializer.mjs +0 -2
- package/dist/validator-SQYUV2JP.mjs +0 -581
- package/dist/validator-SQYUV2JP.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-B1OJrUQ9.mjs","names":["packageJson.version"],"sources":["../src/tools/context.ts","../src/tools/guards.ts","../src/tools/setup.ts","../src/tools/commit-plan.ts","../src/tools/model.ts","../src/tools/content.ts","../src/tools/workflow.ts","../src/tools/normalize.ts","../src/tools/bulk.ts","../src/tools/doctor.ts","../package.json","../src/server.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig, readVocabulary } from '../core/config.js'\nimport { readContext } from '../core/context.js'\nimport { countEntries, listModels, readModel } from '../core/model-manager.js'\nimport { resolveLocaleStrategy } from '../core/content-manager.js'\nimport { contentDirPath, contentFilePath } from '../core/ops/paths.js'\nimport { detectStack } from '../util/detect.js'\nimport { join } from 'node:path'\nimport { pathExists } from '../util/fs.js'\nimport { checkBranchHealth, cleanupMergedBranches } from '../git/branch-lifecycle.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\n\nexport function registerContextTools(\n server: McpServer,\n provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n // ─── contentrain_status ───\n server.tool(\n 'contentrain_status',\n 'Get full project status (read-only). Returns config, models, context. Do NOT manually edit .contentrain/ based on this output.',\n {},\n TOOL_ANNOTATIONS['contentrain_status']!,\n async () => {\n // Read-only status works over any provider. Stack detection and\n // branch health are projectRoot-only — they degrade gracefully\n // when the session is driven by a remote provider.\n const initialized = await provider.fileExists('.contentrain/config.json')\n\n if (!initialized) {\n const detectedStack = projectRoot ? await detectStack(projectRoot) : null\n return {\n content: [{\n type: 'text' as const,\n text: JSON.stringify({\n initialized: false,\n ...(detectedStack ? { detected_stack: detectedStack } : {}),\n suggestion: 'Run contentrain_init to set up .contentrain/ structure',\n next_steps: ['Run contentrain_init'],\n }, null, 2),\n }],\n }\n }\n\n const config = await readConfig(provider)\n const models = await listModels(provider)\n // Local flow uses the filesystem helper for byte-parity; remote\n // flows read the committed `.contentrain/context.json` through\n // the provider. Either way, the context block surfaces the last\n // operation + stats that were written alongside the last commit.\n const context = projectRoot ? await readContext(projectRoot) : await readContext(provider)\n const vocabulary = await readVocabulary(provider)\n\n const errors: string[] = []\n if (!config) errors.push('.contentrain/config.json missing')\n\n const result: Record<string, unknown> = {\n initialized: true,\n config: config ? {\n stack: config.stack,\n workflow: config.workflow,\n locales: config.locales,\n domains: config.domains,\n repository: config.repository,\n } : null,\n models,\n context: context ? {\n lastOperation: context.lastOperation,\n stats: context.stats,\n } : null,\n vocabulary: vocabulary && Object.keys(vocabulary.terms).length > 0\n ? { size: Object.keys(vocabulary.terms).length, terms: vocabulary.terms }\n : { size: 0 },\n }\n\n // Branch lifecycle: lazy cleanup + health check. Local-only, uses\n // simple-git against the working tree. Skipped on remote providers\n // where branch state is managed by Studio / the platform.\n if (projectRoot) {\n const hasGitRepo = await pathExists(join(projectRoot, '.git'))\n if (hasGitRepo) {\n try {\n const cleanup = await cleanupMergedBranches(projectRoot)\n const health = await checkBranchHealth(projectRoot)\n result['branches'] = {\n total: health.total,\n merged: health.merged,\n unmerged: health.unmerged,\n cleaned_up: cleanup.deleted,\n }\n if (health.message) {\n result['branch_warning'] = health.message\n }\n if (health.blocked) {\n errors.push(health.message!)\n }\n } catch {\n // Branch health check is best-effort — don't fail status\n }\n }\n }\n\n if (errors.length > 0) {\n result['validation'] = { errors: errors.length, warnings: 0, summary: errors }\n }\n\n const nextSteps: string[] = []\n if (models.length === 0) nextSteps.push('Create models with contentrain_model_save')\n if (errors.length > 0) nextSteps.push(`Fix ${errors.length} validation error(s)`)\n if (nextSteps.length === 0) nextSteps.push('Use contentrain_describe to inspect a model')\n result['next_steps'] = nextSteps\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_describe ───\n server.tool(\n 'contentrain_describe',\n 'Get full schema of a single model (read-only). Do NOT manually create content files — use contentrain_content_save instead.',\n {\n model: z.string().describe('Model ID (e.g. \"blog-post\", \"hero\")'),\n include_sample: z.boolean().optional().default(false).describe('Include one sample entry'),\n locale: z.string().optional().describe('Locale for sample content (default: config default locale)'),\n },\n TOOL_ANNOTATIONS['contentrain_describe']!,\n async ({ model: modelId, include_sample, locale }) => {\n const modelDef = await readModel(provider, modelId)\n if (!modelDef) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${modelId}\" not found` }) }],\n isError: true,\n }\n }\n\n const config = await readConfig(provider)\n const effectiveLocale = locale ?? config?.locales.default ?? 'en'\n const stats = await countEntries(provider, modelDef)\n\n const result: Record<string, unknown> = {\n id: modelDef.id,\n name: modelDef.name,\n kind: modelDef.kind,\n domain: modelDef.domain,\n i18n: modelDef.i18n,\n description: modelDef.description,\n fields: modelDef.fields,\n stats: { total_entries: stats.total, locales: stats.locales },\n }\n\n if (include_sample) {\n const sample = await getSample(provider, modelDef, effectiveLocale)\n if (sample) result['sample'] = sample\n }\n\n // Stack-aware import snippet\n const stack = config?.stack ?? 'other'\n result['import_snippet'] = generateImportSnippet(modelDef, stack, effectiveLocale)\n\n // Vocabulary hint for dictionary models\n if (modelDef.kind === 'dictionary') {\n const vocabulary = await readVocabulary(provider)\n if (vocabulary && Object.keys(vocabulary.terms).length > 0) {\n result['vocabulary_hint'] = {\n note: 'Check these approved terms before creating new dictionary keys',\n terms: vocabulary.terms,\n }\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_describe_format ───\n server.tool(\n 'contentrain_describe_format',\n 'Describes the Contentrain content file format for any language/platform. Returns a comprehensive specification of the file structure, JSON formats, markdown conventions, meta files, and locale strategies.',\n {},\n TOOL_ANNOTATIONS['contentrain_describe_format']!,\n async () => {\n const formatSpec = {\n overview: 'Contentrain stores content as plain JSON and Markdown files in a .contentrain/ directory at the project root. All files are committed to git.',\n directory_structure: {\n description: 'Standard layout of .contentrain/ directory',\n layout: {\n '.contentrain/config.json': 'Project configuration (stack, workflow, locales, domains)',\n '.contentrain/context.json': 'Last operation metadata (written by MCP after every write)',\n '.contentrain/vocabulary.json': 'Shared vocabulary/terms (optional)',\n '.contentrain/models/{model-id}.json': 'Model schema definitions',\n '.contentrain/content/{domain}/{model-id}/': 'Content files per model',\n '.contentrain/meta/{model-id}/': 'Metadata files per model (status, source, timestamps)',\n },\n },\n model_kinds: {\n collection: {\n description: 'Multiple entries stored as an object-map keyed by entry ID',\n storage_format: 'JSON object-map: { \"entry-id-1\": { ...fields }, \"entry-id-2\": { ...fields } }',\n note: 'Keys are sorted alphabetically for canonical output. Entry IDs are alphanumeric (1-40 chars, hyphens/underscores allowed).',\n example: '{ \"abc123\": { \"title\": \"Hello\", \"slug\": \"hello\" }, \"def456\": { \"title\": \"World\", \"slug\": \"world\" } }',\n },\n singleton: {\n description: 'Single entry (e.g. site settings, hero section)',\n storage_format: 'JSON object with field key-value pairs: { \"title\": \"My Site\", \"description\": \"...\" }',\n },\n document: {\n description: 'Markdown files with YAML-like frontmatter',\n storage_format: 'Markdown file: ---\\\\nslug: my-post\\\\ntitle: My Post\\\\n---\\\\n\\\\nMarkdown body content here.',\n frontmatter_rules: [\n 'Delimited by --- on its own line',\n 'Key-value pairs: key: value',\n 'Arrays: key:\\\\n - item1\\\\n - item2',\n 'Inline arrays: key: [item1, item2]',\n 'Values auto-parsed: true/false -> boolean, integers -> number, quoted -> string',\n '\"body\" key is reserved for the markdown content below the frontmatter',\n ],\n },\n dictionary: {\n description: 'Flat key-value string map (e.g. i18n translation files)',\n storage_format: 'JSON object: { \"greeting\": \"Hello\", \"farewell\": \"Goodbye\" }',\n note: 'All values must be strings.',\n },\n },\n meta_files: {\n description: 'Metadata is stored separately from content in .contentrain/meta/{model-id}/',\n collection_meta: 'Object-map: { \"entry-id\": { \"status\": \"draft\", \"source\": \"agent\", \"updated_by\": \"contentrain-mcp\" } }',\n singleton_meta: 'Single object: { \"status\": \"published\", \"source\": \"human\", \"updated_by\": \"user@example.com\" }',\n fields: {\n status: 'Content lifecycle status: \"draft\" | \"in_review\" | \"published\" | \"rejected\" | \"archived\"',\n source: 'Who created the entry: \"agent\" | \"human\" | \"import\"',\n updated_by: 'Author identifier string',\n approved_by: 'Optional: approver identifier',\n version: 'Optional: version string',\n publish_at: 'Optional: ISO 8601 date for scheduled publishing',\n expire_at: 'Optional: ISO 8601 date for scheduled expiry (must be after publish_at)',\n },\n },\n locale_strategies: {\n description: 'How localized content files are organized. Set per-model via locale_strategy field.',\n strategies: {\n file: {\n description: 'Default. Each locale is a separate file in the model directory.',\n json_pattern: '{content-dir}/{locale}.json (e.g. en.json, tr.json)',\n md_pattern: '{content-dir}/{slug}/{locale}.md',\n },\n suffix: {\n description: 'Locale appended to filename.',\n json_pattern: '{content-dir}/{model-id}.{locale}.json',\n md_pattern: '{content-dir}/{slug}.{locale}.md',\n },\n directory: {\n description: 'Locale as subdirectory.',\n json_pattern: '{content-dir}/{locale}/{model-id}.json',\n md_pattern: '{content-dir}/{locale}/{slug}.md',\n },\n none: {\n description: 'No locale in path (single-language or externally managed).',\n json_pattern: '{content-dir}/{model-id}.json',\n md_pattern: '{content-dir}/{slug}.md',\n },\n },\n },\n i18n_disabled: {\n description: 'When a model has i18n:false, locale is ignored in file paths.',\n json_pattern: '{content-dir}/data.json (always \"data.json\", no locale suffix)',\n md_pattern: '{content-dir}/{slug}.md (no locale in path)',\n },\n canonical_serialization: {\n description: 'All JSON files use deterministic serialization for clean git diffs.',\n rules: [\n 'Object keys sorted alphabetically',\n '2-space indentation',\n 'Trailing newline at end of file',\n 'No trailing commas',\n 'UTF-8 encoding',\n ],\n },\n content_path_override: {\n description: 'Models can specify a custom content_path to store content outside .contentrain/content/.',\n example: 'A model with content_path: \"content/blog\" stores files at {project-root}/content/blog/ instead of .contentrain/content/{domain}/{model-id}/',\n note: 'This is useful for frameworks that expect content in specific directories (e.g. Nuxt Content, Astro).',\n },\n reading_content: {\n steps: [\n '1. Read .contentrain/config.json to get locales and workflow settings',\n '2. Read .contentrain/models/{model-id}.json to get the schema and model kind',\n '3. Determine content directory: model.content_path or .contentrain/content/{domain}/{model-id}/',\n '4. Determine file path using locale_strategy and i18n flag',\n '5. Parse JSON (collection/singleton/dictionary) or Markdown frontmatter (document)',\n '6. Optionally read .contentrain/meta/{model-id}/{locale}.json for status/metadata',\n ],\n },\n vocabulary: {\n description: 'Canonical terms for content consistency across models and locales.',\n file: '.contentrain/vocabulary.json',\n format: {\n version: 'number',\n terms: 'Record<category, Record<slug, translation_value>>',\n },\n usage: [\n 'Check vocabulary before creating dictionary entries — reuse canonical terms',\n 'If a new term is needed, consider adding it to vocabulary first',\n 'Vocabulary applies across ALL dictionary models and ALL locales',\n ],\n },\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(formatSpec, null, 2) }],\n }\n },\n )\n}\n\nasync function getSample(\n reader: import('../core/contracts/index.js').RepoReader,\n model: import('@contentrain/types').ModelDefinition,\n locale: string,\n): Promise<unknown> {\n const cDir = contentDirPath(model)\n\n if (model.kind === 'collection') {\n const data = await tryReadJson<Record<string, Record<string, unknown>>>(\n reader,\n contentFilePath(model, locale),\n )\n if (!data) return null\n const firstKey = Object.keys(data)[0]\n return firstKey ? { id: firstKey, ...data[firstKey] } : null\n }\n\n if (model.kind === 'singleton' || model.kind === 'dictionary') {\n return tryReadJson(reader, contentFilePath(model, locale))\n }\n\n if (model.kind === 'document') {\n const strategy = resolveLocaleStrategy(model)\n\n if (!model.i18n) {\n const entry = (await reader.listDirectory(cDir)).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'file') {\n const slug = (await reader.listDirectory(cDir))[0]\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'suffix') {\n const suffix = `.${locale}.md`\n const entry = (await reader.listDirectory(cDir)).find(file => file.endsWith(suffix))\n const slug = entry?.slice(0, -suffix.length)\n return slug ? { slug, locale } : null\n }\n\n if (strategy === 'directory') {\n const entry = (await reader.listDirectory(`${cDir}/${locale}`)).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n const entry = (await reader.listDirectory(cDir)).find(item => item.endsWith('.md'))\n const slug = entry?.replace(/\\.md$/u, '')\n return slug ? { slug, locale } : null\n }\n\n return null\n}\n\nasync function tryReadJson<T>(\n reader: import('../core/contracts/index.js').RepoReader,\n path: string,\n): Promise<T | null> {\n try {\n return JSON.parse(await reader.readFile(path)) as T\n } catch {\n return null\n }\n}\n\nfunction buildContentPath(\n model: import('@contentrain/types').ModelDefinition,\n locale: string,\n): string {\n const strategy = model.locale_strategy ?? 'file'\n const baseDir = model.content_path ?? `.contentrain/content/${model.domain}/${model.id}`\n\n if (model.kind === 'document') {\n // When i18n is disabled, documents are flat {slug}.md\n if (!model.i18n) return `${baseDir}/{slug}.md`\n\n // Documents use directories — show the base path pattern\n switch (strategy) {\n case 'suffix': return `${baseDir}/{slug}.${locale}.md`\n case 'directory': return `${baseDir}/${locale}/{slug}.md`\n case 'none': return `${baseDir}/{slug}.md`\n case 'file':\n default: return `${baseDir}/{slug}/${locale}.md`\n }\n }\n\n // JSON-based models (collection, singleton, dictionary)\n // When i18n is disabled, always data.json\n if (!model.i18n) return `${baseDir}/data.json`\n\n switch (strategy) {\n case 'suffix': return `${baseDir}/${model.id}.${locale}.json`\n case 'directory': return `${baseDir}/${locale}/${model.id}.json`\n case 'none': return `${baseDir}/${model.id}.json`\n case 'file':\n default: return `${baseDir}/${locale}.json`\n }\n}\n\nfunction generateImportSnippet(\n model: import('@contentrain/types').ModelDefinition,\n stack: string,\n locale: string,\n): Record<string, string> {\n const path = buildContentPath(model, locale)\n\n const snippets: Record<string, string> = {\n generic: `import data from '${path}'`,\n }\n\n switch (stack) {\n case 'nuxt':\n snippets['nuxt'] = model.kind === 'document'\n ? `const { data } = await useAsyncData(() => queryContent('${model.domain}/${model.id}').locale('${locale}').find())`\n : `const { data } = await useFetch('/api/content/${model.id}?locale=${locale}')`\n break\n case 'next':\n snippets['next'] = `import data from '@/${path}'`\n break\n case 'astro':\n snippets['astro'] = `import data from '~/${path}'`\n break\n }\n\n return snippets\n}\n","/**\n * Emit a uniform \"capability not available\" response for tools that\n * require local filesystem access but are being driven by a remote\n * provider (HTTP + GitHubProvider at the moment). The agent can use\n * `capability_required` to decide whether to retry against a different\n * transport or surface the limitation to the user.\n */\nexport function capabilityError(tool: string, capability: string) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `${tool} requires local filesystem access.`,\n capability_required: capability,\n hint: 'This tool is unavailable when MCP is driven by a remote provider (e.g. GitHubProvider). Use a LocalProvider or the stdio transport.',\n }) }],\n isError: true as const,\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ContentrainConfig, Vocabulary } from '@contentrain/types'\nimport { z } from 'zod'\nimport { join } from 'node:path'\nimport { readFile, writeFile, appendFile } from 'node:fs/promises'\nimport { simpleGit } from 'simple-git'\nimport type { ToolProvider } from '../server.js'\nimport { detectStack } from '../util/detect.js'\nimport { contentrainDir, ensureDir, pathExists, writeJson } from '../util/fs.js'\nimport { readConfig, readVocabulary } from '../core/config.js'\nimport { writeModel } from '../core/model-manager.js'\nimport { writeContent, type ContentEntry } from '../core/content-manager.js'\nimport { getTemplate, listTemplates } from '../templates/index.js'\nimport { createTransaction, buildBranchName, ensureContentBranch } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\n\nexport function registerSetupTools(\n server: McpServer,\n _provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n // ─── contentrain_init ───\n server.tool(\n 'contentrain_init',\n 'Initialize .contentrain/ structure. Changes are auto-committed to git — do NOT manually create .contentrain/ files.',\n {\n stack: z.string().optional().describe('Framework stack (nuxt, next, astro, svelte, react-vite, other). Auto-detected if omitted.'),\n locales: z.array(z.string()).optional().describe('Supported locales. Default: [\"en\"]'),\n domains: z.array(z.string()).optional().describe('Content domains. Default: auto-suggested'),\n },\n TOOL_ANNOTATIONS['contentrain_init']!,\n async ({ stack, locales, domains }) => {\n if (!projectRoot) return capabilityError('contentrain_init', 'localWorktree')\n const crDir = contentrainDir(projectRoot)\n\n // Already initialized?\n if (await pathExists(join(crDir, 'config.json'))) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Already initialized',\n suggestion: 'Use contentrain_status to see current state',\n }) }],\n isError: true,\n }\n }\n\n // Detect stack\n const detectedStack = stack ?? await detectStack(projectRoot)\n const supportedLocales = locales ?? ['en']\n const suggestedDomains = domains ?? suggestDomains(projectRoot)\n\n // Ensure .git exists and has at least one commit.\n //\n // `createTransaction` forks a worktree off the project's current\n // branch via `ensureContentBranch`, which calls `git branch\n // contentrain <base>`. An empty repo (zero commits) has no base\n // ref to fork from and the whole transaction fails. Parity with\n // `contentrain init` CLI command: if we just ran `git init` — or\n // the existing repo happens to be commit-free — seed an\n // `--allow-empty` initial commit so the branch operation has a\n // ref to anchor on.\n const hasGit = await pathExists(join(projectRoot, '.git'))\n const git = simpleGit(projectRoot)\n if (!hasGit) {\n await git.init()\n }\n const hasAnyCommit = await git.raw(['rev-list', '-n', '1', '--all']).then(out => out.trim().length > 0).catch(() => false)\n if (!hasAnyCommit) {\n await git.commit('initial commit', { '--allow-empty': null })\n }\n\n // Branch health gate\n const initHealth = await checkBranchHealth(projectRoot)\n if (initHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: initHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Create git transaction\n const branch = buildBranchName('new', 'init')\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n const wtCrDir = join(wt, '.contentrain')\n\n // Create directories with .gitkeep so git tracks them\n await Promise.all(['models', 'content', 'meta'].map(async (dir) => {\n await ensureDir(join(wtCrDir, dir))\n await writeFile(join(wtCrDir, dir, '.gitkeep'), '', 'utf-8')\n }))\n\n // Write config\n const config: ContentrainConfig = {\n version: 1,\n stack: detectedStack as ContentrainConfig['stack'],\n workflow: 'auto-merge',\n locales: {\n default: supportedLocales[0] ?? 'en',\n supported: supportedLocales,\n },\n domains: suggestedDomains,\n }\n await writeJson(join(wtCrDir, 'config.json'), config)\n\n // Write empty vocabulary\n const vocabulary: Vocabulary = { version: 1, terms: {} }\n await writeJson(join(wtCrDir, 'vocabulary.json'), vocabulary)\n\n // Write initial context\n await writeJson(join(wtCrDir, 'context.json'), {\n version: '1',\n lastOperation: {\n tool: 'contentrain_init',\n model: '',\n locale: supportedLocales[0] ?? 'en',\n timestamp: new Date().toISOString(),\n source: process.env['CONTENTRAIN_SOURCE'] === 'mcp-studio' ? 'mcp-studio' : 'mcp-local',\n },\n stats: { models: 0, entries: 0, locales: supportedLocales, lastSync: new Date().toISOString() },\n })\n\n // Update .gitignore\n await updateGitignore(wt)\n })\n\n await tx.commit('[contentrain] init: project setup', {\n tool: 'contentrain_init',\n model: '',\n locale: supportedLocales[0] ?? 'en',\n })\n const gitResult = await tx.complete()\n\n // Ensure the contentrain branch exists after init\n await ensureContentBranch(projectRoot)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Project initialized and committed to git. Do NOT manually edit .contentrain/ files.',\n config_created: '.contentrain/config.json',\n detected_stack: detectedStack,\n detected_locales: supportedLocales,\n suggested_domains: suggestedDomains,\n files_created: [\n '.contentrain/config.json',\n '.contentrain/vocabulary.json',\n '.contentrain/context.json',\n ],\n directories_created: [\n '.contentrain/models/',\n '.contentrain/content/',\n '.contentrain/meta/',\n ],\n gitignore_updated: true,\n git: { branch, action: gitResult.action, commit: gitResult.commit, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\n next_steps: [\n 'Create models with contentrain_model_save or contentrain_scaffold',\n 'Use contentrain_scan to find hardcoded strings',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Init failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n\n // ─── contentrain_scaffold ───\n server.tool(\n 'contentrain_scaffold',\n `Template-based project setup. Available templates: ${listTemplates().join(', ')}. Changes are auto-committed to git.`,\n {\n template: z.string().describe('Template ID: blog, landing, docs, ecommerce, saas, i18n, mobile'),\n locales: z.array(z.string()).optional().describe('Override locales'),\n with_sample_content: z.boolean().optional().default(true).describe('Include sample content (default: true)'),\n },\n TOOL_ANNOTATIONS['contentrain_scaffold']!,\n async ({ template: templateId, locales, with_sample_content }) => {\n if (!projectRoot) return capabilityError('contentrain_scaffold', 'localWorktree')\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Project not initialized. Run contentrain_init first.',\n }) }],\n isError: true,\n }\n }\n\n const tmpl = getTemplate(templateId)\n if (!tmpl) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Unknown template: \"${templateId}\". Available: ${listTemplates().join(', ')}`,\n }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const scaffoldHealth = await checkBranchHealth(projectRoot)\n if (scaffoldHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: scaffoldHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const effectiveLocales = locales ?? config.locales.supported\n const defaultLocale = effectiveLocales[0] ?? 'en'\n\n const branch = buildBranchName('new', `scaffold-${templateId}`, defaultLocale)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n const modelsCreated: Array<{ id: string; kind: string; fields: number }> = []\n let contentCreated = 0\n let vocabAdded = 0\n\n await tx.write(async (wt) => {\n // Write models\n await Promise.all(tmpl.models.map(async (model) => {\n await writeModel(wt, model)\n modelsCreated.push({\n id: model.id,\n kind: model.kind,\n fields: model.fields ? Object.keys(model.fields).length : 0,\n })\n }))\n\n // Write sample content via content pipeline (handles content_path, locale_strategy, meta)\n if (with_sample_content && tmpl.sample_content) {\n contentCreated = await writeSampleContent(wt, tmpl, effectiveLocales, config)\n }\n\n // Merge vocabulary\n if (tmpl.vocabulary) {\n vocabAdded = await mergeVocabulary(wt, tmpl.vocabulary, effectiveLocales)\n }\n\n })\n\n await tx.commit(`[contentrain] scaffold: ${templateId} (${defaultLocale})`, {\n tool: 'contentrain_scaffold',\n model: tmpl.models.map(m => m.id).join(', '),\n locale: defaultLocale,\n })\n const gitResult = await tx.complete()\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Scaffold applied and committed to git. Do NOT manually edit .contentrain/ files.',\n models_created: modelsCreated,\n content_created: contentCreated,\n vocabulary_terms_added: vocabAdded,\n git: { branch, action: gitResult.action, commit: gitResult.commit, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\n context_updated: true,\n next_steps: [\n 'Customize models with contentrain_model_save',\n 'Add content with contentrain_content_save',\n 'Run contentrain_submit when ready',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scaffold failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n },\n )\n}\n\nfunction suggestDomains(_projectRoot: string): string[] {\n return ['marketing', 'blog', 'system']\n}\n\nasync function updateGitignore(projectRoot: string): Promise<void> {\n const gitignorePath = join(projectRoot, '.gitignore')\n const cacheEntry = '.contentrain/.cache/'\n\n if (await pathExists(gitignorePath)) {\n const content = await readFile(gitignorePath, 'utf-8')\n if (!content.includes(cacheEntry)) {\n await appendFile(gitignorePath, `\\n# Contentrain cache\\n${cacheEntry}\\n`)\n }\n } else {\n await writeFile(gitignorePath, `# Contentrain cache\\n${cacheEntry}\\n`, 'utf-8')\n }\n}\n\nasync function writeSampleContent(\n worktreePath: string,\n tmpl: import('@contentrain/types').ScaffoldTemplate,\n locales: string[],\n config: import('@contentrain/types').ContentrainConfig,\n): Promise<number> {\n if (!tmpl.sample_content) return 0\n\n let count = 0\n\n for (const model of tmpl.models) {\n const sampleData = tmpl.sample_content[model.id]\n if (!sampleData) continue\n\n for (const [localeOrKey, data] of Object.entries(sampleData)) {\n const entryMap = data as Record<string, Record<string, unknown>>\n let targetLocale: string\n if (localeOrKey === 'data') {\n targetLocale = locales[0] ?? 'en'\n } else if (locales.includes(localeOrKey)) {\n targetLocale = localeOrKey\n } else {\n continue\n }\n\n // Convert template payload to ContentEntry[] based on model kind\n let entries: ContentEntry[]\n\n if (model.kind === 'singleton' || model.kind === 'dictionary') {\n // Singleton/dictionary: the entire locale object IS the data for one entry\n entries = [{ locale: targetLocale, data: entryMap as Record<string, unknown> }]\n } else if (model.kind === 'document') {\n // Document: keys are slugs, values are frontmatter objects\n entries = Object.entries(entryMap).map(([slug, fields]) => ({\n slug,\n locale: targetLocale,\n data: fields,\n }))\n } else {\n // Collection: keys are entry IDs, values are field objects\n entries = Object.entries(entryMap).map(([id, fields]) => ({\n id,\n locale: targetLocale,\n data: fields,\n }))\n }\n\n await writeContent(worktreePath, model, entries, config)\n count += entries.length\n }\n }\n\n return count\n}\n\nasync function mergeVocabulary(\n projectRoot: string,\n newTerms: Record<string, Record<string, string>>,\n _locales: string[],\n): Promise<number> {\n const vocabPath = join(contentrainDir(projectRoot), 'vocabulary.json')\n const existing = await readVocabulary(projectRoot)\n const vocab: Vocabulary = existing ?? { version: 1, terms: {} }\n\n let added = 0\n for (const [key, translations] of Object.entries(newTerms)) {\n if (!(key in vocab.terms)) {\n added++\n }\n vocab.terms[key] = { ...vocab.terms[key], ...translations }\n }\n\n await writeJson(vocabPath, vocab)\n return added\n}\n","import { CONTENTRAIN_BRANCH } from '@contentrain/types'\nimport type { FileChange } from '../core/contracts/index.js'\nimport { buildContextChange } from '../core/context.js'\nimport { OverlayReader } from '../core/overlay-reader.js'\nimport { LocalProvider } from '../providers/local/index.js'\nimport type { ToolProvider } from '../server.js'\n\n/**\n * Context payload written into `.contentrain/context.json` as part of the\n * same commit. Kept as a loose object so tool-specific payloads can add\n * optional fields (`locale`, `entries`) without churning the helper's\n * signature.\n */\nexport interface CommitContextPayload {\n tool: string\n model: string\n locale?: string\n entries?: string[]\n}\n\nexport interface CommitThroughProviderInput {\n branch: string\n changes: FileChange[]\n message: string\n contextPayload: CommitContextPayload\n}\n\nexport interface CommitThroughProviderResult {\n commitSha: string\n workflowAction: 'auto-merged' | 'pending-review'\n sync?: unknown\n}\n\n/**\n * Commit a plan's changes through whichever provider is wired into the\n * tool handler. Encapsulates the LocalProvider vs remote RepoProvider\n * dispatch that every write-side tool (content_save, content_delete,\n * model_save, model_delete) used to inline:\n *\n * - **LocalProvider** — goes through the worktree-backed transaction.\n * `context` payload is threaded as an extra write-through and the\n * transaction layer decides `auto-merged` vs `pending-review` based on\n * the project's configured workflow. Selective-sync result is surfaced\n * to the caller via `sync`.\n *\n * - **Any other RepoProvider** — the context.json write becomes an extra\n * `FileChange` bundled into the plan so the whole commit lands\n * atomically through the generic `RepoWriter.applyPlan`. Remote flows\n * always report `pending-review`; Studio (or whatever orchestrator is\n * driving the server) owns the merge.\n *\n * The return shape is deliberately uniform so callers don't have to\n * branch on provider type again.\n */\nexport async function commitThroughProvider(\n provider: ToolProvider,\n input: CommitThroughProviderInput,\n): Promise<CommitThroughProviderResult> {\n const { branch, changes, message, contextPayload } = input\n\n if (provider instanceof LocalProvider) {\n const result = await provider.applyPlan({\n branch,\n changes,\n message,\n context: contextPayload,\n })\n return {\n commitSha: result.sha,\n workflowAction: result.workflowAction,\n sync: result.sync,\n }\n }\n\n // Build context.json against an overlay of the pending FileChanges so\n // `stats.entries` / `stats.models` reflect the state *after* this\n // commit lands, not the pre-change base branch. Without the overlay\n // the committed context.json would be stale — a new entry added in\n // this commit would not appear in the entry count until the next\n // write.\n const overlay = new OverlayReader(provider, changes)\n const contextChange = await buildContextChange(overlay, contextPayload)\n const allChanges = [...changes, contextChange]\n .toSorted((a, b) => a.path.localeCompare(b.path))\n // Feature branches ALWAYS fork from the `contentrain` branch — that's\n // the single source of truth the local transaction flow enforces, and\n // the remote flow must match it so Studio's cr/* → contentrain →\n // defaultBranch model stays consistent. `config.repository.default_branch`\n // names the repo's primary branch (main / master / trunk) — that is\n // NOT the content tracking branch, only the downstream target.\n const commit = await provider.applyPlan({\n branch,\n changes: allChanges,\n message,\n author: { name: 'Contentrain', email: 'mcp@contentrain.io' },\n base: CONTENTRAIN_BRANCH,\n })\n return {\n commitSha: commit.sha,\n workflowAction: 'pending-review',\n }\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ModelDefinition } from '@contentrain/types'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig } from '../core/config.js'\n\nimport { resolveContentDir, resolveJsonFilePath, resolveMdFilePath } from '../core/content-manager.js'\nimport { checkReferences, readModel, validateModelDefinition, fieldDefZodSchema } from '../core/model-manager.js'\nimport { planModelDelete, planModelSave } from '../core/ops/index.js'\nimport { LocalProvider } from '../providers/local/index.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { commitThroughProvider } from './commit-plan.js'\n\n// Shared field definition schema — single source of truth with normalize extract\nconst fieldDefSchema = fieldDefZodSchema\n\nexport function registerModelTools(\n server: McpServer,\n provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n // ─── contentrain_model_save ───\n server.tool(\n 'contentrain_model_save',\n 'Create or update a model definition. Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n id: z.string().describe('Model ID (kebab-case, e.g. \"blog-post\")'),\n name: z.string().describe('Human-readable name'),\n kind: z.enum(['singleton', 'collection', 'document', 'dictionary']).describe('Model kind'),\n domain: z.string().describe('Content domain (e.g. \"blog\", \"marketing\", \"system\")'),\n i18n: z.boolean().describe('Whether this model supports localization'),\n description: z.string().optional().describe('Model description'),\n fields: fieldDefSchema.optional().describe('Field definitions (not needed for dictionary)'),\n content_path: z.string().optional().describe('Framework-relative path for content files (e.g. \"content/blog\", \"locales\"). When set, content is written here instead of .contentrain/content/'),\n locale_strategy: z.enum(['file', 'suffix', 'directory', 'none']).optional().describe('How locale is encoded in file names. Default: \"file\"'),\n },\n TOOL_ANNOTATIONS['contentrain_model_save']!,\n async (input) => {\n const config = await readConfig(provider)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const errors = validateModel(input)\n if (errors.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Validation failed', details: errors }) }],\n isError: true,\n }\n }\n\n if (input.locale_strategy === 'none' && input.i18n !== false) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'locale_strategy \"none\" requires i18n:false. The \"none\" strategy stores a single file without locale encoding, which is incompatible with multi-locale content. Use \"file\", \"suffix\", or \"directory\" for i18n models.',\n }) }],\n isError: true,\n }\n }\n\n if (provider instanceof LocalProvider) {\n const health = await checkBranchHealth(provider.projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n }\n\n const model: ModelDefinition = {\n id: input.id,\n name: input.name,\n kind: input.kind,\n domain: input.domain,\n i18n: input.i18n,\n description: input.description,\n fields: input.fields as ModelDefinition['fields'],\n content_path: input.content_path,\n locale_strategy: input.locale_strategy,\n }\n\n const savePlan = await planModelSave(provider, { model })\n const action = savePlan.result.action\n const branch = buildBranchName('model', input.id)\n const message = `[contentrain] ${action}: ${input.id}`\n const contextPayload = { tool: 'contentrain_model_save', model: input.id }\n\n let commitSha: string\n let workflowAction: 'auto-merged' | 'pending-review'\n let sync: unknown\n\n try {\n const result = await commitThroughProvider(provider, {\n branch,\n changes: savePlan.changes,\n message,\n contextPayload,\n })\n commitSha = result.commitSha\n workflowAction = result.workflowAction\n sync = result.sync\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model save failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n\n // DX helpers (import snippet, example path) are only computed when we\n // can resolve local paths — they do not apply to remote-only flows.\n const defaultLocale = config.locales.default\n const contentPath = model.content_path ?? `.contentrain/content/${input.domain}/${input.id}`\n let displayPath: string | undefined\n let importSnippet: Record<string, string> | undefined\n if (projectRoot) {\n const contentDir = resolveContentDir(projectRoot, model)\n const exampleFilePath = model.kind === 'document'\n ? resolveMdFilePath(contentDir, model, defaultLocale, '{slug}')\n : resolveJsonFilePath(contentDir, model, defaultLocale)\n displayPath = exampleFilePath.replace(projectRoot + '/', '').replace(projectRoot, '')\n importSnippet = {\n generic: `import data from '${displayPath}'`,\n }\n if (config.stack === 'nuxt') {\n importSnippet['nuxt'] = model.kind === 'document'\n ? `const { data } = await useAsyncData(() => queryContent('${model.domain}/${model.id}').locale('${defaultLocale}').find())`\n : `const { data } = await useFetch('/api/content/${model.id}?locale=${defaultLocale}')`\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Model saved and committed to git. Do NOT manually edit .contentrain/ files.',\n action,\n model: input.id,\n validation: { valid: true, errors: [] },\n git: { branch, action: workflowAction, commit: commitSha, ...(sync ? { sync } : {}) },\n context_updated: true,\n content_path: contentPath + '/',\n ...(displayPath ? { example_file: displayPath } : {}),\n ...(importSnippet ? { import_snippet: importSnippet } : {}),\n next_steps: ['Add content with contentrain_content_save'],\n }, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_model_delete ───\n server.tool(\n 'contentrain_model_delete',\n 'Delete a model and its content/meta. Changes are auto-committed to git — do NOT manually edit .contentrain/ files.',\n {\n model: z.string().describe('Model ID to delete'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n TOOL_ANNOTATIONS['contentrain_model_delete']!,\n async ({ model: modelId }) => {\n const config = await readConfig(provider)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n const existing = await readModel(provider, modelId)\n if (!existing) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${modelId}\" not found` }) }],\n isError: true,\n }\n }\n\n if (provider instanceof LocalProvider) {\n const deleteHealth = await checkBranchHealth(provider.projectRoot)\n if (deleteHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n }\n\n // Reference integrity — both local and remote now go through the\n // reader-based checkReferences so deletes on GitHubProvider get the\n // same REFERENCED_MODEL guard as local flows.\n const refs = await checkReferences(provider, modelId)\n if (refs.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n deleted: false,\n error: 'REFERENCED_MODEL',\n referenced_by: refs,\n next_steps: ['Remove relation fields from referencing models first'],\n }, null, 2) }],\n }\n }\n\n const deletePlan = await planModelDelete(provider, { model: existing })\n const branch = buildBranchName('model', modelId)\n const message = `[contentrain] delete: ${modelId}`\n const contextPayload = { tool: 'contentrain_model_delete', model: modelId }\n\n let commitSha: string\n let workflowAction: 'auto-merged' | 'pending-review'\n let sync: unknown\n\n try {\n const result = await commitThroughProvider(provider, {\n branch,\n changes: deletePlan.changes,\n message,\n contextPayload,\n })\n commitSha = result.commitSha\n workflowAction = result.workflowAction\n sync = result.sync\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Model deleted and committed to git. Do NOT manually edit .contentrain/ files.',\n deleted: true,\n git: { branch, action: workflowAction, commit: commitSha, ...(sync ? { sync } : {}) },\n files_removed: deletePlan.result,\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n\n// validateModel is now validateModelDefinition from model-manager.ts (shared)\nconst validateModel = validateModelDefinition\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig, readVocabulary } from '../core/config.js'\nimport { readModel } from '../core/model-manager.js'\nimport { listContent } from '../core/content-manager.js'\nimport { planContentDelete, planContentSave } from '../core/ops/index.js'\nimport { LocalProvider } from '../providers/local/index.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { validateProject } from '../core/validator/index.js'\nimport { OverlayReader } from '../core/overlay-reader.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { commitThroughProvider } from './commit-plan.js'\n\nexport function registerContentTools(\n server: McpServer,\n provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n // ─── contentrain_content_save ───\n server.tool(\n 'contentrain_content_save',\n 'Save content entries. Entry format varies by model kind: DICTIONARY — provide \"locale\" and \"data\" (flat key-value, all string values); \"id\" and \"slug\" are ignored; data keys are the identities. COLLECTION — provide \"locale\" and \"data\"; \"id\" is optional (auto-generated if omitted); \"slug\" is ignored. DOCUMENT — provide \"slug\" (required), \"locale\", and \"data\"; use the \"body\" key inside data for markdown content. SINGLETON — provide only \"locale\" and \"data\". Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n model: z.string().describe('Model ID'),\n entries: z.array(z.object({\n id: z.string().optional().describe('Entry ID (collection only, auto-generated if omitted)'),\n slug: z.string().optional().describe('Slug (document only)'),\n locale: z.string().optional().describe('Locale code (defaults to config default)'),\n data: z.record(z.string(), z.unknown()).describe('Content data. For documents, include \"body\" key for markdown content.'),\n publish_at: z.string().optional().describe('ISO 8601 date for scheduled publishing (stored in meta)'),\n expire_at: z.string().optional().describe('ISO 8601 date for scheduled expiry (stored in meta, must be after publish_at)'),\n })).describe('Content entries to save'),\n },\n TOOL_ANNOTATIONS['contentrain_content_save']!,\n async (input) => {\n const config = await readConfig(provider)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(provider, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Validate locales and scheduling fields before starting transaction\n for (const entry of input.entries) {\n if (entry.locale && !config.locales.supported.includes(entry.locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Locale \"${entry.locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // Validate publish_at / expire_at\n if (entry.publish_at !== undefined) {\n const d = new Date(entry.publish_at)\n if (Number.isNaN(d.getTime())) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Invalid publish_at date: \"${entry.publish_at}\". Must be a valid ISO 8601 date string.`,\n }) }],\n isError: true,\n }\n }\n }\n if (entry.expire_at !== undefined) {\n const d = new Date(entry.expire_at)\n if (Number.isNaN(d.getTime())) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Invalid expire_at date: \"${entry.expire_at}\". Must be a valid ISO 8601 date string.`,\n }) }],\n isError: true,\n }\n }\n }\n if (entry.publish_at !== undefined && entry.expire_at !== undefined) {\n if (new Date(entry.expire_at) <= new Date(entry.publish_at)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `expire_at (\"${entry.expire_at}\") must be after publish_at (\"${entry.publish_at}\").`,\n }) }],\n isError: true,\n }\n }\n }\n\n // Merge scheduling fields into data so meta-manager picks them up\n if (entry.publish_at !== undefined) entry.data['publish_at'] = entry.publish_at\n if (entry.expire_at !== undefined) entry.data['expire_at'] = entry.expire_at\n }\n\n // Branch health gate — LocalProvider only (git branch count check).\n if (provider instanceof LocalProvider) {\n const health = await checkBranchHealth(provider.projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n }\n\n const vocabulary = await readVocabulary(provider)\n\n let plan: Awaited<ReturnType<typeof planContentSave>>\n try {\n plan = await planContentSave(provider, {\n model,\n entries: input.entries,\n config,\n vocabulary,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Content save failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n\n const entryIds = plan.result\n .map(r => r.id ?? r.slug ?? r.locale)\n .filter((v): v is string => Boolean(v))\n\n const branch = buildBranchName('content', input.model)\n const message = `[contentrain] content: ${input.model}`\n const contextPayload = {\n tool: 'contentrain_content_save',\n model: input.model,\n locale: input.entries[0]?.locale,\n entries: entryIds,\n }\n\n let commitSha: string\n let workflowAction: 'auto-merged' | 'pending-review'\n let sync: unknown\n\n try {\n const result = await commitThroughProvider(provider, {\n branch,\n changes: plan.changes,\n message,\n contextPayload,\n })\n commitSha = result.commitSha\n workflowAction = result.workflowAction\n sync = result.sync\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Content save failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n\n // Post-save validation — runs against whichever provider backed the\n // write. LocalProvider sees the post-commit state via its\n // filesystem (the worktree transaction has already landed the\n // files). Remote providers see the pre-commit state in their\n // reader; wrap the reader in an OverlayReader so the validator\n // evaluates the committed-but-not-yet-visible state instead of\n // the pre-change base branch.\n const validationResult = projectRoot\n ? await validateProject(projectRoot, { model: input.model })\n : await validateProject(new OverlayReader(provider, plan.changes), { model: input.model })\n\n const allAdvisories = plan.result.flatMap(r => r.advisories ?? [])\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Content saved and committed to git. Do NOT manually edit .contentrain/ files.',\n results: plan.result,\n git: { branch, action: workflowAction, commit: commitSha, ...(sync ? { sync } : {}) },\n ...(allAdvisories.length > 0 ? {\n advisories: allAdvisories,\n advisory_note: 'Save succeeded. Review these warnings and consider consolidating duplicate values.',\n } : {}),\n validation: {\n valid: validationResult.valid,\n errors: validationResult.issues.filter(i => i.severity === 'error').map(i => i.message),\n },\n context_updated: true,\n next_steps: [\n ...(allAdvisories.length > 0 ? ['ADVISORY: Duplicate values detected — review advisories above'] : []),\n ...(!validationResult.valid ? ['WARNING: Content has validation errors — run contentrain_validate for details'] : []),\n ...(model.kind === 'collection'\n ? ['Use contentrain_content_list to verify', 'Add more entries or publish']\n : ['Use contentrain_content_list to verify']),\n ],\n }, null, 2) }],\n }\n },\n )\n\n // ─── contentrain_content_delete ───\n server.tool(\n 'contentrain_content_delete',\n 'Delete content entries. For dictionaries, use \"keys\" to remove specific keys (omit to delete entire locale file). Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.',\n {\n model: z.string().describe('Model ID'),\n id: z.string().optional().describe('Entry ID (collection)'),\n slug: z.string().optional().describe('Slug (document)'),\n locale: z.string().optional().describe('Locale code'),\n keys: z.array(z.string()).optional().describe('Dictionary only: specific keys to remove. Omit to delete entire locale file.'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n TOOL_ANNOTATIONS['contentrain_content_delete']!,\n async (input) => {\n const config = await readConfig(provider)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(provider, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n if (provider instanceof LocalProvider) {\n const deleteHealth = await checkBranchHealth(provider.projectRoot)\n if (deleteHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n }\n\n let deletePlan: Awaited<ReturnType<typeof planContentDelete>>\n try {\n deletePlan = await planContentDelete(provider, {\n model,\n id: input.id,\n slug: input.slug,\n locale: input.locale,\n keys: input.keys,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('content', input.model)\n const message = `[contentrain] delete content: ${input.model}`\n const contextPayload = {\n tool: 'contentrain_content_delete',\n model: input.model,\n locale: input.locale,\n }\n\n let commitSha: string\n let workflowAction: 'auto-merged' | 'pending-review'\n let sync: unknown\n\n try {\n const result = await commitThroughProvider(provider, {\n branch,\n changes: deletePlan.changes,\n message,\n contextPayload,\n })\n commitSha = result.commitSha\n workflowAction = result.workflowAction\n sync = result.sync\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: 'Content deleted and committed to git. Do NOT manually edit .contentrain/ files.',\n deleted: true,\n files_removed: deletePlan.result,\n git: { branch, action: workflowAction, commit: commitSha, ...(sync ? { sync } : {}) },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Delete failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_content_list ───\n server.tool(\n 'contentrain_content_list',\n 'List content entries (read-only). Returns data from .contentrain/ — do NOT manually create or modify content files.',\n {\n model: z.string().describe('Model ID'),\n locale: z.string().optional().describe('Locale code (defaults to config default)'),\n filter: z.record(z.string(), z.unknown()).optional().describe('Filter criteria (collection only)'),\n resolve: z.boolean().optional().describe('Resolve relation fields to actual data'),\n limit: z.number().optional().describe('Max entries to return'),\n offset: z.number().optional().describe('Skip N entries'),\n },\n TOOL_ANNOTATIONS['contentrain_content_list']!,\n async (input) => {\n const config = await readConfig(provider)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(provider, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n try {\n // LocalProvider path keeps the legacy filesystem implementation\n // (with full `resolve:true` relation hydration). Remote providers\n // get the reader-based path — `resolve:true` is rejected there\n // because cross-model relation walks need local disk today.\n const listOpts = {\n locale: input.locale,\n filter: input.filter as Record<string, unknown>,\n resolve: input.resolve,\n limit: input.limit,\n offset: input.offset,\n }\n const result = projectRoot\n ? await listContent(projectRoot, model, listOpts, config)\n : await listContent(provider, model, listOpts, config)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `List failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\nimport { z } from 'zod'\nimport { simpleGit } from 'simple-git'\nimport type { ToolProvider } from '../server.js'\nimport { validateProject } from '../core/validator/index.js'\nimport { readConfig } from '../core/config.js'\nimport { createTransaction, buildBranchName, mergeBranch } from '../git/transaction.js'\nimport { checkBranchHealth, cleanupMergedBranches } from '../git/branch-lifecycle.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\n\nexport function registerWorkflowTools(\n server: McpServer,\n provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n // ─── contentrain_validate ───\n server.tool(\n 'contentrain_validate',\n 'Validate project content against model schemas. Detects required field violations, type mismatches, broken relations, secret leaks, i18n parity issues, and more. If fix:true, auto-fixes structural issues (canonical sort, orphan meta, missing locale files) — do NOT manually edit .contentrain/ files.',\n {\n model: z.string().optional().describe('Model ID to validate (omit for all models)'),\n fix: z.boolean().optional().describe('Auto-fix structural issues (canonical sort, orphan meta, missing locale files). Default: false'),\n },\n TOOL_ANNOTATIONS['contentrain_validate']!,\n async (input) => {\n // Read-only validate runs over any provider (LocalProvider or remote\n // GitHubProvider). Fix mode still needs a local worktree — it opens a\n // git transaction — so it short-circuits with a capability error when\n // no projectRoot is available.\n if (input.fix && !projectRoot) {\n return capabilityError('contentrain_validate', 'localWorktree')\n }\n\n const config = await readConfig(provider)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n try {\n let result: Awaited<ReturnType<typeof validateProject>> | undefined\n\n if (input.fix && projectRoot) {\n // Branch health gate for fix mode (creates a branch)\n const fixHealth = await checkBranchHealth(projectRoot)\n if (fixHealth.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: fixHealth.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before auto-fixing.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n // Use git transaction for fixes\n const branch = buildBranchName('fix', 'validate')\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n await tx.write(async (wt) => {\n result = await validateProject(wt, { model: input.model, fix: true })\n })\n\n if (result!.fixed > 0) {\n await tx.commit(`[contentrain] validate: auto-fix ${result!.fixed} issue(s)`, {\n tool: 'contentrain_validate',\n model: input.model ?? '*',\n })\n const gitResult = await tx.complete()\n\n const nextSteps: string[] = []\n if (result!.summary.errors > 0) nextSteps.push('Fix remaining errors manually')\n if (result!.summary.warnings > 0) nextSteps.push('Review warnings')\n nextSteps.push('Run contentrain_validate again to verify')\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: `Validation complete. ${result!.fixed} issue(s) auto-fixed and committed to git. Do NOT manually edit .contentrain/ files.`,\n ...result!,\n git: { branch, action: gitResult.action, commit: gitResult.commit, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\n context_updated: true,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } else {\n // Nothing to fix, cleanup the branch\n await tx.cleanup()\n }\n } catch (error) {\n await tx.cleanup()\n throw error\n } finally {\n await tx.cleanup()\n }\n }\n\n // No fix or nothing was fixed — run read-only validation. Prefer the\n // local disk walk when we have a projectRoot (identical behavior to\n // pre-5.5 callers); otherwise drive the reader-based path so remote\n // providers stay supported.\n if (!result) {\n result = projectRoot\n ? await validateProject(projectRoot, { model: input.model, fix: false })\n : await validateProject(provider, { model: input.model, fix: false })\n }\n\n const nextSteps: string[] = []\n if (result.summary.errors > 0) nextSteps.push('Fix errors in content using contentrain_content_save')\n if (result.summary.warnings > 0) nextSteps.push('Review warnings')\n if (result.valid) nextSteps.push('Run contentrain_submit to push changes')\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'validated',\n message: result.valid\n ? 'All validation checks passed.'\n : `Validation found ${result.summary.errors} error(s) and ${result.summary.warnings} warning(s).`,\n ...result,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Validation failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_submit ───\n server.tool(\n 'contentrain_submit',\n 'Push contentrain/* branches to remote. MCP is push-only — PR creation is handled by the platform. Do NOT manually push or create PRs.',\n {\n branches: z.array(z.string()).optional().describe('Specific branch names to push (omit for all contentrain/* branches)'),\n message: z.string().optional().describe('Optional message for the push operation'),\n },\n TOOL_ANNOTATIONS['contentrain_submit']!,\n async (input) => {\n // Submit pushes a branch to origin via simple-git — needs both a\n // local worktree (to enumerate cr/* branches) and pushRemote\n // (semantic capability name, even though the underlying code is\n // simple-git rather than a provider call).\n if (!provider.capabilities.localWorktree || !provider.capabilities.pushRemote || !projectRoot) {\n return capabilityError('contentrain_submit', 'localWorktree')\n }\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const git = simpleGit(projectRoot)\n const remoteName = process.env['CONTENTRAIN_REMOTE'] ?? 'origin'\n\n // Check remote exists\n let hasRemote = false\n try {\n const remotes = await git.getRemotes()\n hasRemote = remotes.some(r => r.name === remoteName)\n } catch {\n hasRemote = false\n }\n\n if (!hasRemote) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `No remote \"${remoteName}\" found. Configure a git remote first.`,\n next_steps: [`git remote add ${remoteName} <url>`],\n }) }],\n isError: true,\n }\n }\n\n try {\n // Determine the base branch for merge-status checks\n const baseBranch = config.repository?.default_branch\n ?? process.env['CONTENTRAIN_BRANCH']\n ?? ((await git.raw(['branch', '--show-current'])).trim() || 'main')\n\n // Get contentrain branches\n const branchSummary = await git.branchLocal()\n\n // Get branches NOT yet merged into base (only these need pushing)\n let unmergedRaw = ''\n try {\n unmergedRaw = await git.raw(['branch', '--no-merged', baseBranch])\n } catch {\n // If base branch doesn't exist yet, treat all as unmerged\n unmergedRaw = branchSummary.all.join('\\n')\n }\n const unmergedSet = new Set(\n unmergedRaw.split('\\n').map(b => b.replace(/^\\*?\\s+/, '').trim()).filter(Boolean),\n )\n\n let branchesToPush: string[]\n\n if (input.branches && input.branches.length > 0) {\n branchesToPush = input.branches.filter(b => branchSummary.all.includes(b))\n const missing = input.branches.filter(b => !branchSummary.all.includes(b))\n if (missing.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Branches not found: ${missing.join(', ')}`,\n }) }],\n isError: true,\n }\n }\n // Filter out already-merged branches from explicit list\n branchesToPush = branchesToPush.filter(b => unmergedSet.has(b))\n } else {\n branchesToPush = branchSummary.all.filter(b => b.startsWith('cr/') && unmergedSet.has(b))\n }\n\n if (branchesToPush.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'No unmerged cr/* branches found to push.',\n next_steps: ['Make changes first with contentrain_content_save or contentrain_model_save'],\n }) }],\n isError: true,\n }\n }\n\n // Push each branch\n const pushed: string[] = []\n const errors: Array<{ branch: string; error: string }> = []\n\n for (const branch of branchesToPush) {\n try {\n await git.push(remoteName, branch)\n pushed.push(branch)\n } catch (error) {\n errors.push({\n branch,\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n\n // Best-effort: push the contentrain branch itself\n try {\n await git.push(remoteName, CONTENTRAIN_BRANCH)\n } catch {\n // contentrain branch push is best-effort\n }\n\n // Lazy cleanup: delete merged branches after push\n const cleanup = await cleanupMergedBranches(projectRoot)\n\n const nextSteps: string[] = []\n if (pushed.length > 0) nextSteps.push('Create PRs on your git platform for review')\n if (pushed.length > 0) nextSteps.push('For merge: use contentrain_merge or direct user to http://localhost:3333/branches')\n if (errors.length > 0) nextSteps.push('Fix push errors and retry')\n if (cleanup.remaining >= 50) nextSteps.push(`Warning: ${cleanup.remaining} active contentrain branches. Consider reviewing old branches.`)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n message: `Pushed ${pushed.length} branch(es) to ${remoteName}.`,\n pushed,\n errors: errors.length > 0 ? errors : undefined,\n remote: remoteName,\n cleanup: cleanup.deleted > 0 ? { deleted: cleanup.deleted, remaining: cleanup.remaining } : undefined,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_merge ───\n server.tool(\n 'contentrain_merge',\n 'Merge a review-mode branch into contentrain. Local git operation — no external platform needed. Merges the feature branch into the contentrain branch, advances the base branch via update-ref, and selectively syncs .contentrain/ files to the working tree.',\n {\n branch: z.string().describe('Branch name to merge (e.g. cr/normalize/extract/...)'),\n confirm: z.literal(true).describe('Must be true to confirm the merge'),\n },\n TOOL_ANNOTATIONS['contentrain_merge']!,\n async (input) => {\n // Merge runs a local git transaction (worktree + update-ref +\n // selective sync). Remote providers do not expose this today; use\n // provider.mergeBranch directly from a Studio-style driver instead.\n if (!provider.capabilities.localWorktree || !projectRoot) {\n return capabilityError('contentrain_merge', 'localWorktree')\n }\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n try {\n // Verify branch exists\n const git = simpleGit(projectRoot)\n const branches = await git.branchLocal()\n if (!branches.all.includes(input.branch)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Branch \"${input.branch}\" not found locally.`,\n next_steps: ['Check branch name with git branch -l', 'Use contentrain_submit to push first if needed'],\n }) }],\n isError: true,\n }\n }\n\n const result = await mergeBranch(projectRoot, input.branch)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'merged',\n action: result.action,\n commit: result.commit,\n sync: result.sync,\n next_steps: [\n 'Run `npx contentrain generate` to update SDK client',\n 'Run contentrain_validate to verify content integrity',\n result.sync.skipped.length > 0\n ? `${result.sync.skipped.length} file(s) skipped due to local changes — resolve manually`\n : '',\n ].filter(Boolean),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Merge failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { FieldDef } from '@contentrain/types'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig } from '../core/config.js'\nimport { buildGraph } from '../core/graph-builder.js'\nimport { scanCandidates, scanSummary } from '../core/scanner.js'\nimport { applyExtract, applyReuse } from '../core/apply-manager.js'\nimport { fieldDefZodSchema } from '../core/model-manager.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\n\nexport function registerNormalizeTools(\n server: McpServer,\n provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n // ─── contentrain_scan ───\n server.tool(\n 'contentrain_scan',\n 'Scan project source code for content strings. Three modes: \"graph\" builds import/component graph for project intelligence, \"candidates\" extracts string literals with pre-filtering and pagination, \"summary\" provides quick overview stats. Read-only — no changes to disk or git. MCP finds strings deterministically; the agent decides what is content. Recommended workflow: start with \"summary\" or \"graph\" for orientation, then paginate through \"candidates\" to evaluate strings.',\n {\n mode: z.enum(['graph', 'candidates', 'summary']).optional().describe('Scan mode. Default: candidates'),\n paths: z.array(z.string()).optional().describe('Directories to scan (relative to project root). Default: auto-detect'),\n include: z.array(z.string()).optional().describe('File extensions to include. Default: .tsx, .jsx, .vue, .ts, .js, .mjs, .astro, .svelte'),\n exclude: z.array(z.string()).optional().describe('Additional directory names to exclude'),\n limit: z.number().optional().describe('Candidates mode: batch size. Default: 50'),\n offset: z.number().optional().describe('Candidates mode: pagination offset. Default: 0'),\n min_length: z.number().optional().describe('Candidates mode: minimum string length. Default: 2'),\n max_length: z.number().optional().describe('Candidates mode: maximum string length. Default: 500'),\n },\n TOOL_ANNOTATIONS['contentrain_scan']!,\n async (input) => {\n // AST scans require local disk access — GitHubProvider et al. expose\n // `astScan: false`, so this rejects with a uniform capability error.\n if (!provider.capabilities.astScan || !projectRoot) {\n return capabilityError('contentrain_scan', 'astScan')\n }\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const mode = input.mode ?? 'candidates'\n\n try {\n switch (mode) {\n case 'graph': {\n const graph = await buildGraph(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'graph',\n ...graph,\n next_steps: [\n 'Use mode:candidates to scan specific files/directories for strings',\n 'Focus on pages and components with high string counts first',\n ],\n }, null, 2) }],\n }\n }\n\n case 'candidates': {\n const result = await scanCandidates(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n limit: input.limit,\n offset: input.offset,\n min_length: input.min_length,\n max_length: input.max_length,\n })\n\n const nextSteps: string[] = []\n if (result.stats.has_more) {\n nextSteps.push(`Use offset:${(input.offset ?? 0) + (input.limit ?? 50)} for next batch`)\n }\n nextSteps.push('Evaluate each candidate: is it user-facing content? Which domain/model?')\n if (result.duplicates.length > 0) {\n nextSteps.push('Consider deduplicating repeated strings into shared models')\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'candidates',\n ...result,\n next_steps: nextSteps,\n }, null, 2) }],\n }\n }\n\n case 'summary': {\n const result = await scanSummary(projectRoot, {\n paths: input.paths,\n include: input.include,\n exclude: input.exclude,\n min_length: input.min_length,\n max_length: input.max_length,\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'summary',\n ...result,\n next_steps: [\n 'Use mode:graph for project structure analysis',\n 'Use mode:candidates to scan directories with most candidates',\n ],\n }, null, 2) }],\n }\n }\n\n default:\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown mode: ${mode}` }) }],\n isError: true,\n }\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scan failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_apply ───\n server.tool(\n 'contentrain_apply',\n 'Apply normalize operations. Two modes: \"extract\" writes agent-approved strings to Contentrain content files (source untouched), \"reuse\" patches source files with agent-provided replacement expressions. DRY RUN (default, dry_run:true): validates inputs, resolves conflicts, and returns a full preview — NO changes to disk or git. EXECUTE (dry_run:false): writes files to disk, commits to a branch, and requires branch health check to pass. Recommended workflow: always run dry_run first, review the preview, then call again with dry_run:false to execute. Normalize operations always use review workflow (never auto-merge).',\n {\n mode: z.enum(['extract', 'reuse']).describe('Apply mode: extract (content creation) or reuse (source patching)'),\n dry_run: z.boolean().optional().default(true).describe('Defaults to preview mode (dry_run:true). Set dry_run:false to execute after reviewing the preview.'),\n\n // Extract mode fields\n extractions: z.array(z.object({\n model: z.string().describe('Model ID (e.g. \"ui-texts\", \"hero-section\")'),\n kind: z.enum(['singleton', 'collection', 'dictionary', 'document']).describe('Model kind'),\n domain: z.string().describe('Content domain (e.g. \"marketing\", \"app\")'),\n i18n: z.boolean().optional().describe('Enable i18n. Default: true'),\n fields: fieldDefZodSchema.optional().describe('Field definitions — shared schema with model_save for full parity'),\n entries: z.array(z.object({\n locale: z.string().optional().describe('Locale. Default: project default'),\n slug: z.string().optional().describe('Document slug'),\n data: z.record(z.any()).describe('Content data'),\n source: z.object({\n file: z.string().describe('Source file path'),\n line: z.number().describe('Source line number'),\n value: z.string().describe('Original string value'),\n }).optional().describe('Source tracking for traceability'),\n sources: z.array(z.object({\n file: z.string().describe('Source file path'),\n line: z.number().describe('Source line number'),\n key: z.string().describe('Dictionary key this source maps to'),\n value: z.string().describe('Original string value'),\n })).optional().describe('Per-key source tracking for dictionary models'),\n })).describe('Content entries to create'),\n })).optional().describe('Extract mode: content extractions'),\n\n // Reuse mode fields\n scope: z.object({\n model: z.string().optional().describe('Target model ID'),\n domain: z.string().optional().describe('Target domain'),\n }).optional().describe('Reuse mode: scope (model or domain required)'),\n patches: z.array(z.object({\n file: z.string().describe('Relative file path to patch'),\n line: z.number().describe('Line number hint (±10 line search)'),\n old_value: z.string().describe('Original string to replace'),\n new_expression: z.string().describe('Replacement expression (agent-determined)'),\n import_statement: z.string().optional().describe('Import to add if needed'),\n })).optional().describe('Reuse mode: patches to apply (max 100)'),\n },\n TOOL_ANNOTATIONS['contentrain_apply']!,\n async (input) => {\n // Normalize extract needs to read source files; reuse needs to write\n // them back. Remote providers expose both capabilities as `false` and\n // get rejected before any work starts.\n const capability = input.mode === 'reuse' ? 'sourceWrite' : 'sourceRead'\n if (!provider.capabilities[capability] || !projectRoot) {\n return capabilityError('contentrain_apply', capability)\n }\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n try {\n switch (input.mode) {\n case 'extract': {\n if (!input.extractions || input.extractions.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Extract mode requires extractions array' }) }],\n isError: true,\n }\n }\n\n const result = await applyExtract(projectRoot, {\n extractions: input.extractions.map(e => ({\n ...e,\n fields: e.fields as Record<string, FieldDef> | undefined,\n })),\n dry_run: input.dry_run,\n })\n\n // Check for branch-blocked response\n if (result.error !== undefined) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'extract',\n ...result,\n }, null, 2) }],\n isError: true,\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'extract',\n ...result,\n }, null, 2) }],\n }\n }\n\n case 'reuse': {\n if (!input.scope) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Reuse mode requires scope (model or domain)' }) }],\n isError: true,\n }\n }\n if (!input.patches || input.patches.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Reuse mode requires patches array' }) }],\n isError: true,\n }\n }\n\n const result = await applyReuse(projectRoot, {\n scope: input.scope,\n patches: input.patches,\n dry_run: input.dry_run,\n })\n\n // Check for branch-blocked response\n if (result.error !== undefined) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'reuse',\n ...result,\n }, null, 2) }],\n isError: true,\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n mode: 'reuse',\n ...result,\n }, null, 2) }],\n }\n }\n\n default:\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown mode: ${input.mode}` }) }],\n isError: true,\n }\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Apply failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { EntryMeta } from '@contentrain/types'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig } from '../core/config.js'\nimport { readModel } from '../core/model-manager.js'\nimport { resolveContentDir, resolveJsonFilePath, deleteContent } from '../core/content-manager.js'\nimport { readMeta, writeMeta } from '../core/meta-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { readJson, writeJson } from '../util/fs.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\n\nexport function registerBulkTools(\n server: McpServer,\n _provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n server.tool(\n 'contentrain_bulk',\n 'Batch operations on content entries. All operations are auto-committed to git.',\n {\n operation: z.enum(['copy_locale', 'update_status', 'delete_entries']),\n model: z.string().describe('Model ID'),\n source_locale: z.string().optional().describe('Source locale for copy_locale operation'),\n target_locale: z.string().optional().describe('Target locale for copy_locale operation'),\n entry_ids: z.array(z.string()).optional().describe('Entry IDs for update_status or delete_entries'),\n status: z.enum(['draft', 'in_review', 'published', 'rejected', 'archived']).optional().describe('New status for update_status'),\n confirm: z.boolean().optional().describe('Must be true for delete_entries'),\n },\n TOOL_ANNOTATIONS['contentrain_bulk']!,\n async (input) => {\n if (!projectRoot) return capabilityError('contentrain_bulk', 'localWorktree')\n const config = await readConfig(projectRoot)\n if (!config) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }],\n isError: true,\n }\n }\n\n const model = await readModel(projectRoot, input.model)\n if (!model) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Model \"${input.model}\" not found` }) }],\n isError: true,\n }\n }\n\n // Branch health gate\n const health = await checkBranchHealth(projectRoot)\n if (health.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: health.message,\n action: 'blocked',\n hint: 'Merge or delete old contentrain/* branches before creating new ones.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n switch (input.operation) {\n case 'copy_locale': {\n if (!input.source_locale || !input.target_locale) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'copy_locale requires source_locale and target_locale' }) }],\n isError: true,\n }\n }\n if (!config.locales.supported.includes(input.source_locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Source locale \"${input.source_locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n if (!config.locales.supported.includes(input.target_locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Target locale \"${input.target_locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // Guard: copy_locale requires i18n model — non-i18n models use data.json for all locales\n if (!model.i18n) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model \"${input.model}\" has i18n disabled. copy_locale only works with i18n-enabled models. Enable i18n first with contentrain_model_save.`,\n }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection' && model.kind !== 'singleton' && model.kind !== 'dictionary') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'copy_locale is only supported for collection, singleton, and dictionary models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model, input.target_locale)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let copiedCount = 0\n\n await tx.write(async (wt) => {\n const cDir = resolveContentDir(wt, model)\n const sourceFile = resolveJsonFilePath(cDir, model, input.source_locale!)\n const targetFile = resolveJsonFilePath(cDir, model, input.target_locale!)\n\n const sourceData = await readJson<Record<string, unknown>>(sourceFile)\n if (!sourceData) {\n throw new Error(`No content found for locale \"${input.source_locale}\" in model \"${input.model}\"`)\n }\n\n await writeJson(targetFile, sourceData)\n\n if (model.kind === 'collection') {\n const entries = sourceData as Record<string, Record<string, unknown>>\n const entryIds = Object.keys(entries)\n const metaOps = entryIds.map(entryId =>\n writeMeta(wt, model, { locale: input.target_locale!, entryId }, {\n status: 'draft',\n source: 'agent',\n updated_by: 'contentrain-mcp',\n }),\n )\n await Promise.all(metaOps)\n copiedCount = entryIds.length\n } else {\n await writeMeta(wt, model, { locale: input.target_locale! }, {\n status: 'draft',\n source: 'agent',\n updated_by: 'contentrain-mcp',\n })\n copiedCount = 1\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: copy ${input.source_locale} → ${input.target_locale} for ${input.model}`, {\n tool: 'contentrain_bulk',\n model: input.model,\n locale: input.target_locale!,\n })\n const gitResult = await tx.complete()\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'copy_locale',\n message: `Copied ${copiedCount} entries from ${input.source_locale} to ${input.target_locale}.`,\n copied: copiedCount,\n git: { branch, action: gitResult.action, commit: gitResult.commit, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `copy_locale failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'update_status': {\n if (!input.entry_ids || input.entry_ids.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status requires entry_ids' }) }],\n isError: true,\n }\n }\n if (!input.status) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status requires status' }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'update_status with entry_ids is only supported for collection models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n let updatedCount = 0\n const notFound: string[] = []\n\n await tx.write(async (wt) => {\n for (const locale of config.locales.supported) {\n const metaData = await readMeta(wt, model, { locale }) as Record<string, EntryMeta> | null\n\n const updateOps: Array<Promise<void>> = []\n for (const entryId of input.entry_ids!) {\n const existing = metaData?.[entryId]\n if (existing) {\n updateOps.push(writeMeta(wt, model, { locale, entryId }, {\n ...existing,\n status: input.status!,\n updated_by: 'contentrain-mcp',\n }))\n updatedCount++\n } else if (locale === config.locales.default) {\n notFound.push(entryId)\n }\n }\n await Promise.all(updateOps)\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: update status → ${input.status} for ${input.model}`, {\n tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n const gitResult = await tx.complete()\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'update_status',\n message: `Updated ${updatedCount} meta entries to status \"${input.status}\".`,\n updated: updatedCount,\n not_found: notFound.length > 0 ? notFound : undefined,\n git: { branch, action: gitResult.action, commit: gitResult.commit, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `update_status failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'delete_entries': {\n if (!input.entry_ids || input.entry_ids.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries requires entry_ids' }) }],\n isError: true,\n }\n }\n if (input.confirm !== true) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries requires confirm:true' }) }],\n isError: true,\n }\n }\n\n if (model.kind !== 'collection') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: 'delete_entries with entry_ids is only supported for collection models' }) }],\n isError: true,\n }\n }\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n const allRemoved: string[] = []\n\n await tx.write(async (wt) => {\n for (const entryId of input.entry_ids!) {\n const removed = await deleteContent(wt, model, { id: entryId })\n allRemoved.push(...removed)\n }\n\n })\n\n await tx.commit(`[contentrain] bulk: delete ${input.entry_ids.length} entries from ${input.model}`, {\n tool: 'contentrain_bulk',\n model: input.model,\n entries: input.entry_ids!,\n })\n const gitResult = await tx.complete()\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n operation: 'delete_entries',\n message: `Deleted ${input.entry_ids.length} entries.`,\n deleted: input.entry_ids.length,\n files_removed: allRemoved,\n git: { branch, action: gitResult.action, commit: gitResult.commit, ...(gitResult.sync ? { sync: gitResult.sync } : {}) },\n context_updated: true,\n }, null, 2) }],\n }\n } catch (error) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `delete_entries failed: ${error instanceof Error ? error.message : String(error)}`,\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n default: {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `Unknown operation: ${input.operation}` }) }],\n isError: true,\n }\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { runDoctor } from '../core/doctor.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\n\nexport function registerDoctorTools(\n server: McpServer,\n _provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n server.tool(\n 'contentrain_doctor',\n 'Project health report (read-only). Returns structured checks: git, node, .contentrain/ structure, model parse, orphan content, branch pressure, SDK freshness. Pass `usage: true` for a deeper analysis of content-key references in source files (unused keys, duplicate dictionary values, locale coverage). Local-filesystem only — unavailable over remote providers.',\n {\n usage: z.boolean().optional().default(false).describe('Run the heavier usage-analysis branch (unused keys, duplicate values, missing locales). Default: false.'),\n },\n TOOL_ANNOTATIONS['contentrain_doctor']!,\n async ({ usage }) => {\n if (!projectRoot) return capabilityError('contentrain_doctor', 'localWorktree')\n\n const report = await runDoctor(projectRoot, { usage })\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(report, null, 2) }],\n }\n },\n )\n}\n","","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { RepoProvider } from './core/contracts/index.js'\nimport { LocalProvider } from './providers/local/index.js'\n\n/**\n * The provider shape tool handlers consume. Now that every provider\n * (Local, GitHub, GitLab) implements the full `RepoProvider`, tools can\n * depend on the shared surface directly — no private alias required.\n * Kept as a re-export so callers that already import `ToolProvider` do\n * not need to migrate.\n */\nexport type ToolProvider = RepoProvider\nimport { registerContextTools } from './tools/context.js'\nimport { registerSetupTools } from './tools/setup.js'\nimport { registerModelTools } from './tools/model.js'\nimport { registerContentTools } from './tools/content.js'\nimport { registerWorkflowTools } from './tools/workflow.js'\nimport { registerNormalizeTools } from './tools/normalize.js'\nimport { registerBulkTools } from './tools/bulk.js'\nimport { registerDoctorTools } from './tools/doctor.js'\nimport packageJson from '../package.json' with { type: 'json' }\n\nexport interface CreateServerOptions {\n /**\n * Content provider — drives reads (and, in later phases, writes) through\n * a reader surface. Required when `projectRoot` is omitted. Accepts the\n * narrow `ToolProvider` shape so either `LocalProvider` or\n * `GitHubProvider` satisfies the contract.\n */\n provider?: ToolProvider\n /**\n * Local project root. When the provider is a `LocalProvider`, its own\n * `projectRoot` is used as the fallback. Tools that require local disk\n * (normalize, setup, git submit/merge) short-circuit with a capability\n * error when no projectRoot is available.\n */\n projectRoot?: string\n}\n\n/**\n * Create an MCP server instance with every Contentrain tool registered.\n *\n * Two signatures:\n *\n * - `createServer('/path/to/project')` — legacy stdio flow. A `LocalProvider`\n * is constructed under the hood; every tool keeps behaving exactly as it\n * did before phase 5.3.\n * - `createServer({ provider, projectRoot? })` — phase 5.3 flow. Any\n * `RepoProvider` (including `GitHubProvider`) drives reads and writes. If\n * the provider is a `LocalProvider` and `projectRoot` is omitted, the\n * provider's own `projectRoot` is used. Otherwise `projectRoot` stays\n * undefined and tools that need local disk report a capability error.\n *\n * Public MCP tool surface (names, parameters, response JSON shape) is\n * unchanged across both signatures.\n */\nexport function createServer(projectRoot: string): McpServer\nexport function createServer(opts: CreateServerOptions): McpServer\nexport function createServer(input: string | CreateServerOptions): McpServer {\n const { provider, projectRoot } = resolveServerContext(input)\n\n const server = new McpServer({\n name: 'contentrain-mcp',\n version: packageJson.version,\n })\n\n registerContextTools(server, provider, projectRoot)\n registerSetupTools(server, provider, projectRoot)\n registerModelTools(server, provider, projectRoot)\n registerContentTools(server, provider, projectRoot)\n registerWorkflowTools(server, provider, projectRoot)\n registerNormalizeTools(server, provider, projectRoot)\n registerBulkTools(server, provider, projectRoot)\n registerDoctorTools(server, provider, projectRoot)\n\n return server\n}\n\nfunction resolveServerContext(input: string | CreateServerOptions): {\n provider: ToolProvider\n projectRoot: string | undefined\n} {\n if (typeof input === 'string') {\n return { provider: new LocalProvider(input), projectRoot: input }\n }\n if (input.provider) {\n let projectRoot = input.projectRoot\n if (!projectRoot && input.provider instanceof LocalProvider) {\n projectRoot = input.provider.projectRoot\n }\n return { provider: input.provider, projectRoot }\n }\n if (input.projectRoot) {\n return { provider: new LocalProvider(input.projectRoot), projectRoot: input.projectRoot }\n }\n throw new Error('createServer: either `provider` or `projectRoot` must be provided')\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { u as RepoProvider } from "./index-D0wQJSgs.mjs";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
|
|
4
|
+
//#region src/server.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The provider shape tool handlers consume. Now that every provider
|
|
7
|
+
* (Local, GitHub, GitLab) implements the full `RepoProvider`, tools can
|
|
8
|
+
* depend on the shared surface directly — no private alias required.
|
|
9
|
+
* Kept as a re-export so callers that already import `ToolProvider` do
|
|
10
|
+
* not need to migrate.
|
|
11
|
+
*/
|
|
12
|
+
type ToolProvider = RepoProvider;
|
|
13
|
+
interface CreateServerOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Content provider — drives reads (and, in later phases, writes) through
|
|
16
|
+
* a reader surface. Required when `projectRoot` is omitted. Accepts the
|
|
17
|
+
* narrow `ToolProvider` shape so either `LocalProvider` or
|
|
18
|
+
* `GitHubProvider` satisfies the contract.
|
|
19
|
+
*/
|
|
20
|
+
provider?: ToolProvider;
|
|
21
|
+
/**
|
|
22
|
+
* Local project root. When the provider is a `LocalProvider`, its own
|
|
23
|
+
* `projectRoot` is used as the fallback. Tools that require local disk
|
|
24
|
+
* (normalize, setup, git submit/merge) short-circuit with a capability
|
|
25
|
+
* error when no projectRoot is available.
|
|
26
|
+
*/
|
|
27
|
+
projectRoot?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create an MCP server instance with every Contentrain tool registered.
|
|
31
|
+
*
|
|
32
|
+
* Two signatures:
|
|
33
|
+
*
|
|
34
|
+
* - `createServer('/path/to/project')` — legacy stdio flow. A `LocalProvider`
|
|
35
|
+
* is constructed under the hood; every tool keeps behaving exactly as it
|
|
36
|
+
* did before phase 5.3.
|
|
37
|
+
* - `createServer({ provider, projectRoot? })` — phase 5.3 flow. Any
|
|
38
|
+
* `RepoProvider` (including `GitHubProvider`) drives reads and writes. If
|
|
39
|
+
* the provider is a `LocalProvider` and `projectRoot` is omitted, the
|
|
40
|
+
* provider's own `projectRoot` is used. Otherwise `projectRoot` stays
|
|
41
|
+
* undefined and tools that need local disk report a capability error.
|
|
42
|
+
*
|
|
43
|
+
* Public MCP tool surface (names, parameters, response JSON shape) is
|
|
44
|
+
* unchanged across both signatures.
|
|
45
|
+
*/
|
|
46
|
+
declare function createServer(projectRoot: string): McpServer;
|
|
47
|
+
declare function createServer(opts: CreateServerOptions): McpServer;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { ToolProvider as n, createServer as r, CreateServerOptions as t };
|
|
50
|
+
//# sourceMappingURL=server-WqGx6ait.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-WqGx6ait.d.mts","names":[],"sources":["../src/server.ts"],"mappings":";;;;;;AAWA;;;;;KAAY,YAAA,GAAe,YAAA;AAAA,UAWV,mBAAA;;;;;;;EAOf,QAAA,GAAW,YAAA;EA2BG;;;;;AAChB;EArBE,WAAA;AAAA;;;;;;;;;;;;;;;;;;iBAoBc,YAAA,CAAa,WAAA,WAAsB,SAAA;AAAA,iBACnC,YAAA,CAAa,IAAA,EAAM,mBAAA,GAAsB,SAAA"}
|
package/dist/server.d.mts
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//#region src/server.d.ts
|
|
4
|
-
declare function createServer(projectRoot: string): McpServer;
|
|
5
|
-
//#endregion
|
|
6
|
-
export { createServer };
|
|
7
|
-
//# sourceMappingURL=server.d.mts.map
|
|
1
|
+
import { n as ToolProvider, r as createServer, t as CreateServerOptions } from "./server-WqGx6ait.mjs";
|
|
2
|
+
export { CreateServerOptions, ToolProvider, createServer };
|
package/dist/server.mjs
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import "./
|
|
2
|
-
import "./
|
|
3
|
-
import "./
|
|
4
|
-
import "./
|
|
5
|
-
import "./
|
|
6
|
-
import "./
|
|
7
|
-
import "./
|
|
8
|
-
import "./
|
|
9
|
-
import
|
|
10
|
-
import "./
|
|
11
|
-
import "./
|
|
12
|
-
import "./
|
|
13
|
-
import "./
|
|
14
|
-
import "./
|
|
15
|
-
import "./
|
|
16
|
-
import "./
|
|
17
|
-
import "./
|
|
1
|
+
import "./contracts-DfL0BfrD.mjs";
|
|
2
|
+
import "./ops-DjEWi5Wa.mjs";
|
|
3
|
+
import "./serialization-B1CEzR4H.mjs";
|
|
4
|
+
import "./fs-DLbVB-Ek.mjs";
|
|
5
|
+
import "./config-BIl5Ybj0.mjs";
|
|
6
|
+
import "./model-manager-BhLsUgaB.mjs";
|
|
7
|
+
import "./meta-manager-C900rEyG.mjs";
|
|
8
|
+
import "./context-J0OU9Nfp.mjs";
|
|
9
|
+
import "./id-CIO9s0x_.mjs";
|
|
10
|
+
import "./transaction-DgkNgWSI.mjs";
|
|
11
|
+
import "./local-DvP4Y4T-.mjs";
|
|
12
|
+
import "./detect-DL9mFmaS.mjs";
|
|
13
|
+
import "./branch-lifecycle-CFVpTpNT.mjs";
|
|
14
|
+
import "./annotations-Clhbw2lh.mjs";
|
|
15
|
+
import { t as createServer } from "./server-B1OJrUQ9.mjs";
|
|
16
|
+
import "./validator-B0nNRFeU.mjs";
|
|
17
|
+
import "./scan-config-jC6XmiqF.mjs";
|
|
18
|
+
import "./graph-builder-DXv2JEp-.mjs";
|
|
19
|
+
import "./scanner-DKMwc03k.mjs";
|
|
20
|
+
import "./tsx-parser-CQc6nr-o.mjs";
|
|
21
|
+
import "./apply-manager-D5ym5Jba.mjs";
|
|
22
|
+
import "./doctor-CgBoT6h1.mjs";
|
|
18
23
|
export { createServer };
|
|
@@ -890,4 +890,4 @@ var require_source_map_generator = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
890
890
|
//#endregion
|
|
891
891
|
export { require_base64_vlq as i, require_array_set as n, require_util as r, require_source_map_generator as t };
|
|
892
892
|
|
|
893
|
-
//# sourceMappingURL=source-map-generator-
|
|
893
|
+
//# sourceMappingURL=source-map-generator-BWDva49I.mjs.map
|