@contentrain/mcp 3.2.0 → 3.3.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server-ChpsLHlF.mjs","names":["packageJson.version"],"sources":["../src/tools/context.ts","../src/tools/guards.ts","../src/tools/commit-plan.ts","../src/tools/setup.ts","../src/tools/model.ts","../src/tools/content.ts","../src/tools/vocabulary.ts","../src/tools/workflow.ts","../src/tools/reconcile.ts","../src/tools/normalize.ts","../src/tools/bulk.ts","../src/tools/doctor.ts","../src/tools/media.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, contentBranchRelation } from '../git/branch-lifecycle.js'\nimport { createGit } from '../git/identity.js'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\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 // Stats source depends on the provider:\n // - Local (projectRoot): context.json's stats can lag (init writes 0/0,\n // and the working-tree copy may be unsynced after a write), which made\n // status report models:0/entries:0 alongside a non-empty models array.\n // Derive both live from the just-enumerated models. #19\n // - Remote: the committed context.json is rebuilt per-commit and is the\n // source of truth; the reader can't always walk content cheaply, so\n // trust context.stats.\n let stats: Record<string, unknown> | null = context?.stats ?? null\n if (projectRoot) {\n const fullModels = await Promise.all(models.map(m => readModel(provider, m.id)))\n const entryCounts = await Promise.all(\n fullModels.filter((m): m is NonNullable<typeof m> => m !== null).map(m => countEntries(provider, m)),\n )\n stats = {\n models: models.length,\n entries: entryCounts.reduce((acc, c) => acc + c.total, 0),\n locales: config?.locales.supported ?? context?.stats?.locales ?? ['en'],\n ...(context?.stats?.lastSync ? { lastSync: context.stats.lastSync } : {}),\n }\n }\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 || stats) ? {\n lastOperation: context?.lastOperation ?? null,\n 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 // contentrain ↔ base relation, both directions. A diverged base is\n // the state that blocks every auto-merge advance — status must not\n // read \"in sync\" while that is true.\n try {\n const git = createGit(projectRoot)\n const branches = await git.branchLocal()\n if (branches.all.includes(CONTENTRAIN_BRANCH)) {\n const baseBranch = config?.repository?.default_branch\n ?? (branches.current !== CONTENTRAIN_BRANCH ? branches.current : 'main')\n const relation = await contentBranchRelation(git, baseBranch)\n result['content_branch'] = {\n base: baseBranch,\n ahead: relation.ahead,\n behind: relation.behind,\n relation: relation.relation,\n }\n if (relation.relation === 'diverged') {\n result['content_branch_warning']\n = `\"${CONTENTRAIN_BRANCH}\" and \"${baseBranch}\" have diverged (${relation.ahead} ahead / ${relation.behind} behind). Writes still land on \"${CONTENTRAIN_BRANCH}\", but \"${baseBranch}\" cannot fast-forward until the branches are reconciled.`\n }\n }\n } catch {\n // Relation 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 title_field: modelDef.title_field,\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_definition: {\n description: 'Shape of .contentrain/models/{model-id}.json. Keys are written in this order.',\n required: {\n id: 'Kebab-case model ID, matching the file name.',\n name: 'Human-readable display name.',\n kind: 'One of: singleton, collection, document, dictionary.',\n domain: 'Organizational group; maps to the content subdirectory.',\n i18n: 'Whether the model stores content per locale.',\n title_field: 'Name of the field shown as an entry title in listings, pickers and relation references. Must name a field declared on this model whose type is one of: string, text, slug, email, url, code, markdown, richtext. Dictionary models have no fields — they use the reserved value \"key\", meaning the entry key is the title.',\n },\n optional: {\n description: 'Model description for documentation and agent context.',\n fields: 'Field definitions. Dictionary models must NOT declare fields.',\n content_path: 'Framework-relative path for content files, replacing .contentrain/content/.',\n locale_strategy: 'How locale is encoded in file names: file (default), suffix, directory, none.',\n },\n note: 'title_field is declared, never inferred. A consumer that guesses the title from field order or length picks the icon over the headline — set it explicitly on create AND keep it correct when renaming or removing fields.',\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. A delivery gate on top of status \"published\" — it never changes status, so a draft with a past publish_at stays a draft. Meta only; never written to the content file.',\n expire_at: 'Optional: ISO 8601 date for scheduled expiry (must be after publish_at). Same gate semantics as 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:\n 'When a model has i18n:false, locale is ignored in CONTENT paths — but not in meta paths. '\n + 'The model holds one content file, so it has exactly one meta record, and that record lives '\n + 'at the default locale. Saving under a non-default locale does not move it.',\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 meta_pattern: '.contentrain/meta/{model-id}/{default-locale}.json (always the default locale, never \"data.json\")',\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<term-slug, Record<locale, translation>>',\n },\n // The nesting reads the same either way round, and a field report\n // built it locale-first — the reverse — because nothing here said\n // which. An example is the only description that cannot be misread.\n example: {\n version: 1,\n terms: {\n 'sign-in': { en: 'Sign in', tr: 'Giriş yap' },\n 'add-to-cart': { en: 'Add to cart', tr: 'Sepete ekle' },\n },\n },\n note: 'OUTER key is the term (kebab-case, locale-independent); INNER key is a locale code. One term, one entry, all its translations inside. Grouping by locale at the top level is the common mistake and produces a vocabulary that matches nothing.',\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 'Write it with contentrain_vocabulary_save — never edit the file by hand',\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 { CONTENTRAIN_BRANCH } from '@contentrain/types'\nimport type { BaseAdvance, RemotePush } from '@contentrain/types'\nimport type { FileChange } from '../core/contracts/index.js'\nimport type { ToolProvider } from '../server.js'\n\n/**\n * Context payload describing the operation, threaded into the local\n * transaction so it can regenerate `.contentrain/context.json` on the\n * contentrain branch after merge. Kept as a loose object so tool-specific\n * payloads can add optional fields (`locale`, `entries`) without churning\n * the helper's signature. Remote providers ignore it — see\n * `commitThroughProvider` below.\n */\nconst COMMIT_AUTHOR = { name: 'Contentrain', email: 'ai@contentrain.io' } as const\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 /** Non-fatal problem from the provider (push failure, partial sync, diverged base). */\n warning?: string\n /** Present when a local provider auto-merged: did the base branch advance? */\n base_advance?: BaseAdvance\n /** Present when a local provider pushed: outcome for the contentrain branch. */\n remote_push?: RemotePush\n}\n\n/**\n * The `git` object every write tool reports. One producer instead of nine\n * hand-rolled literals, so `base_advance`/`remote_push` cannot drift between\n * tools. Both fields appear only when the provider reported them (local\n * auto-merge) — remote/review responses keep their previous shape.\n */\nexport function gitReport(input: {\n branch: string\n action: string\n commit: string\n sync?: unknown\n base_advance?: BaseAdvance\n remote_push?: RemotePush\n}): Record<string, unknown> {\n return {\n branch: input.branch,\n action: input.action,\n commit: input.commit,\n ...(input.sync ? { sync: input.sync } : {}),\n ...(input.base_advance ? { base_advance: input.base_advance } : {}),\n ...(input.remote_push ? { remote_push: input.remote_push } : {}),\n }\n}\n\n/**\n * Next-step guidance for a partial success — empty when the base advanced\n * and the push landed.\n */\nexport function divergenceNextSteps(input: {\n base_advance?: BaseAdvance\n remote_push?: RemotePush\n}): string[] {\n const steps: string[] = []\n if (input.base_advance === 'blocked_diverged') {\n steps.push('DIVERGED: the content is safe on the contentrain branch, but the base branch has commits contentrain does not — run contentrain_reconcile (dry_run first) to merge them and restore the fast-forward advance')\n }\n if (input.remote_push === 'rejected') {\n steps.push('PUSH REJECTED: the remote refused the contentrain push even after a retry — the local and remote contentrain branches have diverged; fetch and run contentrain_reconcile')\n }\n return steps\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 * - **localWorktree providers** — go 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** — only the plan's own changes are\n * committed. Feature branches NEVER carry `.contentrain/context.json`:\n * the file embeds timestamps, so two parallel cr/* branches forked from\n * the same contentrain commit would always conflict on it and the\n * second merge would fail permanently. This mirrors the local\n * transaction flow, which regenerates context.json on the contentrain\n * branch after merge (single-threaded, deterministic). Remote flows\n * always report `pending-review`; Studio (or whatever orchestrator is\n * driving the server) owns the merge AND the post-merge context\n * regeneration on the contentrain branch (`buildContextChange` from\n * `@contentrain/mcp/core/context` is exported for exactly that).\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 // Capability, not class identity: \"backs onto a local worktree\" is exactly\n // what decides which applyPlan shape comes back, and it is a property any\n // implementation can declare — including a test double.\n if (provider.capabilities.localWorktree) {\n const result = await provider.applyPlan({\n branch,\n changes,\n message,\n author: COMMIT_AUTHOR,\n context: contextPayload,\n })\n return {\n commitSha: result.sha,\n // A provider that owns the merge decision reports it; one that hands the\n // branch off to an orchestrator does not, and that means review.\n workflowAction: result.workflowAction ?? 'pending-review',\n sync: result.sync,\n warning: result.warning,\n base_advance: result.base_advance,\n remote_push: result.remote_push,\n }\n }\n\n const allChanges = changes\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: COMMIT_AUTHOR,\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 { createGit } from '../git/identity.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 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 { validateModelDefinition, 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 { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\nimport { gitReport } from './commit-plan.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 = createGit(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, '--no-verify': 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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\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 ...normalizeOperationError(error, 'init'),\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 // Templates are ours, but they are still model definitions: scaffolding an\n // invalid one would hand the user a project that fails its first validate.\n const templateErrors = tmpl.models.flatMap(m =>\n validateModelDefinition(m).errors.map(e => `[${m.id}] ${e}`),\n )\n if (templateErrors.length > 0) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scaffold template \"${templateId}\" is invalid`,\n details: templateErrors,\n }, null, 2) }],\n isError: true,\n }\n }\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\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 ...normalizeOperationError(error, 'scaffold'),\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 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, collectFieldPaths, readModel, validateModelDefinition, fieldDefZodSchema } from '../core/model-manager.js'\nimport { planModelDelete, planModelSave } from '../core/ops/index.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { commitThroughProvider, divergenceNextSteps, gitReport, type CommitThroughProviderResult } 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 title_field: z.string().describe('Name of the field shown as an entry\\'s title in listings, pickers and relation references. Must name a field on this model whose type is string, text, slug, email, url, code, markdown or richtext. Dictionary models have no fields — use \"key\".'),\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 // Read the existing model first: a field name the model already has is\n // grandfathered past the snake_case rule, so a legacy `creativeWork` does\n // not make every later edit to its model impossible (#116).\n const existing = await readModel(provider, input.id)\n const { errors, warnings: schemaWarnings } = validateModel(input, {\n existingFieldPaths: collectFieldPaths(existing?.fields),\n })\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 const readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.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 model: ModelDefinition = {\n id: input.id,\n name: input.name,\n kind: input.kind,\n domain: input.domain,\n i18n: input.i18n,\n title_field: input.title_field,\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 commit: CommitThroughProviderResult\n\n try {\n commit = await commitThroughProvider(provider, {\n branch,\n changes: savePlan.changes,\n message,\n contextPayload,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'model_save'),\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 // Constraints the schema accepts but MCP does not enforce, said out loud\n // rather than left for the author to discover in production.\n ...(schemaWarnings.length > 0 ? { schema_warnings: schemaWarnings } : {}),\n git: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n context_updated: true,\n content_path: contentPath + '/',\n ...(displayPath ? { example_file: displayPath } : {}),\n ...(importSnippet ? { import_snippet: importSnippet } : {}),\n next_steps: [\n ...divergenceNextSteps(commit),\n ...(schemaWarnings.length > 0 ? ['REVIEW: see schema_warnings — some declared constraints are not enforced by MCP'] : []),\n 'Add content with contentrain_content_save',\n ],\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 const deleteReadiness = await provider.checkWriteReadiness?.()\n if (deleteReadiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteReadiness.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 // 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 try {\n const commit = await commitThroughProvider(provider, {\n branch,\n changes: deletePlan.changes,\n message,\n contextPayload,\n })\n\n const nextSteps = divergenceNextSteps(commit)\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: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n files_removed: deletePlan.result,\n context_updated: true,\n ...(nextSteps.length > 0 ? { next_steps: nextSteps } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'model_delete'),\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 { ValidationError } from '@contentrain/types'\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 type { ContentSaveEntryResult } from '../core/ops/types.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { validateProject } from '../core/validator/index.js'\nimport { OverlayReader } from '../core/overlay-reader.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { commitThroughProvider, divergenceNextSteps, gitReport, type CommitThroughProviderResult } 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\". MEDIA FIELDS (image/video/file): for a media-library asset, pass its storage path (\"media/...\") or URL; in cloud mode these are automatically normalized to absolute public delivery URLs on save (in markdown bodies too), so saved content renders in a browser anywhere with no SDK — in local mode the relative path is kept as-is. For external images (e.g. a CDN or Unsplash URL), pass the URL directly; it is saved untouched. 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().nullable().optional().describe('Scheduled publish date, ISO 8601. Stored in meta only — never in the content file — and it does NOT change status: a draft stays a draft, and a published entry is delivered only once this date has passed. Publish with contentrain_bulk update_status. Pass null to clear a previously set date; omit to leave it unchanged.'),\n expire_at: z.string().nullable().optional().describe('Scheduled expiry, ISO 8601, must be after publish_at. Same rules as publish_at: meta only, never changes status, null clears, omit leaves unchanged.'),\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. `null` is a clear, not a date, so it\n // is skipped here and honoured by mergeEntryMeta. The dates are carried\n // on the entry itself, never merged into `data`: that merge is what wrote\n // `publish_at` into document frontmatter and collection JSON (#125).\n if (typeof entry.publish_at === 'string') {\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 (typeof entry.expire_at === 'string') {\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 (typeof entry.publish_at === 'string' && typeof entry.expire_at === 'string') {\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\n // Scheduling was mentioned — set or cleared. Said out loud in the response,\n // because the one thing an agent expects a past publish_at to do is the one\n // thing it does not: status is untouched, and a draft stays a draft.\n const schedulingTouched = input.entries.some(e => e.publish_at !== undefined || e.expire_at !== undefined)\n\n // Write-readiness gate. The provider decides what blocks a write —\n // a local worktree counts unmerged cr/* branches, a hosted one does not.\n const readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.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 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 mediaBaseUrl: provider.mediaBaseUrl,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_save'),\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 // Validate BEFORE committing. This used to run after the commit and only\n // report, so an invalid value landed in git — and was auto-merged — while\n // the response still said `status: \"committed\"` and buried the problem in a\n // next_steps string. The OverlayReader layers the pending changes over the\n // provider, so it needs nothing from the commit.\n const validationResult = await validateProject(\n new OverlayReader(provider, plan.changes),\n { model: input.model },\n )\n\n // Only this save's own entries are fatal. validateProject checks the whole\n // model, so an unrelated pre-existing error elsewhere in it must not block\n // a caller who is writing something valid — they may not even be able to\n // fix it. Everything else stays in the response as context.\n const blocking = validationResult.issues.filter(\n i => i.severity === 'error' && touchesSavedEntries(i, plan.result),\n )\n\n if (blocking.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Validation failed — nothing was written.',\n issues: blocking.map(describeIssue),\n hint: 'Fix the values and call contentrain_content_save again. Nothing was committed, so there is no branch to clean up.',\n }, null, 2) }],\n isError: true,\n }\n }\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 commit: CommitThroughProviderResult\n\n try {\n commit = await commitThroughProvider(provider, {\n branch,\n changes: plan.changes,\n message,\n contextPayload,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_save'),\n }) }],\n isError: true,\n }\n }\n\n const allAdvisories = plan.result.flatMap(r => r.advisories ?? [])\n\n // Warnings do not block — they are heuristics (a colour that may not parse,\n // an extension that may contradict `accept`), and a legitimate value can sit\n // outside an approximate pattern. They ride along in the response instead.\n const warnings = validationResult.issues\n .filter(i => i.severity === 'warning' && touchesSavedEntries(i, plan.result))\n .map(i => `${i.field ?? i.locale}: ${i.message}`)\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: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n ...(allAdvisories.length > 0 ? {\n advisories: allAdvisories,\n advisory_note: 'Save succeeded. Review these warnings and consider consolidating duplicate values.',\n } : {}),\n ...(warnings.length > 0 ? { warnings } : {}),\n ...(schedulingTouched ? {\n scheduling_note: 'publish_at/expire_at were written to meta only — not to the content file — and status is unchanged: a draft is still a draft. They gate delivery of a *published* entry (served once publish_at has passed, until expire_at). To publish, call contentrain_bulk update_status.',\n } : {}),\n validation: { valid: true, errors: [] },\n context_updated: true,\n next_steps: [\n ...divergenceNextSteps(commit),\n ...(allAdvisories.length > 0 ? ['ADVISORY: Duplicate values detected — review advisories above'] : []),\n ...(warnings.length > 0 ? ['REVIEW: see warnings above — the save was not blocked by them'] : []),\n ...(schedulingTouched ? ['PUBLISH: publish_at did not change status — use contentrain_bulk update_status if this entry should go live'] : []),\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 const deleteReadiness = await provider.checkWriteReadiness?.()\n if (deleteReadiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteReadiness.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 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 defaultLocale: config.locales.default,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_delete'),\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 try {\n const commit = await commitThroughProvider(provider, {\n branch,\n changes: deletePlan.changes,\n message,\n contextPayload,\n })\n\n const nextSteps = divergenceNextSteps(commit)\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: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n context_updated: true,\n ...(nextSteps.length > 0 ? { next_steps: nextSteps } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_delete'),\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 ...normalizeOperationError(error, 'content_list'),\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n\n/** Trim a validation issue to the fields that locate it, dropping empty ones. */\nfunction describeIssue(issue: ValidationError): Record<string, string> {\n const out: Record<string, string> = {}\n if (issue.entry) out['entry'] = issue.entry\n if (issue.slug) out['slug'] = issue.slug\n if (issue.locale) out['locale'] = issue.locale\n if (issue.field) out['field'] = issue.field\n out['message'] = issue.message\n return out\n}\n\n/**\n * Does a validation issue belong to one of the entries this save is writing?\n *\n * The write gate blocks on errors, but `validateProject` inspects the whole model.\n * Without this filter, a pre-existing bad entry somewhere else in the model would\n * block an unrelated — and perfectly valid — save, leaving the caller stuck behind\n * a problem they did not create and may not be able to fix.\n *\n * Matching is by identity: entry ID for collections, slug for documents, and\n * locale alone for singletons and dictionaries, which have one record per locale.\n */\nfunction touchesSavedEntries(\n issue: { entry?: string, slug?: string, locale?: string },\n results: ContentSaveEntryResult[],\n): boolean {\n return results.some((r) => {\n if (issue.locale && r.locale && issue.locale !== r.locale) return false\n if (r.id) return issue.entry === r.id\n if (r.slug) return issue.slug === r.slug\n // Singleton / dictionary: the locale is the whole identity, and an issue with\n // no entry or slug is about the record itself.\n return !issue.entry && !issue.slug\n })\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig } from '../core/config.js'\nimport { planVocabularyDelete, planVocabularySave } from '../core/ops/index.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { commitThroughProvider } from './commit-plan.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\n\n/**\n * Vocabulary write tools.\n *\n * The vocabulary is the one piece of `.contentrain/` that had no tool. The\n * rules forbid hand-editing that directory, so the only way to add a canonical\n * term was to break the rule — which a field report duly did, having no\n * alternative.\n *\n * These go through the same plan → commit path as content and models, so a\n * vocabulary change lands on a `cr/*` branch and obeys the project's workflow\n * like everything else.\n */\nexport function registerVocabularyTools(\n server: McpServer,\n provider: ToolProvider,\n): void {\n // ─── contentrain_vocabulary_save ───\n server.tool(\n 'contentrain_vocabulary_save',\n 'Add or update canonical vocabulary terms. Terms nest as { \"term-slug\": { \"en\": \"…\", \"tr\": \"…\" } } — the OUTER key is the term, the INNER key is a locale. Merges with the existing vocabulary: a term you omit is untouched. Changes are auto-committed to git — do NOT manually edit .contentrain/vocabulary.json.',\n {\n terms: z.record(z.string(), z.record(z.string(), z.string())).describe(\n 'Terms keyed by kebab-case term slug, each holding its translations keyed by locale code. Example: { \"sign-in\": { \"en\": \"Sign in\", \"tr\": \"Giriş yap\" } }',\n ),\n },\n TOOL_ANNOTATIONS['contentrain_vocabulary_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 readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.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 let plan: Awaited<ReturnType<typeof planVocabularySave>>\n try {\n plan = await planVocabularySave(provider, { terms: input.terms })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: error instanceof Error ? error.message : String(error),\n hint: 'Terms nest as { \"term-slug\": { \"en\": \"…\" } }. Grouping by locale at the top level is the usual mistake.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const termCount = plan.result.added.length + plan.result.updated.length\n try {\n const commit = await commitThroughProvider(provider, {\n branch: buildBranchName('vocabulary', 'save'),\n changes: plan.changes,\n message: `[contentrain] vocabulary: ${termCount} term(s)`,\n contextPayload: { tool: 'contentrain_vocabulary_save', model: 'vocabulary' },\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n added: plan.result.added,\n updated: plan.result.updated,\n total_terms: plan.result.total,\n commit: commit.commitSha,\n workflow_action: commit.workflowAction,\n ...(commit.base_advance ? { base_advance: commit.base_advance } : {}),\n ...(commit.remote_push ? { remote_push: commit.remote_push } : {}),\n ...(commit.warning ? { warning: commit.warning } : {}),\n ...(plan.advisories.length > 0 ? { advisories: plan.advisories } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'vocabulary_save'),\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_vocabulary_delete ───\n server.tool(\n 'contentrain_vocabulary_delete',\n 'Remove canonical vocabulary terms by slug. Content already using a term is not touched — the vocabulary only advises. Changes are auto-committed to git.',\n {\n terms: z.array(z.string()).min(1).describe('Term slugs to remove'),\n confirm: z.boolean().describe('Must be true — this deletes canonical terms'),\n },\n TOOL_ANNOTATIONS['contentrain_vocabulary_delete']!,\n async (input) => {\n if (!input.confirm) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Deleting vocabulary terms requires confirm: true',\n }) }],\n isError: true,\n }\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 const plan = await planVocabularyDelete(provider, { terms: input.terms })\n\n // Nothing matched — say so without spawning a branch for an empty commit.\n if (plan.changes.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'no-op',\n deleted: [],\n missing: plan.result.missing,\n total_terms: plan.result.total,\n ...(plan.advisories.length > 0 ? { advisories: plan.advisories } : {}),\n }, null, 2) }],\n }\n }\n\n const readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.message,\n action: 'blocked',\n }, null, 2) }],\n isError: true,\n }\n }\n\n try {\n const commit = await commitThroughProvider(provider, {\n branch: buildBranchName('vocabulary', 'delete'),\n changes: plan.changes,\n message: `[contentrain] vocabulary: remove ${plan.result.deleted.length} term(s)`,\n contextPayload: { tool: 'contentrain_vocabulary_delete', model: 'vocabulary' },\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n deleted: plan.result.deleted,\n missing: plan.result.missing,\n total_terms: plan.result.total,\n commit: commit.commitSha,\n workflow_action: commit.workflowAction,\n ...(commit.base_advance ? { base_advance: commit.base_advance } : {}),\n ...(commit.remote_push ? { remote_push: commit.remote_push } : {}),\n ...(commit.warning ? { warning: commit.warning } : {}),\n ...(plan.advisories.length > 0 ? { advisories: plan.advisories } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'vocabulary_delete'),\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { createGit } from '../git/identity.js'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\nimport { z } from 'zod'\nimport { type 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, deleteRemoteBranch, listRemoteCrBranches, pruneMergedRemoteBranches } from '../git/branch-lifecycle.js'\nimport { isMerged } from '../providers/local/branch-ops.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\nimport { isToolAvailable } from './availability.js'\nimport { divergenceNextSteps, gitReport } from './commit-plan.js'\n\n/**\n * Resolve a merge target from either an exact branch name or a\n * model/locale/latest selector. Returns the concrete branch, or an error with\n * candidate branches when the selector is ambiguous or matches nothing.\n */\nasync function resolveMergeBranch(\n git: SimpleGit,\n input: { branch?: string, model?: string, locale?: string, latest?: boolean },\n): Promise<{ branch: string } | { error: string, candidates: string[] }> {\n const summary = await git.branchLocal()\n const crBranches = summary.all.filter(b => b.startsWith('cr/') && b !== CONTENTRAIN_BRANCH)\n\n if (input.branch) {\n if (summary.all.includes(input.branch)) return { branch: input.branch }\n return { error: `Branch \"${input.branch}\" not found locally.`, candidates: crBranches }\n }\n\n if (!input.model) {\n return { error: 'Provide either \"branch\" (exact name) or \"model\" (to resolve the branch).', candidates: crBranches }\n }\n\n const prefix = input.locale\n ? `cr/content/${input.model}/${input.locale}/`\n : `cr/content/${input.model}/`\n let matches = crBranches.filter(b => b.startsWith(prefix))\n if (matches.length === 0) {\n // Fall back to any scope whose path contains the model segment (fix/bulk/...)\n matches = crBranches.filter(b => b.split('/').includes(input.model!))\n }\n if (matches.length === 0) {\n return { error: `No pending cr/* branch found for model \"${input.model}\".`, candidates: crBranches }\n }\n if (matches.length === 1) return { branch: matches[0]! }\n if (!input.latest) {\n return { error: `Multiple branches match model \"${input.model}\". Pass latest:true or an exact branch.`, candidates: matches }\n }\n const withTimes = await Promise.all(matches.map(async (b) => {\n const t = await git.raw(['log', '-1', '--format=%ct', b]).then(s => Number(s.trim())).catch(() => 0)\n return { b, t }\n }))\n const newest = withTimes.toSorted((left, right) => right.t - left.t)[0]!\n return { branch: newest.b }\n}\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, stray non-i18n meta layout, scheduling keys an older content_save leaked into document frontmatter) and backfills a missing model title_field — 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, stray non-i18n meta layout, publish_at/expire_at copies an older content_save left in document frontmatter when meta holds the same key) and backfill a missing model title_field. Each backfilled title_field is reported as a notice naming the rule that chose it. 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. Force auto-merge: structural fixes\n // (canonical sort, orphan meta, missing locale files) are cosmetic\n // infra repairs and should land directly on contentrain rather than\n // spawn a pending cr/fix/validate review branch.\n const branch = buildBranchName('fix', 'validate')\n const tx = await createTransaction(projectRoot, branch, { workflowOverride: 'auto-merge' })\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[] = [...divergenceNextSteps(gitResult)]\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\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 // Only suggest submit where it can actually run. validate itself works\n // over any provider, but contentrain_submit needs a local worktree and\n // a pushable remote — on a remote provider it is not even registered,\n // so recommending it sends the agent at a tool that cannot exist.\n if (result.valid && isToolAvailable('contentrain_submit', provider, projectRoot)) {\n nextSteps.push('Run contentrain_submit to push changes')\n }\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 = createGit(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 const summary = await git.branchLocal().catch(() => ({ all: [] as string[] }))\n const pending = summary.all.filter(b => b.startsWith('cr/') && b !== CONTENTRAIN_BRANCH)\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `No remote \"${remoteName}\" configured — contentrain_submit (push) is unavailable.`,\n stage: 'submit',\n agent_hint: 'This project has no git remote. In a local/solo workflow, land pending review branches with contentrain_merge instead of submit, or set workflow:\"auto-merge\" in config.json so saves merge locally without a remote.',\n pending_branches: pending,\n next_steps: [\n pending.length > 0\n ? `Merge a pending branch locally: contentrain_merge { branch: \"${pending[0]}\", confirm: true }`\n : 'Make changes with contentrain_content_save — auto-merge lands them locally without a remote',\n `Or add a remote to enable submit: git remote add ${remoteName} <url>`,\n ],\n }, null, 2) }],\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 // Lazy remote sweep: prune already-merged cr/* leftovers on the\n // remote (capped, config-gated, never throws) so review workflows\n // self-heal instead of accumulating phantom pending reviews.\n const remotePrune = await pruneMergedRemoteBranches(projectRoot, { config, max: 20 })\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 (remotePrune.errors.length > 0) nextSteps.push('Some merged remote branches could not be pruned — run `contentrain prune`')\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 remote_cleanup: remotePrune.deleted.length > 0 ? { deleted: remotePrune.deleted.length } : 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, selectively syncs .contentrain/ files to the working tree, and prunes the merged branch. Target by exact \"branch\" name, or resolve by \"model\" (+ optional \"locale\"/\"latest\").',\n {\n branch: z.string().optional().describe('Exact branch name to merge (e.g. cr/content/blog-post/...). Omit to resolve by model.'),\n model: z.string().optional().describe('Resolve the branch by model id (e.g. \"blog-post\").'),\n locale: z.string().optional().describe('Narrow model resolution to a locale.'),\n latest: z.boolean().optional().describe('When multiple branches match the model, merge the most recently committed one.'),\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 // Resolve the target branch from exact name or model/locale/latest selector\n const git = createGit(projectRoot)\n const resolved = await resolveMergeBranch(git, input)\n if ('error' in resolved) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: resolved.error,\n candidates: resolved.candidates,\n next_steps: [\n 'Pass an exact { branch } or { model, latest: true }',\n 'List pending branches with contentrain_branch_list',\n ],\n }, null, 2) }],\n isError: true,\n }\n }\n const targetBranch = resolved.branch\n\n const result = await mergeBranch(projectRoot, targetBranch)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'merged',\n branch: targetBranch,\n action: result.action,\n commit: result.commit,\n sync: result.sync,\n base_advance: result.base_advance,\n remote_push: result.remote_push,\n ...(result.warning ? { warning: result.warning } : {}),\n ...(result.remote ? { remote: result.remote } : {}),\n next_steps: [\n ...divergenceNextSteps(result),\n 'Run `contentrain generate` (or `npx contentrain-query generate`) to update the 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 result.remote?.warning\n ? `Remote copy not deleted (${result.remote.warning}) — delete it manually: git push <remote> --delete ${targetBranch}`\n : '',\n ].filter(Boolean),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'merge'), null, 2) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_branch_list ───\n server.tool(\n 'contentrain_branch_list',\n 'List pending contentrain (cr/*) branches with their merge status against the contentrain branch. Use this to discover branch names for contentrain_merge / contentrain_branch_delete, and to monitor branch-health limits (warning at 50, blocked at 80 unmerged).',\n {\n unmerged_only: z.boolean().optional().describe('Only list branches not yet merged into contentrain. Default: false'),\n remote: z.boolean().optional().describe('Also check the git remote: annotate entries with on_remote and report remote-only cr/* leftovers. Requires network. Default: false'),\n },\n TOOL_ANNOTATIONS['contentrain_branch_list']!,\n async (input) => {\n if (!provider.capabilities.localWorktree || !projectRoot) {\n return capabilityError('contentrain_branch_list', 'localWorktree')\n }\n try {\n const git = createGit(projectRoot)\n const summary = await git.branchLocal()\n const crBranches = summary.all.filter(b => b.startsWith('cr/') && b !== CONTENTRAIN_BRANCH)\n\n const remoteList = input.remote ? await listRemoteCrBranches(projectRoot) : undefined\n const remoteNames = remoteList && !remoteList.error\n ? new Set(remoteList.branches.map(b => b.name))\n : undefined\n\n const branches = await Promise.all(crBranches.map(async (name) => {\n const [merged, ts] = await Promise.all([\n isMerged(projectRoot, name, CONTENTRAIN_BRANCH),\n git.raw(['log', '-1', '--format=%cI', name]).then(s => s.trim()).catch(() => ''),\n ])\n return {\n name,\n sha: summary.branches[name]?.commit ?? '',\n merged,\n lastCommit: ts,\n ...(remoteNames ? { on_remote: remoteNames.has(name) } : {}),\n }\n }))\n const ordered = branches.toSorted((left, right) => right.lastCommit.localeCompare(left.lastCommit))\n const filtered = input.unmerged_only ? ordered.filter(b => !b.merged) : ordered\n const health = await checkBranchHealth(projectRoot)\n\n const localNames = new Set(crBranches)\n const remoteExtras = input.remote\n ? (remoteList === null\n ? { remote_error: 'No git remote configured.' }\n : remoteList!.error\n ? { remote_error: remoteList!.error }\n : { remote_only: remoteList!.branches.filter(b => !localNames.has(b.name)).map(b => b.name) })\n : {}\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n total: ordered.length,\n unmerged: ordered.filter(b => !b.merged).length,\n branches: filtered,\n ...remoteExtras,\n health: { warning: health.warning, blocked: health.blocked, message: health.message },\n next_steps: [\n 'Merge a branch: contentrain_merge { branch: \"...\", confirm: true }',\n 'Delete a stale branch: contentrain_branch_delete { branch: \"...\", confirm: true }',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'branch_list'), null, 2) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_branch_delete ───\n server.tool(\n 'contentrain_branch_delete',\n 'Delete a pending contentrain (cr/*) branch that will not be merged — e.g. a branch left behind by a failed operation, or a superseded draft. Only cr/* branches can be deleted; the contentrain branch is protected. This is destructive: the branch and its unmerged commits are removed.',\n {\n branch: z.string().describe('The cr/* branch to delete'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n TOOL_ANNOTATIONS['contentrain_branch_delete']!,\n async (input) => {\n if (!provider.capabilities.localWorktree || !projectRoot) {\n return capabilityError('contentrain_branch_delete', 'localWorktree')\n }\n if (!input.branch.startsWith('cr/') || input.branch === CONTENTRAIN_BRANCH) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Refusing to delete \"${input.branch}\". Only cr/* feature branches can be deleted (the contentrain branch is protected).`,\n }) }],\n isError: true,\n }\n }\n try {\n const git = createGit(projectRoot)\n const summary = await git.branchLocal()\n if (!summary.all.includes(input.branch)) {\n // The local copy may already be pruned while the pushed copy\n // leaked on the remote — attempt a remote-only delete before\n // giving up.\n const remoteOnly = await deleteRemoteBranch(projectRoot, input.branch)\n if (remoteOnly.deleted) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'deleted',\n branch: input.branch,\n scope: 'remote-only',\n message: 'Branch was not present locally; its remote copy was deleted.',\n }, null, 2) }],\n }\n }\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Branch \"${input.branch}\" not found locally${remoteOnly.skipped === 'not-found' ? ' or on the remote' : ''}.`,\n ...(remoteOnly.warning ? { remote_warning: remoteOnly.warning } : {}),\n next_steps: ['List branches with contentrain_branch_list'],\n }) }],\n isError: true,\n }\n }\n const merged = await isMerged(projectRoot, input.branch, CONTENTRAIN_BRANCH)\n // Force-delete (-D): the branch may carry unmerged commits the caller\n // has explicitly decided to discard.\n await git.raw(['branch', '-D', input.branch])\n // Delete the pushed copy too — a discarded draft must not linger on\n // the remote as a phantom pending review. Best-effort, config-gated.\n const remote = await deleteRemoteBranch(projectRoot, input.branch)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'deleted',\n branch: input.branch,\n was_merged: merged,\n remote_deleted: remote.deleted,\n ...(remote.skipped ? { remote_skipped: remote.skipped } : {}),\n ...(remote.warning ? { remote_warning: remote.warning } : {}),\n warning: merged ? undefined : 'Branch was not merged — its commits were discarded.',\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'branch_delete'), null, 2) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport type { ConflictResolution } from '@contentrain/types'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig } from '../core/config.js'\nimport { reconcileBranches } from '../git/reconcile.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\n\nconst resolutionSchema = z.union([\n z.object({\n id: z.string().describe('Conflict id from a previous dry-run'),\n choose: z.enum(['ours', 'theirs']).describe('Take the contentrain side (ours) or the base-branch side (theirs)'),\n }),\n z.object({\n id: z.string().describe('Conflict id from a previous dry-run'),\n value: z.unknown().describe('Hand-authored replacement value'),\n }),\n])\n\nexport function registerReconcileTools(\n server: McpServer,\n provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n server.tool(\n 'contentrain_reconcile',\n 'Reconcile a diverged contentrain ↔ base-branch pair with a content-aware three-way merge. '\n + 'DRY RUN (default, dry_run:true): reports what would merge and which conflicts need a decision — touches nothing. '\n + 'EXECUTE (dry_run:false): performs the merge as a two-parent commit on contentrain and fast-forwards the base branch. '\n + 'Everything one side changed merges mechanically (entry-, key-, term+locale-level); only the same item changed differently '\n + 'on both sides becomes a conflict. Answer conflicts by passing resolutions (from the dry-run ids) and running again — '\n + 'a resolution whose values changed since the dry-run is dropped and the conflict re-reported. '\n + 'Recommended workflow: always run dry_run first, review the summary and conflicts, then execute.',\n {\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 resolutions: z.array(resolutionSchema).optional().describe('Decisions for conflicts reported by a previous dry-run, matched by id'),\n },\n TOOL_ANNOTATIONS['contentrain_reconcile']!,\n async (input) => {\n if (!provider.capabilities.localWorktree || !projectRoot) {\n return capabilityError('contentrain_reconcile', 'localWorktree')\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 const result = await reconcileBranches(projectRoot, {\n dryRun: input.dry_run,\n resolutions: input.resolutions as ConflictResolution[] | undefined,\n source: 'mcp-local',\n })\n\n if (result.action === 'in_sync') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'in_sync',\n message: `\"${result.base}\" is already contained in the contentrain branch — nothing to reconcile.`,\n base: result.base,\n }, null, 2) }],\n }\n }\n\n if (result.action === 'preview') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'preview',\n dry_run: true,\n base: result.base,\n merge_base: result.merge_base,\n summary: result.plan.result,\n changes: result.plan.changes.map(c => ({ path: c.path, action: c.content === null ? 'delete' : 'write' })),\n conflicts: result.plan.conflicts,\n ...(result.plan.advisories.length > 0 ? { advisories: result.plan.advisories } : {}),\n next_steps: result.plan.conflicts.length > 0\n ? [\n `${result.plan.conflicts.length} conflict(s) need a decision — collect resolutions ({ id, choose: 'ours'|'theirs' } or { id, value }) and call contentrain_reconcile again`,\n 'Conflicts are content decisions: ask the editor/user, do not guess',\n ]\n : ['Plan is clean — run contentrain_reconcile with dry_run:false to execute'],\n }, null, 2) }],\n }\n }\n\n if (result.action === 'conflicts') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'conflicts',\n message: `${result.plan.conflicts.length} conflict(s) remain — nothing was written.`,\n base: result.base,\n conflicts: result.plan.conflicts,\n ...(result.plan.advisories.length > 0 ? { advisories: result.plan.advisories } : {}),\n agent_hint: 'Each conflict is a content decision. Ask the editor/user which side (or value) should win, then pass resolutions and run again.',\n }, null, 2) }],\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'reconciled',\n message: `Merged \"${result.base}\" into the contentrain branch and fast-forwarded \"${result.base}\".`,\n base: result.base,\n commit: result.commit,\n base_advance: result.base_advance,\n ...(result.remote_push ? { remote_push: result.remote_push } : {}),\n ...(result.sync ? { sync: result.sync } : {}),\n summary: result.plan.result,\n ...(result.plan.advisories.length > 0 ? { advisories: result.plan.advisories } : {}),\n next_steps: [\n 'Run contentrain_validate to verify content integrity',\n 'Run `contentrain generate` to refresh the SDK client',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'reconcile'), null, 2) }],\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 title_field: z.string().optional().describe('Field shown as an entry title. Inferred from the extracted fields when omitted; the dry run reports what was chosen. Dictionary models: \"key\".'),\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 { applyStatusChange, mergeEntryMeta, readMeta, writeMeta, writeMetaEntries } from '../core/meta-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { readJson, writeJson } from '../util/fs.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\nimport { divergenceNextSteps, gitReport } from './commit-plan.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 (collection models) or delete_entries'),\n slugs: z.array(z.string()).optional().describe('Document slugs for update_status (document models) — a document is addressed by slug, the same identity contentrain_content_save uses, not by entry ID'),\n locale: z.string().optional().describe('Scope update_status to a single locale (i18n models only; defaults to every supported locale)'),\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 updates: Record<string, EntryMeta> = {}\n for (const entryId of Object.keys(entries)) {\n // A copied entry is a new entry in the target locale.\n updates[entryId] = mergeEntryMeta(undefined)\n }\n // One read-modify-write for the whole locale file — see writeMetaEntries.\n const written = await writeMetaEntries(wt, model, { locale: input.target_locale!, defaultLocale: config.locales.default }, updates)\n copiedCount = written.length\n } else {\n await writeMeta(wt, model, { locale: input.target_locale!, defaultLocale: config.locales.default }, mergeEntryMeta(undefined))\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\n ...(divergenceNextSteps(gitResult).length > 0 ? { next_steps: divergenceNextSteps(gitResult) } : {}),\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 ...normalizeOperationError(error, 'bulk_copy_locale'),\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'update_status': {\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 // Three meta layouts, three ways to address a record. Collections key\n // meta by entry ID in one file per locale; documents keep one file per\n // slug (meta/{model}/{slug}/{locale}.json) and are addressed by slug,\n // as content_save already does (#124); singletons and dictionaries have\n // exactly one record per locale and take no list at all. Each guard is\n // checked before any git work so the caller gets a usable error, not a\n // dead end.\n const keyedByEntry = model.kind === 'collection'\n const keyedBySlug = model.kind === 'document'\n const hasEntryIds = Boolean(input.entry_ids && input.entry_ids.length > 0)\n const hasSlugs = Boolean(input.slugs && input.slugs.length > 0)\n\n if (keyedBySlug && hasEntryIds) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model \"${input.model}\" is a document model — documents are keyed by slug, not entry ID. Pass slugs instead of entry_ids.`,\n }) }],\n isError: true,\n }\n }\n if (keyedBySlug && !hasSlugs) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `update_status requires slugs for document model \"${input.model}\".` }) }],\n isError: true,\n }\n }\n if (!keyedBySlug && hasSlugs) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `slugs only apply to document models — \"${input.model}\" is a ${model.kind} model. ${keyedByEntry ? 'Pass entry_ids.' : 'Omit both entry_ids and slugs.'}`,\n }) }],\n isError: true,\n }\n }\n if (keyedByEntry && !hasEntryIds) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `update_status requires entry_ids for collection model \"${input.model}\".` }) }],\n isError: true,\n }\n }\n if (!keyedByEntry && !keyedBySlug && hasEntryIds) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model \"${input.model}\" is a ${model.kind} model — it has one meta record per locale, so entry_ids do not apply. Omit entry_ids.`,\n }) }],\n isError: true,\n }\n }\n\n if (input.locale && !config.locales.supported.includes(input.locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Locale \"${input.locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // A non-i18n model stores one meta record at the default locale, so\n // fanning out over supported locales would rewrite the same file.\n const targetLocales = model.i18n\n ? (input.locale ? [input.locale] : config.locales.supported)\n : [config.locales.default]\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n // Counted from what is actually persisted, never from the input.\n const updatedPerLocale: Record<string, string[]> = {}\n const foundIds = new Set<string>()\n\n await tx.write(async (wt) => {\n for (const locale of targetLocales) {\n if (keyedByEntry) {\n const metaData = await readMeta(wt, model, { locale, defaultLocale: config.locales.default }) as Record<string, EntryMeta> | null\n\n const updates: Record<string, EntryMeta> = {}\n for (const entryId of input.entry_ids!) {\n const existing = metaData?.[entryId]\n if (!existing) continue\n updates[entryId] = applyStatusChange(existing, input.status!)\n foundIds.add(entryId)\n }\n\n // One read-modify-write for the whole locale file — see writeMetaEntries.\n const written = await writeMetaEntries(wt, model, { locale, defaultLocale: config.locales.default }, updates)\n if (written.length > 0) updatedPerLocale[locale] = written\n } else if (keyedBySlug) {\n // One meta file per slug, so each write lands on its own path —\n // the shared-file race writeMetaEntries exists for cannot occur.\n const written: string[] = []\n for (const slug of input.slugs!) {\n const existing = await readMeta(wt, model, { locale, slug, defaultLocale: config.locales.default }) as EntryMeta | null\n if (!existing) continue\n await writeMeta(wt, model, { locale, slug, defaultLocale: config.locales.default }, applyStatusChange(existing, input.status!))\n written.push(slug)\n foundIds.add(slug)\n }\n if (written.length > 0) updatedPerLocale[locale] = written\n } else {\n const existing = await readMeta(wt, model, { locale, defaultLocale: config.locales.default }) as EntryMeta | null\n if (!existing) continue\n await writeMeta(wt, model, { locale, defaultLocale: config.locales.default }, applyStatusChange(existing, input.status!))\n updatedPerLocale[locale] = [model.id]\n }\n }\n })\n\n const updatedCount = Object.values(updatedPerLocale).reduce((n, ids) => n + ids.length, 0)\n const requested = keyedByEntry ? input.entry_ids! : keyedBySlug ? input.slugs! : []\n const notFound = requested.filter(key => !foundIds.has(key))\n\n if (updatedCount === 0) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: keyedByEntry\n ? `No meta records found for the given entry_ids in model \"${input.model}\" across locales [${targetLocales.join(', ')}]. Nothing was changed.`\n : keyedBySlug\n ? `No meta records found for the given slugs in model \"${input.model}\" across locales [${targetLocales.join(', ')}]. Nothing was changed. Check the slugs with contentrain_content_list.`\n : `No meta record found for model \"${input.model}\" across locales [${targetLocales.join(', ')}]. Nothing was changed.`,\n not_found: notFound.length > 0 ? notFound : undefined,\n }, null, 2) }],\n isError: true,\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 ...(requested.length > 0 ? { entries: requested } : {}),\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 record(s) to status \"${input.status}\" across locales [${Object.keys(updatedPerLocale).join(', ')}].`,\n updated: updatedCount,\n updated_by_locale: updatedPerLocale,\n not_found: notFound.length > 0 ? notFound : undefined,\n git: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\n ...(divergenceNextSteps(gitResult).length > 0 ? { next_steps: divergenceNextSteps(gitResult) } : {}),\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 ...normalizeOperationError(error, 'bulk_update_status'),\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, defaultLocale: config.locales.default })\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\n ...(divergenceNextSteps(gitResult).length > 0 ? { next_steps: divergenceNextSteps(gitResult) } : {}),\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 ...normalizeOperationError(error, 'bulk_delete_entries'),\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 { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ToolProvider } from '../server.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { normalizeOperationError } from '../git/errors.js'\n\n/**\n * Media tools — a deterministic passthrough to the provider's optional\n * media facet (`RepoProvider.media`). Registration is capability-aware:\n * `createServer` skips all five tools when the provider has no media\n * stack, so the guard below is defense in depth only.\n *\n * The facet closes the discovery loop for external agents: list assets →\n * pick a `media/...` path → reference it via `contentrain_content_save`\n * (which normalizes it to an absolute delivery URL when `mediaBaseUrl`\n * is set). Ingest is URL-based — MCP has no binary channel — and the\n * provider implementation owns SSRF/MIME/size policy for the fetch.\n */\n\nfunction mediaUnavailable(tool: string) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `${tool} requires a provider with a media stack.`,\n capability_required: 'media',\n hint: 'Media tools are available when MCP is driven by a media-capable provider (e.g. Studio MCP Cloud). Local and plain git providers do not expose one.',\n }) }],\n isError: true as const,\n }\n}\n\nfunction mediaError(error: unknown, stage: string) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, stage),\n }) }],\n isError: true as const,\n }\n}\n\nfunction ok(payload: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(payload) }] }\n}\n\nexport function registerMediaTools(\n server: McpServer,\n provider: ToolProvider,\n _projectRoot: string | undefined,\n): void {\n // ─── contentrain_media_list ───\n server.tool(\n 'contentrain_media_list',\n 'List media assets from the provider media stack. Returns storage paths (media/...) usable in media/image/file fields via contentrain_content_save, plus delivery URLs when available. Supports search, tag filter, and cursor pagination.',\n {\n search: z.string().optional().describe('Substring match on filename/path/alt'),\n tag: z.string().optional().describe('Only assets carrying this tag'),\n limit: z.number().int().min(1).max(100).optional().describe('Page size. Default: provider-defined'),\n cursor: z.string().optional().describe('Opaque cursor from a previous response'),\n },\n TOOL_ANNOTATIONS['contentrain_media_list']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_list')\n try {\n const result = await provider.media.list(input)\n return ok({\n assets: result.assets,\n next_cursor: result.nextCursor,\n total: result.total,\n })\n } catch (error) {\n return mediaError(error, 'media_list')\n }\n },\n )\n\n // ─── contentrain_media_get ───\n server.tool(\n 'contentrain_media_get',\n 'Get a single media asset by id, including its storage path, delivery URL, and metadata.',\n {\n id: z.string().describe('Asset id (from contentrain_media_list)'),\n },\n TOOL_ANNOTATIONS['contentrain_media_get']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_get')\n try {\n const asset = await provider.media.get(input.id)\n if (!asset) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Media asset not found: ${input.id}`,\n hint: 'Use contentrain_media_list to discover asset ids.',\n }) }],\n isError: true as const,\n }\n }\n return ok({ asset })\n } catch (error) {\n return mediaError(error, 'media_get')\n }\n },\n )\n\n // ─── contentrain_media_ingest ───\n server.tool(\n 'contentrain_media_ingest',\n 'Ingest a media asset from a source URL. The provider fetches the URL server-side under its own SSRF/MIME/size policy and stores the asset in the media stack. Returns the stored asset with its media/... path.',\n {\n url: z.string().url().describe('Source URL to fetch server-side'),\n filename: z.string().optional().describe('Target filename override'),\n alt: z.string().optional().describe('Alt text for the asset'),\n tags: z.array(z.string()).max(20).optional().describe('Tags to attach'),\n },\n TOOL_ANNOTATIONS['contentrain_media_ingest']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_ingest')\n try {\n const asset = await provider.media.ingest(input)\n return ok({ status: 'ingested', asset })\n } catch (error) {\n return mediaError(error, 'media_ingest')\n }\n },\n )\n\n // ─── contentrain_media_update ───\n server.tool(\n 'contentrain_media_update',\n 'Update metadata (alt text, tags, filename) of an existing media asset. Does not touch the binary.',\n {\n id: z.string().describe('Asset id to update'),\n alt: z.string().optional().describe('New alt text'),\n tags: z.array(z.string()).max(20).optional().describe('Replacement tag list'),\n filename: z.string().optional().describe('New filename'),\n },\n TOOL_ANNOTATIONS['contentrain_media_update']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_update')\n try {\n const { id, ...patch } = input\n const asset = await provider.media.update(id, patch)\n return ok({ status: 'updated', asset })\n } catch (error) {\n return mediaError(error, 'media_update')\n }\n },\n )\n\n // ─── contentrain_media_delete ───\n server.tool(\n 'contentrain_media_delete',\n 'Delete a media asset from the media stack. Content entries referencing its path are NOT rewritten — check references before deleting.',\n {\n id: z.string().describe('Asset id to delete'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n TOOL_ANNOTATIONS['contentrain_media_delete']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_delete')\n try {\n await provider.media.delete(input.id)\n return ok({ status: 'deleted', id: input.id })\n } catch (error) {\n return mediaError(error, 'media_delete')\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'\nimport { isToolAvailable, TOOL_REQUIREMENTS } from './tools/availability.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 { registerVocabularyTools } from './tools/vocabulary.js'\nimport { registerWorkflowTools } from './tools/workflow.js'\nimport { registerReconcileTools } from './tools/reconcile.js'\nimport { registerNormalizeTools } from './tools/normalize.js'\nimport { registerBulkTools } from './tools/bulk.js'\nimport { registerDoctorTools } from './tools/doctor.js'\nimport { registerMediaTools } from './tools/media.js'\nimport packageJson from '../package.json' with { type: 'json' }\n\n/**\n * Default MCP `instructions` surfaced to clients at initialize time.\n * Deliberately kept under 512 characters — directory listings and client\n * UIs truncate longer strings. Override via `CreateServerOptions.instructions`.\n */\nexport const DEFAULT_INSTRUCTIONS\n = 'Contentrain is git-native content governance: models define structure; '\n + 'content is canonical JSON/Markdown on a dedicated branch. Start with '\n + 'contentrain_status; call contentrain_describe_format before creating '\n + 'models or content, and contentrain_describe before editing a model\\'s '\n + 'entries. Every write lands on an isolated cr/* branch and content_save '\n + 'validates before committing. Destructive tools require confirm:true; '\n + 'preview contentrain_apply with dry_run:true first. Content decisions '\n + 'stay with the agent.'\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) are not registered when no\n * projectRoot is available.\n */\n projectRoot?: string\n /**\n * MCP `instructions` string sent to clients in the `initialize` response.\n * Defaults to `DEFAULT_INSTRUCTIONS`; pass an empty string to omit\n * instructions entirely.\n */\n instructions?: string\n}\n\n/**\n * `McpServer` variant that silently skips registration for tools named in\n * `skipTools`. Register functions call `server.tool(...)` unconditionally;\n * this subclass is what keeps capability-gated tools out of `tools/list`\n * when the provider (or missing projectRoot) could never satisfy them.\n */\nclass CapabilityFilteredMcpServer extends McpServer {\n private readonly _skipTools: ReadonlySet<string>\n\n constructor(\n serverInfo: ConstructorParameters<typeof McpServer>[0],\n options: ConstructorParameters<typeof McpServer>[1],\n skipTools: ReadonlySet<string>,\n ) {\n super(serverInfo, options)\n this._skipTools = skipTools\n }\n\n // McpServer.tool has six overloads; a rest-args override is the only\n // signature that satisfies all of them. Skipped tools return undefined —\n // register functions discard the handle, so nothing downstream observes it.\n override tool(...args: unknown[]): ReturnType<McpServer['tool']> {\n if (this._skipTools.has(args[0] as string)) {\n return undefined as unknown as ReturnType<McpServer['tool']>\n }\n return (McpServer.prototype.tool as (...toolArgs: unknown[]) => ReturnType<McpServer['tool']>).apply(this, args)\n }\n}\n\n/**\n * Create an MCP server instance with every *available* Contentrain tool\n * 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.\n *\n * Tool listing is capability-aware: tools whose requirements\n * (`TOOL_REQUIREMENTS`) cannot be met by the resolved provider +\n * projectRoot pair are not registered, so `tools/list` only advertises\n * tools that can actually succeed. With a `LocalProvider` (stdio and CLI\n * flows) all 19 tools remain registered — behavior there is unchanged.\n */\nexport function createServer(projectRoot: string): McpServer\nexport function createServer(opts: CreateServerOptions): McpServer\nexport function createServer(input: string | CreateServerOptions): McpServer {\n const { provider, projectRoot, instructions } = resolveServerContext(input)\n\n const skipTools = new Set(\n Object.keys(TOOL_REQUIREMENTS).filter(name => !isToolAvailable(name, provider, projectRoot)),\n )\n\n const server = new CapabilityFilteredMcpServer(\n {\n name: 'contentrain-mcp',\n version: packageJson.version,\n },\n { instructions: instructions || undefined },\n skipTools,\n )\n\n registerContextTools(server, provider, projectRoot)\n registerSetupTools(server, provider, projectRoot)\n registerModelTools(server, provider, projectRoot)\n registerContentTools(server, provider, projectRoot)\n registerVocabularyTools(server, provider)\n registerWorkflowTools(server, provider, projectRoot)\n registerReconcileTools(server, provider, projectRoot)\n registerNormalizeTools(server, provider, projectRoot)\n registerBulkTools(server, provider, projectRoot)\n registerDoctorTools(server, provider, projectRoot)\n registerMediaTools(server, provider, projectRoot)\n\n return server\n}\n\nfunction resolveServerContext(input: string | CreateServerOptions): {\n provider: ToolProvider\n projectRoot: string | undefined\n instructions: string\n} {\n if (typeof input === 'string') {\n return { provider: new LocalProvider(input), projectRoot: input, instructions: DEFAULT_INSTRUCTIONS }\n }\n const instructions = input.instructions ?? DEFAULT_INSTRUCTIONS\n if (input.provider) {\n let projectRoot = input.projectRoot\n // Structural, not nominal: any worktree-backed provider exposes a\n // projectRoot, and a test double should be able to as well.\n if (!projectRoot) {\n const maybeRoot = (input.provider as { projectRoot?: unknown }).projectRoot\n if (typeof maybeRoot === 'string') projectRoot = maybeRoot\n }\n return { provider: input.provider, projectRoot, instructions }\n }\n if (input.projectRoot) {\n return { provider: new LocalProvider(input.projectRoot), projectRoot: input.projectRoot, instructions }\n }\n throw new Error('createServer: either `provider` or `projectRoot` must be provided')\n}\n"],"mappings":"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ACV,qBAAqB;KACnB,aAAa;KACb,QAAQ;MACN,4BAA4B;MAC5B,6BAA6B;MAC7B,gCAAgC;MAChC,uCAAuC;MACvC,6CAA6C;MAC7C,iCAAiC;MAClC;KACF;IACD,kBAAkB;KAChB,aAAa;KACb,UAAU;MACR,IAAI;MACJ,MAAM;MACN,MAAM;MACN,QAAQ;MACR,MAAM;MACN,aAAa;MACd;KACD,UAAU;MACR,aAAa;MACb,QAAQ;MACR,cAAc;MACd,iBAAiB;MAClB;KACD,MAAM;KACP;IACD,aAAa;KACX,YAAY;MACV,aAAa;MACb,gBAAgB;MAChB,MAAM;MACN,SAAS;MACV;KACD,WAAW;MACT,aAAa;MACb,gBAAgB;MACjB;KACD,UAAU;MACR,aAAa;MACb,gBAAgB;MAChB,mBAAmB;OACjB;OACA;OACA;OACA;OACA;OACA;OACD;MACF;KACD,YAAY;MACV,aAAa;MACb,gBAAgB;MAChB,MAAM;MACP;KACF;IACD,YAAY;KACV,aAAa;KACb,iBAAiB;KACjB,gBAAgB;KAChB,QAAQ;MACN,QAAQ;MACR,QAAQ;MACR,YAAY;MACZ,aAAa;MACb,SAAS;MACT,YAAY;MACZ,WAAW;MACZ;KACF;IACD,mBAAmB;KACjB,aAAa;KACb,YAAY;MACV,MAAM;OACJ,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACD,QAAQ;OACN,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACD,WAAW;OACT,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACD,MAAM;OACJ,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACF;KACF;IACD,eAAe;KACb,aACE;KAGF,cAAc;KACd,YAAY;KACZ,cAAc;KACf;IACD,yBAAyB;KACvB,aAAa;KACb,OAAO;MACL;MACA;MACA;MACA;MACA;MACD;KACF;IACD,uBAAuB;KACrB,aAAa;KACb,SAAS;KACT,MAAM;KACP;IACD,iBAAiB,EACf,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACD,EACF;IACD,YAAY;KACV,aAAa;KACb,MAAM;KACN,QAAQ;MACN,SAAS;MACT,OAAO;MACR;KAID,SA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"}
1
+ {"version":3,"file":"server-BwICwJYA.mjs","names":["packageJson.version"],"sources":["../src/tools/context.ts","../src/tools/guards.ts","../src/tools/commit-plan.ts","../src/tools/setup.ts","../src/tools/model.ts","../src/tools/content.ts","../src/tools/vocabulary.ts","../src/tools/workflow.ts","../src/tools/reconcile.ts","../src/tools/normalize.ts","../src/tools/bulk.ts","../src/tools/doctor.ts","../src/tools/media.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, contentBranchRelation } from '../git/branch-lifecycle.js'\nimport { createGit } from '../git/identity.js'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\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 // Stats source depends on the provider:\n // - Local (projectRoot): context.json's stats can lag (init writes 0/0,\n // and the working-tree copy may be unsynced after a write), which made\n // status report models:0/entries:0 alongside a non-empty models array.\n // Derive both live from the just-enumerated models. #19\n // - Remote: the committed context.json is rebuilt per-commit and is the\n // source of truth; the reader can't always walk content cheaply, so\n // trust context.stats.\n let stats: Record<string, unknown> | null = context?.stats ?? null\n if (projectRoot) {\n const fullModels = await Promise.all(models.map(m => readModel(provider, m.id)))\n const entryCounts = await Promise.all(\n fullModels.filter((m): m is NonNullable<typeof m> => m !== null).map(m => countEntries(provider, m)),\n )\n stats = {\n models: models.length,\n entries: entryCounts.reduce((acc, c) => acc + c.total, 0),\n locales: config?.locales.supported ?? context?.stats?.locales ?? ['en'],\n ...(context?.stats?.lastSync ? { lastSync: context.stats.lastSync } : {}),\n }\n }\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 || stats) ? {\n lastOperation: context?.lastOperation ?? null,\n 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 // contentrain ↔ base relation, both directions. A diverged base is\n // the state that blocks every auto-merge advance — status must not\n // read \"in sync\" while that is true.\n try {\n const git = createGit(projectRoot)\n const branches = await git.branchLocal()\n if (branches.all.includes(CONTENTRAIN_BRANCH)) {\n const baseBranch = config?.repository?.default_branch\n ?? (branches.current !== CONTENTRAIN_BRANCH ? branches.current : 'main')\n const relation = await contentBranchRelation(git, baseBranch)\n result['content_branch'] = {\n base: baseBranch,\n ahead: relation.ahead,\n behind: relation.behind,\n relation: relation.relation,\n }\n if (relation.relation === 'diverged') {\n result['content_branch_warning']\n = `\"${CONTENTRAIN_BRANCH}\" and \"${baseBranch}\" have diverged (${relation.ahead} ahead / ${relation.behind} behind). Writes still land on \"${CONTENTRAIN_BRANCH}\", but \"${baseBranch}\" cannot fast-forward until the branches are reconciled.`\n }\n }\n } catch {\n // Relation 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 title_field: modelDef.title_field,\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_definition: {\n description: 'Shape of .contentrain/models/{model-id}.json. Keys are written in this order.',\n required: {\n id: 'Kebab-case model ID, matching the file name.',\n name: 'Human-readable display name.',\n kind: 'One of: singleton, collection, document, dictionary.',\n domain: 'Organizational group; maps to the content subdirectory.',\n i18n: 'Whether the model stores content per locale.',\n title_field: 'Name of the field shown as an entry title in listings, pickers and relation references. Must name a field declared on this model whose type is one of: string, text, slug, email, url, code, markdown, richtext. Dictionary models have no fields — they use the reserved value \"key\", meaning the entry key is the title.',\n },\n optional: {\n description: 'Model description for documentation and agent context.',\n fields: 'Field definitions. Dictionary models must NOT declare fields.',\n content_path: 'Framework-relative path for content files, replacing .contentrain/content/.',\n locale_strategy: 'How locale is encoded in file names: file (default), suffix, directory, none.',\n },\n note: 'title_field is declared, never inferred. A consumer that guesses the title from field order or length picks the icon over the headline — set it explicitly on create AND keep it correct when renaming or removing fields.',\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. A delivery gate on top of status \"published\" — it never changes status, so a draft with a past publish_at stays a draft. Meta only; never written to the content file.',\n expire_at: 'Optional: ISO 8601 date for scheduled expiry (must be after publish_at). Same gate semantics as 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:\n 'When a model has i18n:false, locale is ignored in CONTENT paths — but not in meta paths. '\n + 'The model holds one content file, so it has exactly one meta record, and that record lives '\n + 'at the default locale. Saving under a non-default locale does not move it.',\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 meta_pattern: '.contentrain/meta/{model-id}/{default-locale}.json (always the default locale, never \"data.json\")',\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<term-slug, Record<locale, translation>>',\n },\n // The nesting reads the same either way round, and a field report\n // built it locale-first — the reverse — because nothing here said\n // which. An example is the only description that cannot be misread.\n example: {\n version: 1,\n terms: {\n 'sign-in': { en: 'Sign in', tr: 'Giriş yap' },\n 'add-to-cart': { en: 'Add to cart', tr: 'Sepete ekle' },\n },\n },\n note: 'OUTER key is the term (kebab-case, locale-independent); INNER key is a locale code. One term, one entry, all its translations inside. Grouping by locale at the top level is the common mistake and produces a vocabulary that matches nothing.',\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 'Write it with contentrain_vocabulary_save — never edit the file by hand',\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 { CONTENTRAIN_BRANCH } from '@contentrain/types'\nimport type { BaseAdvance, RemotePush } from '@contentrain/types'\nimport type { FileChange } from '../core/contracts/index.js'\nimport type { ToolProvider } from '../server.js'\n\n/**\n * Context payload describing the operation, threaded into the local\n * transaction so it can regenerate `.contentrain/context.json` on the\n * contentrain branch after merge. Kept as a loose object so tool-specific\n * payloads can add optional fields (`locale`, `entries`) without churning\n * the helper's signature. Remote providers ignore it — see\n * `commitThroughProvider` below.\n */\nconst COMMIT_AUTHOR = { name: 'Contentrain', email: 'ai@contentrain.io' } as const\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 /** Non-fatal problem from the provider (push failure, partial sync, diverged base). */\n warning?: string\n /** Present when a local provider auto-merged: did the base branch advance? */\n base_advance?: BaseAdvance\n /** Present when a local provider pushed: outcome for the contentrain branch. */\n remote_push?: RemotePush\n}\n\n/**\n * The `git` object every write tool reports. One producer instead of nine\n * hand-rolled literals, so `base_advance`/`remote_push` cannot drift between\n * tools. Both fields appear only when the provider reported them (local\n * auto-merge) — remote/review responses keep their previous shape.\n */\nexport function gitReport(input: {\n branch: string\n action: string\n commit: string\n sync?: unknown\n base_advance?: BaseAdvance\n remote_push?: RemotePush\n}): Record<string, unknown> {\n return {\n branch: input.branch,\n action: input.action,\n commit: input.commit,\n ...(input.sync ? { sync: input.sync } : {}),\n ...(input.base_advance ? { base_advance: input.base_advance } : {}),\n ...(input.remote_push ? { remote_push: input.remote_push } : {}),\n }\n}\n\n/**\n * Next-step guidance for a partial success — empty when the base advanced\n * and the push landed.\n */\nexport function divergenceNextSteps(input: {\n base_advance?: BaseAdvance\n remote_push?: RemotePush\n}): string[] {\n const steps: string[] = []\n if (input.base_advance === 'blocked_diverged') {\n steps.push('DIVERGED: the content is safe on the contentrain branch, but the base branch has commits contentrain does not — run contentrain_reconcile (dry_run first) to merge them and restore the fast-forward advance')\n }\n if (input.remote_push === 'rejected') {\n steps.push('PUSH REJECTED: the remote refused the contentrain push even after a retry — the local and remote contentrain branches have diverged; fetch and run contentrain_reconcile')\n }\n return steps\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 * - **localWorktree providers** — go 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** — only the plan's own changes are\n * committed. Feature branches NEVER carry `.contentrain/context.json`:\n * the file embeds timestamps, so two parallel cr/* branches forked from\n * the same contentrain commit would always conflict on it and the\n * second merge would fail permanently. This mirrors the local\n * transaction flow, which regenerates context.json on the contentrain\n * branch after merge (single-threaded, deterministic). Remote flows\n * always report `pending-review`; Studio (or whatever orchestrator is\n * driving the server) owns the merge AND the post-merge context\n * regeneration on the contentrain branch (`buildContextChange` from\n * `@contentrain/mcp/core/context` is exported for exactly that).\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 // Capability, not class identity: \"backs onto a local worktree\" is exactly\n // what decides which applyPlan shape comes back, and it is a property any\n // implementation can declare — including a test double.\n if (provider.capabilities.localWorktree) {\n const result = await provider.applyPlan({\n branch,\n changes,\n message,\n author: COMMIT_AUTHOR,\n context: contextPayload,\n })\n return {\n commitSha: result.sha,\n // A provider that owns the merge decision reports it; one that hands the\n // branch off to an orchestrator does not, and that means review.\n workflowAction: result.workflowAction ?? 'pending-review',\n sync: result.sync,\n warning: result.warning,\n base_advance: result.base_advance,\n remote_push: result.remote_push,\n }\n }\n\n const allChanges = changes\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: COMMIT_AUTHOR,\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 { createGit } from '../git/identity.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 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 { validateModelDefinition, 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 { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\nimport { gitReport } from './commit-plan.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 = createGit(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, '--no-verify': 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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\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 ...normalizeOperationError(error, 'init'),\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 // Templates are ours, but they are still model definitions: scaffolding an\n // invalid one would hand the user a project that fails its first validate.\n const templateErrors = tmpl.models.flatMap(m =>\n validateModelDefinition(m).errors.map(e => `[${m.id}] ${e}`),\n )\n if (templateErrors.length > 0) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Scaffold template \"${templateId}\" is invalid`,\n details: templateErrors,\n }, null, 2) }],\n isError: true,\n }\n }\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\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 ...normalizeOperationError(error, 'scaffold'),\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 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, collectFieldPaths, readModel, validateModelDefinition, fieldDefZodSchema } from '../core/model-manager.js'\nimport { planModelDelete, planModelSave } from '../core/ops/index.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { commitThroughProvider, divergenceNextSteps, gitReport, type CommitThroughProviderResult } 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 title_field: z.string().describe('Name of the field shown as an entry\\'s title in listings, pickers and relation references. Must name a field on this model whose type is string, text, slug, email, url, code, markdown or richtext. Dictionary models have no fields — use \"key\".'),\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 // Read the existing model first: a field name the model already has is\n // grandfathered past the snake_case rule, so a legacy `creativeWork` does\n // not make every later edit to its model impossible (#116).\n const existing = await readModel(provider, input.id)\n const { errors, warnings: schemaWarnings } = validateModel(input, {\n existingFieldPaths: collectFieldPaths(existing?.fields),\n })\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 const readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.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 model: ModelDefinition = {\n id: input.id,\n name: input.name,\n kind: input.kind,\n domain: input.domain,\n i18n: input.i18n,\n title_field: input.title_field,\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 commit: CommitThroughProviderResult\n\n try {\n commit = await commitThroughProvider(provider, {\n branch,\n changes: savePlan.changes,\n message,\n contextPayload,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'model_save'),\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 // Constraints the schema accepts but MCP does not enforce, said out loud\n // rather than left for the author to discover in production.\n ...(schemaWarnings.length > 0 ? { schema_warnings: schemaWarnings } : {}),\n git: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n context_updated: true,\n content_path: contentPath + '/',\n ...(displayPath ? { example_file: displayPath } : {}),\n ...(importSnippet ? { import_snippet: importSnippet } : {}),\n next_steps: [\n ...divergenceNextSteps(commit),\n ...(schemaWarnings.length > 0 ? ['REVIEW: see schema_warnings — some declared constraints are not enforced by MCP'] : []),\n 'Add content with contentrain_content_save',\n ],\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 const deleteReadiness = await provider.checkWriteReadiness?.()\n if (deleteReadiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteReadiness.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 // 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 try {\n const commit = await commitThroughProvider(provider, {\n branch,\n changes: deletePlan.changes,\n message,\n contextPayload,\n })\n\n const nextSteps = divergenceNextSteps(commit)\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: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n files_removed: deletePlan.result,\n context_updated: true,\n ...(nextSteps.length > 0 ? { next_steps: nextSteps } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'model_delete'),\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 { ValidationError } from '@contentrain/types'\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 type { ContentSaveEntryResult } from '../core/ops/types.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { validateProject } from '../core/validator/index.js'\nimport { OverlayReader } from '../core/overlay-reader.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { commitThroughProvider, divergenceNextSteps, gitReport, type CommitThroughProviderResult } 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\". MEDIA FIELDS (image/video/file): for a media-library asset, pass its storage path (\"media/...\") or URL; in cloud mode these are automatically normalized to absolute public delivery URLs on save (in markdown bodies too), so saved content renders in a browser anywhere with no SDK — in local mode the relative path is kept as-is. For external images (e.g. a CDN or Unsplash URL), pass the URL directly; it is saved untouched. 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().nullable().optional().describe('Scheduled publish date, ISO 8601. Stored in meta only — never in the content file — and it does NOT change status: a draft stays a draft, and a published entry is delivered only once this date has passed. Publish with contentrain_bulk update_status. Pass null to clear a previously set date; omit to leave it unchanged.'),\n expire_at: z.string().nullable().optional().describe('Scheduled expiry, ISO 8601, must be after publish_at. Same rules as publish_at: meta only, never changes status, null clears, omit leaves unchanged.'),\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. `null` is a clear, not a date, so it\n // is skipped here and honoured by mergeEntryMeta. The dates are carried\n // on the entry itself, never merged into `data`: that merge is what wrote\n // `publish_at` into document frontmatter and collection JSON (#125).\n if (typeof entry.publish_at === 'string') {\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 (typeof entry.expire_at === 'string') {\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 (typeof entry.publish_at === 'string' && typeof entry.expire_at === 'string') {\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\n // Scheduling was mentioned — set or cleared. Said out loud in the response,\n // because the one thing an agent expects a past publish_at to do is the one\n // thing it does not: status is untouched, and a draft stays a draft.\n const schedulingTouched = input.entries.some(e => e.publish_at !== undefined || e.expire_at !== undefined)\n\n // Write-readiness gate. The provider decides what blocks a write —\n // a local worktree counts unmerged cr/* branches, a hosted one does not.\n const readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.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 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 mediaBaseUrl: provider.mediaBaseUrl,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_save'),\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 // Validate BEFORE committing. This used to run after the commit and only\n // report, so an invalid value landed in git — and was auto-merged — while\n // the response still said `status: \"committed\"` and buried the problem in a\n // next_steps string. The OverlayReader layers the pending changes over the\n // provider, so it needs nothing from the commit.\n const validationResult = await validateProject(\n new OverlayReader(provider, plan.changes),\n { model: input.model },\n )\n\n // Only this save's own entries are fatal. validateProject checks the whole\n // model, so an unrelated pre-existing error elsewhere in it must not block\n // a caller who is writing something valid — they may not even be able to\n // fix it. Everything else stays in the response as context.\n const blocking = validationResult.issues.filter(\n i => i.severity === 'error' && touchesSavedEntries(i, plan.result),\n )\n\n if (blocking.length > 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Validation failed — nothing was written.',\n issues: blocking.map(describeIssue),\n hint: 'Fix the values and call contentrain_content_save again. Nothing was committed, so there is no branch to clean up.',\n }, null, 2) }],\n isError: true,\n }\n }\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 commit: CommitThroughProviderResult\n\n try {\n commit = await commitThroughProvider(provider, {\n branch,\n changes: plan.changes,\n message,\n contextPayload,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_save'),\n }) }],\n isError: true,\n }\n }\n\n const allAdvisories = plan.result.flatMap(r => r.advisories ?? [])\n\n // Warnings do not block — they are heuristics (a colour that may not parse,\n // an extension that may contradict `accept`), and a legitimate value can sit\n // outside an approximate pattern. They ride along in the response instead.\n const warnings = validationResult.issues\n .filter(i => i.severity === 'warning' && touchesSavedEntries(i, plan.result))\n .map(i => `${i.field ?? i.locale}: ${i.message}`)\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: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n ...(allAdvisories.length > 0 ? {\n advisories: allAdvisories,\n advisory_note: 'Save succeeded. Review these warnings and consider consolidating duplicate values.',\n } : {}),\n ...(warnings.length > 0 ? { warnings } : {}),\n ...(schedulingTouched ? {\n scheduling_note: 'publish_at/expire_at were written to meta only — not to the content file — and status is unchanged: a draft is still a draft. They gate delivery of a *published* entry (served once publish_at has passed, until expire_at). To publish, call contentrain_bulk update_status.',\n } : {}),\n validation: { valid: true, errors: [] },\n context_updated: true,\n next_steps: [\n ...divergenceNextSteps(commit),\n ...(allAdvisories.length > 0 ? ['ADVISORY: Duplicate values detected — review advisories above'] : []),\n ...(warnings.length > 0 ? ['REVIEW: see warnings above — the save was not blocked by them'] : []),\n ...(schedulingTouched ? ['PUBLISH: publish_at did not change status — use contentrain_bulk update_status if this entry should go live'] : []),\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 const deleteReadiness = await provider.checkWriteReadiness?.()\n if (deleteReadiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: deleteReadiness.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 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 defaultLocale: config.locales.default,\n })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_delete'),\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 try {\n const commit = await commitThroughProvider(provider, {\n branch,\n changes: deletePlan.changes,\n message,\n contextPayload,\n })\n\n const nextSteps = divergenceNextSteps(commit)\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: gitReport({ branch, action: commit.workflowAction, commit: commit.commitSha, sync: commit.sync, base_advance: commit.base_advance, remote_push: commit.remote_push }),\n ...(commit.warning ? { warning: commit.warning } : {}),\n context_updated: true,\n ...(nextSteps.length > 0 ? { next_steps: nextSteps } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'content_delete'),\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 ...normalizeOperationError(error, 'content_list'),\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n\n/** Trim a validation issue to the fields that locate it, dropping empty ones. */\nfunction describeIssue(issue: ValidationError): Record<string, string> {\n const out: Record<string, string> = {}\n if (issue.entry) out['entry'] = issue.entry\n if (issue.slug) out['slug'] = issue.slug\n if (issue.locale) out['locale'] = issue.locale\n if (issue.field) out['field'] = issue.field\n out['message'] = issue.message\n return out\n}\n\n/**\n * Does a validation issue belong to one of the entries this save is writing?\n *\n * The write gate blocks on errors, but `validateProject` inspects the whole model.\n * Without this filter, a pre-existing bad entry somewhere else in the model would\n * block an unrelated — and perfectly valid — save, leaving the caller stuck behind\n * a problem they did not create and may not be able to fix.\n *\n * Matching is by identity: entry ID for collections, slug for documents, and\n * locale alone for singletons and dictionaries, which have one record per locale.\n */\nfunction touchesSavedEntries(\n issue: { entry?: string, slug?: string, locale?: string },\n results: ContentSaveEntryResult[],\n): boolean {\n return results.some((r) => {\n if (issue.locale && r.locale && issue.locale !== r.locale) return false\n if (r.id) return issue.entry === r.id\n if (r.slug) return issue.slug === r.slug\n // Singleton / dictionary: the locale is the whole identity, and an issue with\n // no entry or slug is about the record itself.\n return !issue.entry && !issue.slug\n })\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig } from '../core/config.js'\nimport { planVocabularyDelete, planVocabularySave } from '../core/ops/index.js'\nimport { buildBranchName } from '../git/transaction.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { commitThroughProvider } from './commit-plan.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\n\n/**\n * Vocabulary write tools.\n *\n * The vocabulary is the one piece of `.contentrain/` that had no tool. The\n * rules forbid hand-editing that directory, so the only way to add a canonical\n * term was to break the rule — which a field report duly did, having no\n * alternative.\n *\n * These go through the same plan → commit path as content and models, so a\n * vocabulary change lands on a `cr/*` branch and obeys the project's workflow\n * like everything else.\n */\nexport function registerVocabularyTools(\n server: McpServer,\n provider: ToolProvider,\n): void {\n // ─── contentrain_vocabulary_save ───\n server.tool(\n 'contentrain_vocabulary_save',\n 'Add or update canonical vocabulary terms. Terms nest as { \"term-slug\": { \"en\": \"…\", \"tr\": \"…\" } } — the OUTER key is the term, the INNER key is a locale. Merges with the existing vocabulary: a term you omit is untouched. Changes are auto-committed to git — do NOT manually edit .contentrain/vocabulary.json.',\n {\n terms: z.record(z.string(), z.record(z.string(), z.string())).describe(\n 'Terms keyed by kebab-case term slug, each holding its translations keyed by locale code. Example: { \"sign-in\": { \"en\": \"Sign in\", \"tr\": \"Giriş yap\" } }',\n ),\n },\n TOOL_ANNOTATIONS['contentrain_vocabulary_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 readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.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 let plan: Awaited<ReturnType<typeof planVocabularySave>>\n try {\n plan = await planVocabularySave(provider, { terms: input.terms })\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: error instanceof Error ? error.message : String(error),\n hint: 'Terms nest as { \"term-slug\": { \"en\": \"…\" } }. Grouping by locale at the top level is the usual mistake.',\n }, null, 2) }],\n isError: true,\n }\n }\n\n const termCount = plan.result.added.length + plan.result.updated.length\n try {\n const commit = await commitThroughProvider(provider, {\n branch: buildBranchName('vocabulary', 'save'),\n changes: plan.changes,\n message: `[contentrain] vocabulary: ${termCount} term(s)`,\n contextPayload: { tool: 'contentrain_vocabulary_save', model: 'vocabulary' },\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n added: plan.result.added,\n updated: plan.result.updated,\n total_terms: plan.result.total,\n commit: commit.commitSha,\n workflow_action: commit.workflowAction,\n ...(commit.base_advance ? { base_advance: commit.base_advance } : {}),\n ...(commit.remote_push ? { remote_push: commit.remote_push } : {}),\n ...(commit.warning ? { warning: commit.warning } : {}),\n ...(plan.advisories.length > 0 ? { advisories: plan.advisories } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'vocabulary_save'),\n }) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_vocabulary_delete ───\n server.tool(\n 'contentrain_vocabulary_delete',\n 'Remove canonical vocabulary terms by slug. Content already using a term is not touched — the vocabulary only advises. Changes are auto-committed to git.',\n {\n terms: z.array(z.string()).min(1).describe('Term slugs to remove'),\n confirm: z.boolean().describe('Must be true — this deletes canonical terms'),\n },\n TOOL_ANNOTATIONS['contentrain_vocabulary_delete']!,\n async (input) => {\n if (!input.confirm) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'Deleting vocabulary terms requires confirm: true',\n }) }],\n isError: true,\n }\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 const plan = await planVocabularyDelete(provider, { terms: input.terms })\n\n // Nothing matched — say so without spawning a branch for an empty commit.\n if (plan.changes.length === 0) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'no-op',\n deleted: [],\n missing: plan.result.missing,\n total_terms: plan.result.total,\n ...(plan.advisories.length > 0 ? { advisories: plan.advisories } : {}),\n }, null, 2) }],\n }\n }\n\n const readiness = await provider.checkWriteReadiness?.()\n if (readiness?.blocked) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: readiness.message,\n action: 'blocked',\n }, null, 2) }],\n isError: true,\n }\n }\n\n try {\n const commit = await commitThroughProvider(provider, {\n branch: buildBranchName('vocabulary', 'delete'),\n changes: plan.changes,\n message: `[contentrain] vocabulary: remove ${plan.result.deleted.length} term(s)`,\n contextPayload: { tool: 'contentrain_vocabulary_delete', model: 'vocabulary' },\n })\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'committed',\n deleted: plan.result.deleted,\n missing: plan.result.missing,\n total_terms: plan.result.total,\n commit: commit.commitSha,\n workflow_action: commit.workflowAction,\n ...(commit.base_advance ? { base_advance: commit.base_advance } : {}),\n ...(commit.remote_push ? { remote_push: commit.remote_push } : {}),\n ...(commit.warning ? { warning: commit.warning } : {}),\n ...(plan.advisories.length > 0 ? { advisories: plan.advisories } : {}),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, 'vocabulary_delete'),\n }) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { createGit } from '../git/identity.js'\nimport { CONTENTRAIN_BRANCH } from '@contentrain/types'\nimport { z } from 'zod'\nimport { type 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, deleteRemoteBranch, listRemoteCrBranches, pruneMergedRemoteBranches } from '../git/branch-lifecycle.js'\nimport { isMerged } from '../providers/local/branch-ops.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\nimport { isToolAvailable } from './availability.js'\nimport { divergenceNextSteps, gitReport } from './commit-plan.js'\n\n/**\n * Resolve a merge target from either an exact branch name or a\n * model/locale/latest selector. Returns the concrete branch, or an error with\n * candidate branches when the selector is ambiguous or matches nothing.\n */\nasync function resolveMergeBranch(\n git: SimpleGit,\n input: { branch?: string, model?: string, locale?: string, latest?: boolean },\n): Promise<{ branch: string } | { error: string, candidates: string[] }> {\n const summary = await git.branchLocal()\n const crBranches = summary.all.filter(b => b.startsWith('cr/') && b !== CONTENTRAIN_BRANCH)\n\n if (input.branch) {\n if (summary.all.includes(input.branch)) return { branch: input.branch }\n return { error: `Branch \"${input.branch}\" not found locally.`, candidates: crBranches }\n }\n\n if (!input.model) {\n return { error: 'Provide either \"branch\" (exact name) or \"model\" (to resolve the branch).', candidates: crBranches }\n }\n\n const prefix = input.locale\n ? `cr/content/${input.model}/${input.locale}/`\n : `cr/content/${input.model}/`\n let matches = crBranches.filter(b => b.startsWith(prefix))\n if (matches.length === 0) {\n // Fall back to any scope whose path contains the model segment (fix/bulk/...)\n matches = crBranches.filter(b => b.split('/').includes(input.model!))\n }\n if (matches.length === 0) {\n return { error: `No pending cr/* branch found for model \"${input.model}\".`, candidates: crBranches }\n }\n if (matches.length === 1) return { branch: matches[0]! }\n if (!input.latest) {\n return { error: `Multiple branches match model \"${input.model}\". Pass latest:true or an exact branch.`, candidates: matches }\n }\n const withTimes = await Promise.all(matches.map(async (b) => {\n const t = await git.raw(['log', '-1', '--format=%ct', b]).then(s => Number(s.trim())).catch(() => 0)\n return { b, t }\n }))\n const newest = withTimes.toSorted((left, right) => right.t - left.t)[0]!\n return { branch: newest.b }\n}\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, stray non-i18n meta layout, scheduling keys an older content_save leaked into document frontmatter) and backfills a missing model title_field — 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, stray non-i18n meta layout, publish_at/expire_at copies an older content_save left in document frontmatter when meta holds the same key) and backfill a missing model title_field. Each backfilled title_field is reported as a notice naming the rule that chose it. 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. Force auto-merge: structural fixes\n // (canonical sort, orphan meta, missing locale files) are cosmetic\n // infra repairs and should land directly on contentrain rather than\n // spawn a pending cr/fix/validate review branch.\n const branch = buildBranchName('fix', 'validate')\n const tx = await createTransaction(projectRoot, branch, { workflowOverride: 'auto-merge' })\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[] = [...divergenceNextSteps(gitResult)]\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\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 // Only suggest submit where it can actually run. validate itself works\n // over any provider, but contentrain_submit needs a local worktree and\n // a pushable remote — on a remote provider it is not even registered,\n // so recommending it sends the agent at a tool that cannot exist.\n if (result.valid && isToolAvailable('contentrain_submit', provider, projectRoot)) {\n nextSteps.push('Run contentrain_submit to push changes')\n }\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 = createGit(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 const summary = await git.branchLocal().catch(() => ({ all: [] as string[] }))\n const pending = summary.all.filter(b => b.startsWith('cr/') && b !== CONTENTRAIN_BRANCH)\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `No remote \"${remoteName}\" configured — contentrain_submit (push) is unavailable.`,\n stage: 'submit',\n agent_hint: 'This project has no git remote. In a local/solo workflow, land pending review branches with contentrain_merge instead of submit, or set workflow:\"auto-merge\" in config.json so saves merge locally without a remote.',\n pending_branches: pending,\n next_steps: [\n pending.length > 0\n ? `Merge a pending branch locally: contentrain_merge { branch: \"${pending[0]}\", confirm: true }`\n : 'Make changes with contentrain_content_save — auto-merge lands them locally without a remote',\n `Or add a remote to enable submit: git remote add ${remoteName} <url>`,\n ],\n }, null, 2) }],\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 // Lazy remote sweep: prune already-merged cr/* leftovers on the\n // remote (capped, config-gated, never throws) so review workflows\n // self-heal instead of accumulating phantom pending reviews.\n const remotePrune = await pruneMergedRemoteBranches(projectRoot, { config, max: 20 })\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 (remotePrune.errors.length > 0) nextSteps.push('Some merged remote branches could not be pruned — run `contentrain prune`')\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 remote_cleanup: remotePrune.deleted.length > 0 ? { deleted: remotePrune.deleted.length } : 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, selectively syncs .contentrain/ files to the working tree, and prunes the merged branch. Target by exact \"branch\" name, or resolve by \"model\" (+ optional \"locale\"/\"latest\").',\n {\n branch: z.string().optional().describe('Exact branch name to merge (e.g. cr/content/blog-post/...). Omit to resolve by model.'),\n model: z.string().optional().describe('Resolve the branch by model id (e.g. \"blog-post\").'),\n locale: z.string().optional().describe('Narrow model resolution to a locale.'),\n latest: z.boolean().optional().describe('When multiple branches match the model, merge the most recently committed one.'),\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 // Resolve the target branch from exact name or model/locale/latest selector\n const git = createGit(projectRoot)\n const resolved = await resolveMergeBranch(git, input)\n if ('error' in resolved) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: resolved.error,\n candidates: resolved.candidates,\n next_steps: [\n 'Pass an exact { branch } or { model, latest: true }',\n 'List pending branches with contentrain_branch_list',\n ],\n }, null, 2) }],\n isError: true,\n }\n }\n const targetBranch = resolved.branch\n\n const result = await mergeBranch(projectRoot, targetBranch)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'merged',\n branch: targetBranch,\n action: result.action,\n commit: result.commit,\n sync: result.sync,\n base_advance: result.base_advance,\n remote_push: result.remote_push,\n ...(result.warning ? { warning: result.warning } : {}),\n ...(result.remote ? { remote: result.remote } : {}),\n next_steps: [\n ...divergenceNextSteps(result),\n 'Run `contentrain generate` (or `npx contentrain-query generate`) to update the 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 result.remote?.warning\n ? `Remote copy not deleted (${result.remote.warning}) — delete it manually: git push <remote> --delete ${targetBranch}`\n : '',\n ].filter(Boolean),\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'merge'), null, 2) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_branch_list ───\n server.tool(\n 'contentrain_branch_list',\n 'List pending contentrain (cr/*) branches with their merge status against the contentrain branch. Use this to discover branch names for contentrain_merge / contentrain_branch_delete, and to monitor branch-health limits (warning at 50, blocked at 80 unmerged).',\n {\n unmerged_only: z.boolean().optional().describe('Only list branches not yet merged into contentrain. Default: false'),\n remote: z.boolean().optional().describe('Also check the git remote: annotate entries with on_remote and report remote-only cr/* leftovers. Requires network. Default: false'),\n },\n TOOL_ANNOTATIONS['contentrain_branch_list']!,\n async (input) => {\n if (!provider.capabilities.localWorktree || !projectRoot) {\n return capabilityError('contentrain_branch_list', 'localWorktree')\n }\n try {\n const git = createGit(projectRoot)\n const summary = await git.branchLocal()\n const crBranches = summary.all.filter(b => b.startsWith('cr/') && b !== CONTENTRAIN_BRANCH)\n\n const remoteList = input.remote ? await listRemoteCrBranches(projectRoot) : undefined\n const remoteNames = remoteList && !remoteList.error\n ? new Set(remoteList.branches.map(b => b.name))\n : undefined\n\n const branches = await Promise.all(crBranches.map(async (name) => {\n const [merged, ts] = await Promise.all([\n isMerged(projectRoot, name, CONTENTRAIN_BRANCH),\n git.raw(['log', '-1', '--format=%cI', name]).then(s => s.trim()).catch(() => ''),\n ])\n return {\n name,\n sha: summary.branches[name]?.commit ?? '',\n merged,\n lastCommit: ts,\n ...(remoteNames ? { on_remote: remoteNames.has(name) } : {}),\n }\n }))\n const ordered = branches.toSorted((left, right) => right.lastCommit.localeCompare(left.lastCommit))\n const filtered = input.unmerged_only ? ordered.filter(b => !b.merged) : ordered\n const health = await checkBranchHealth(projectRoot)\n\n const localNames = new Set(crBranches)\n const remoteExtras = input.remote\n ? (remoteList === null\n ? { remote_error: 'No git remote configured.' }\n : remoteList!.error\n ? { remote_error: remoteList!.error }\n : { remote_only: remoteList!.branches.filter(b => !localNames.has(b.name)).map(b => b.name) })\n : {}\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n total: ordered.length,\n unmerged: ordered.filter(b => !b.merged).length,\n branches: filtered,\n ...remoteExtras,\n health: { warning: health.warning, blocked: health.blocked, message: health.message },\n next_steps: [\n 'Merge a branch: contentrain_merge { branch: \"...\", confirm: true }',\n 'Delete a stale branch: contentrain_branch_delete { branch: \"...\", confirm: true }',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'branch_list'), null, 2) }],\n isError: true,\n }\n }\n },\n )\n\n // ─── contentrain_branch_delete ───\n server.tool(\n 'contentrain_branch_delete',\n 'Delete a pending contentrain (cr/*) branch that will not be merged — e.g. a branch left behind by a failed operation, or a superseded draft. Only cr/* branches can be deleted; the contentrain branch is protected. This is destructive: the branch and its unmerged commits are removed.',\n {\n branch: z.string().describe('The cr/* branch to delete'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n TOOL_ANNOTATIONS['contentrain_branch_delete']!,\n async (input) => {\n if (!provider.capabilities.localWorktree || !projectRoot) {\n return capabilityError('contentrain_branch_delete', 'localWorktree')\n }\n if (!input.branch.startsWith('cr/') || input.branch === CONTENTRAIN_BRANCH) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Refusing to delete \"${input.branch}\". Only cr/* feature branches can be deleted (the contentrain branch is protected).`,\n }) }],\n isError: true,\n }\n }\n try {\n const git = createGit(projectRoot)\n const summary = await git.branchLocal()\n if (!summary.all.includes(input.branch)) {\n // The local copy may already be pruned while the pushed copy\n // leaked on the remote — attempt a remote-only delete before\n // giving up.\n const remoteOnly = await deleteRemoteBranch(projectRoot, input.branch)\n if (remoteOnly.deleted) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'deleted',\n branch: input.branch,\n scope: 'remote-only',\n message: 'Branch was not present locally; its remote copy was deleted.',\n }, null, 2) }],\n }\n }\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Branch \"${input.branch}\" not found locally${remoteOnly.skipped === 'not-found' ? ' or on the remote' : ''}.`,\n ...(remoteOnly.warning ? { remote_warning: remoteOnly.warning } : {}),\n next_steps: ['List branches with contentrain_branch_list'],\n }) }],\n isError: true,\n }\n }\n const merged = await isMerged(projectRoot, input.branch, CONTENTRAIN_BRANCH)\n // Force-delete (-D): the branch may carry unmerged commits the caller\n // has explicitly decided to discard.\n await git.raw(['branch', '-D', input.branch])\n // Delete the pushed copy too — a discarded draft must not linger on\n // the remote as a phantom pending review. Best-effort, config-gated.\n const remote = await deleteRemoteBranch(projectRoot, input.branch)\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'deleted',\n branch: input.branch,\n was_merged: merged,\n remote_deleted: remote.deleted,\n ...(remote.skipped ? { remote_skipped: remote.skipped } : {}),\n ...(remote.warning ? { remote_warning: remote.warning } : {}),\n warning: merged ? undefined : 'Branch was not merged — its commits were discarded.',\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'branch_delete'), null, 2) }],\n isError: true,\n }\n }\n },\n )\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { z } from 'zod'\nimport type { ConflictResolution } from '@contentrain/types'\nimport type { ToolProvider } from '../server.js'\nimport { readConfig } from '../core/config.js'\nimport { reconcileBranches } from '../git/reconcile.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\n\nconst resolutionSchema = z.union([\n z.object({\n id: z.string().describe('Conflict id from a previous dry-run'),\n choose: z.enum(['ours', 'theirs']).describe('Take the contentrain side (ours) or the base-branch side (theirs)'),\n }),\n z.object({\n id: z.string().describe('Conflict id from a previous dry-run'),\n value: z.unknown().describe('Hand-authored replacement value'),\n }),\n])\n\nexport function registerReconcileTools(\n server: McpServer,\n provider: ToolProvider,\n projectRoot: string | undefined,\n): void {\n server.tool(\n 'contentrain_reconcile',\n 'Reconcile a diverged contentrain ↔ base-branch pair with a content-aware three-way merge. '\n + 'DRY RUN (default, dry_run:true): reports what would merge and which conflicts need a decision — touches nothing. '\n + 'EXECUTE (dry_run:false): performs the merge as a two-parent commit on contentrain and fast-forwards the base branch. '\n + 'Everything one side changed merges mechanically (entry-, key-, term+locale-level); only the same item changed differently '\n + 'on both sides becomes a conflict. Answer conflicts by passing resolutions (from the dry-run ids) and running again — '\n + 'a resolution whose values changed since the dry-run is dropped and the conflict re-reported. '\n + 'Recommended workflow: always run dry_run first, review the summary and conflicts, then execute.',\n {\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 resolutions: z.array(resolutionSchema).optional().describe('Decisions for conflicts reported by a previous dry-run, matched by id'),\n },\n TOOL_ANNOTATIONS['contentrain_reconcile']!,\n async (input) => {\n if (!provider.capabilities.localWorktree || !projectRoot) {\n return capabilityError('contentrain_reconcile', 'localWorktree')\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 const result = await reconcileBranches(projectRoot, {\n dryRun: input.dry_run,\n resolutions: input.resolutions as ConflictResolution[] | undefined,\n source: 'mcp-local',\n })\n\n if (result.action === 'in_sync') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'in_sync',\n message: `\"${result.base}\" is already contained in the contentrain branch — nothing to reconcile.`,\n base: result.base,\n }, null, 2) }],\n }\n }\n\n if (result.action === 'preview') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'preview',\n dry_run: true,\n base: result.base,\n merge_base: result.merge_base,\n summary: result.plan.result,\n changes: result.plan.changes.map(c => ({ path: c.path, action: c.content === null ? 'delete' : 'write' })),\n conflicts: result.plan.conflicts,\n ...(result.plan.advisories.length > 0 ? { advisories: result.plan.advisories } : {}),\n next_steps: result.plan.conflicts.length > 0\n ? [\n `${result.plan.conflicts.length} conflict(s) need a decision — collect resolutions ({ id, choose: 'ours'|'theirs' } or { id, value }) and call contentrain_reconcile again`,\n 'Conflicts are content decisions: ask the editor/user, do not guess',\n ]\n : ['Plan is clean — run contentrain_reconcile with dry_run:false to execute'],\n }, null, 2) }],\n }\n }\n\n if (result.action === 'conflicts') {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'conflicts',\n message: `${result.plan.conflicts.length} conflict(s) remain — nothing was written.`,\n base: result.base,\n conflicts: result.plan.conflicts,\n ...(result.plan.advisories.length > 0 ? { advisories: result.plan.advisories } : {}),\n agent_hint: 'Each conflict is a content decision. Ask the editor/user which side (or value) should win, then pass resolutions and run again.',\n }, null, 2) }],\n }\n }\n\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'reconciled',\n message: `Merged \"${result.base}\" into the contentrain branch and fast-forwarded \"${result.base}\".`,\n base: result.base,\n commit: result.commit,\n base_advance: result.base_advance,\n ...(result.remote_push ? { remote_push: result.remote_push } : {}),\n ...(result.sync ? { sync: result.sync } : {}),\n summary: result.plan.result,\n ...(result.plan.advisories.length > 0 ? { advisories: result.plan.advisories } : {}),\n next_steps: [\n 'Run contentrain_validate to verify content integrity',\n 'Run `contentrain generate` to refresh the SDK client',\n ],\n }, null, 2) }],\n }\n } catch (error) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(normalizeOperationError(error, 'reconcile'), null, 2) }],\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 title_field: z.string().optional().describe('Field shown as an entry title. Inferred from the extracted fields when omitted; the dry run reports what was chosen. Dictionary models: \"key\".'),\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 { applyStatusChange, mergeEntryMeta, readMeta, writeMeta, writeMetaEntries } from '../core/meta-manager.js'\nimport { createTransaction, buildBranchName } from '../git/transaction.js'\nimport { checkBranchHealth } from '../git/branch-lifecycle.js'\nimport { normalizeOperationError } from '../git/errors.js'\nimport { readJson, writeJson } from '../util/fs.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { capabilityError } from './guards.js'\nimport { divergenceNextSteps, gitReport } from './commit-plan.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 + 'DRY RUN (dry_run:true): runs the whole operation in a throwaway worktree and reports exactly what would change — nothing is committed and no branch survives. '\n + 'Recommended workflow: preview with dry_run:true, review the counts, then call again to execute.',\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 (collection models) or delete_entries'),\n slugs: z.array(z.string()).optional().describe('Document slugs for update_status (document models) — a document is addressed by slug, the same identity contentrain_content_save uses, not by entry ID'),\n locale: z.string().optional().describe('Scope update_status to a single locale (i18n models only; defaults to every supported locale)'),\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 (not needed while dry_run:true — a preview deletes nothing)'),\n dry_run: z.boolean().optional().describe('Preview mode: perform the operation in a throwaway worktree and report what would change, committing nothing. Defaults to false so existing callers keep executing.'),\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 // Copying REPLACES the target locale file. A preview that does not\n // say how many records are already there hides the only thing worth\n // previewing about this operation.\n let replacedCount = 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 const targetData = await readJson<Record<string, unknown>>(targetFile)\n if (targetData) replacedCount = model.kind === 'collection' ? Object.keys(targetData).length : 1\n\n await writeJson(targetFile, sourceData)\n\n if (model.kind === 'collection') {\n const entries = sourceData as Record<string, Record<string, unknown>>\n const updates: Record<string, EntryMeta> = {}\n for (const entryId of Object.keys(entries)) {\n // A copied entry is a new entry in the target locale.\n updates[entryId] = mergeEntryMeta(undefined)\n }\n // One read-modify-write for the whole locale file — see writeMetaEntries.\n const written = await writeMetaEntries(wt, model, { locale: input.target_locale!, defaultLocale: config.locales.default }, updates)\n copiedCount = written.length\n } else {\n await writeMeta(wt, model, { locale: input.target_locale!, defaultLocale: config.locales.default }, mergeEntryMeta(undefined))\n copiedCount = 1\n }\n\n })\n\n if (input.dry_run) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'preview',\n dry_run: true,\n operation: 'copy_locale',\n message: `Would copy ${copiedCount} entries from ${input.source_locale} to ${input.target_locale}.`,\n would_copy: copiedCount,\n would_replace: replacedCount,\n next_steps: replacedCount > 0\n ? [\n `${replacedCount} record(s) already exist in \"${input.target_locale}\" and would be REPLACED — confirm that is intended`,\n 'Call contentrain_bulk again without dry_run to execute',\n ]\n : ['Call contentrain_bulk again without dry_run to execute'],\n }, null, 2) }],\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\n ...(divergenceNextSteps(gitResult).length > 0 ? { next_steps: divergenceNextSteps(gitResult) } : {}),\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 ...normalizeOperationError(error, 'bulk_copy_locale'),\n }) }],\n isError: true,\n }\n } finally {\n await tx.cleanup()\n }\n }\n\n case 'update_status': {\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 // Three meta layouts, three ways to address a record. Collections key\n // meta by entry ID in one file per locale; documents keep one file per\n // slug (meta/{model}/{slug}/{locale}.json) and are addressed by slug,\n // as content_save already does (#124); singletons and dictionaries have\n // exactly one record per locale and take no list at all. Each guard is\n // checked before any git work so the caller gets a usable error, not a\n // dead end.\n const keyedByEntry = model.kind === 'collection'\n const keyedBySlug = model.kind === 'document'\n const hasEntryIds = Boolean(input.entry_ids && input.entry_ids.length > 0)\n const hasSlugs = Boolean(input.slugs && input.slugs.length > 0)\n\n if (keyedBySlug && hasEntryIds) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model \"${input.model}\" is a document model — documents are keyed by slug, not entry ID. Pass slugs instead of entry_ids.`,\n }) }],\n isError: true,\n }\n }\n if (keyedBySlug && !hasSlugs) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `update_status requires slugs for document model \"${input.model}\".` }) }],\n isError: true,\n }\n }\n if (!keyedBySlug && hasSlugs) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `slugs only apply to document models — \"${input.model}\" is a ${model.kind} model. ${keyedByEntry ? 'Pass entry_ids.' : 'Omit both entry_ids and slugs.'}`,\n }) }],\n isError: true,\n }\n }\n if (keyedByEntry && !hasEntryIds) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({ error: `update_status requires entry_ids for collection model \"${input.model}\".` }) }],\n isError: true,\n }\n }\n if (!keyedByEntry && !keyedBySlug && hasEntryIds) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Model \"${input.model}\" is a ${model.kind} model — it has one meta record per locale, so entry_ids do not apply. Omit entry_ids.`,\n }) }],\n isError: true,\n }\n }\n\n if (input.locale && !config.locales.supported.includes(input.locale)) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Locale \"${input.locale}\" is not supported. Supported: [${config.locales.supported.join(', ')}]`,\n }) }],\n isError: true,\n }\n }\n\n // A non-i18n model stores one meta record at the default locale, so\n // fanning out over supported locales would rewrite the same file.\n const targetLocales = model.i18n\n ? (input.locale ? [input.locale] : config.locales.supported)\n : [config.locales.default]\n\n const branch = buildBranchName('bulk', input.model)\n const tx = await createTransaction(projectRoot, branch)\n\n try {\n // Counted from what is actually persisted, never from the input.\n const updatedPerLocale: Record<string, string[]> = {}\n const foundIds = new Set<string>()\n\n await tx.write(async (wt) => {\n for (const locale of targetLocales) {\n if (keyedByEntry) {\n const metaData = await readMeta(wt, model, { locale, defaultLocale: config.locales.default }) as Record<string, EntryMeta> | null\n\n const updates: Record<string, EntryMeta> = {}\n for (const entryId of input.entry_ids!) {\n const existing = metaData?.[entryId]\n if (!existing) continue\n updates[entryId] = applyStatusChange(existing, input.status!)\n foundIds.add(entryId)\n }\n\n // One read-modify-write for the whole locale file — see writeMetaEntries.\n const written = await writeMetaEntries(wt, model, { locale, defaultLocale: config.locales.default }, updates)\n if (written.length > 0) updatedPerLocale[locale] = written\n } else if (keyedBySlug) {\n // One meta file per slug, so each write lands on its own path —\n // the shared-file race writeMetaEntries exists for cannot occur.\n const written: string[] = []\n for (const slug of input.slugs!) {\n const existing = await readMeta(wt, model, { locale, slug, defaultLocale: config.locales.default }) as EntryMeta | null\n if (!existing) continue\n await writeMeta(wt, model, { locale, slug, defaultLocale: config.locales.default }, applyStatusChange(existing, input.status!))\n written.push(slug)\n foundIds.add(slug)\n }\n if (written.length > 0) updatedPerLocale[locale] = written\n } else {\n const existing = await readMeta(wt, model, { locale, defaultLocale: config.locales.default }) as EntryMeta | null\n if (!existing) continue\n await writeMeta(wt, model, { locale, defaultLocale: config.locales.default }, applyStatusChange(existing, input.status!))\n updatedPerLocale[locale] = [model.id]\n }\n }\n })\n\n const updatedCount = Object.values(updatedPerLocale).reduce((n, ids) => n + ids.length, 0)\n const requested = keyedByEntry ? input.entry_ids! : keyedBySlug ? input.slugs! : []\n const notFound = requested.filter(key => !foundIds.has(key))\n\n if (updatedCount === 0) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: keyedByEntry\n ? `No meta records found for the given entry_ids in model \"${input.model}\" across locales [${targetLocales.join(', ')}]. Nothing was changed.`\n : keyedBySlug\n ? `No meta records found for the given slugs in model \"${input.model}\" across locales [${targetLocales.join(', ')}]. Nothing was changed. Check the slugs with contentrain_content_list.`\n : `No meta record found for model \"${input.model}\" across locales [${targetLocales.join(', ')}]. Nothing was changed.`,\n not_found: notFound.length > 0 ? notFound : undefined,\n }, null, 2) }],\n isError: true,\n }\n }\n\n if (input.dry_run) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'preview',\n dry_run: true,\n operation: 'update_status',\n message: `Would update ${updatedCount} meta record(s) to status \"${input.status}\" across locales [${Object.keys(updatedPerLocale).join(', ')}].`,\n would_update: updatedCount,\n would_update_by_locale: updatedPerLocale,\n not_found: notFound.length > 0 ? notFound : undefined,\n next_steps: ['Call contentrain_bulk again without dry_run to execute'],\n }, null, 2) }],\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 ...(requested.length > 0 ? { entries: requested } : {}),\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 record(s) to status \"${input.status}\" across locales [${Object.keys(updatedPerLocale).join(', ')}].`,\n updated: updatedCount,\n updated_by_locale: updatedPerLocale,\n not_found: notFound.length > 0 ? notFound : undefined,\n git: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\n ...(divergenceNextSteps(gitResult).length > 0 ? { next_steps: divergenceNextSteps(gitResult) } : {}),\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 ...normalizeOperationError(error, 'bulk_update_status'),\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 // A preview deletes nothing, so demanding confirmation for it would\n // make the safe path the harder one.\n if (!input.dry_run && input.confirm !== true) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: 'delete_entries requires confirm:true',\n hint: 'Run with dry_run:true first to see which files would be removed — no confirmation needed for a preview.',\n }) }],\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, defaultLocale: config.locales.default })\n allRemoved.push(...removed)\n }\n\n })\n\n if (input.dry_run) {\n await tx.cleanup()\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n status: 'preview',\n dry_run: true,\n operation: 'delete_entries',\n message: `Would delete ${input.entry_ids!.length} entries, removing ${allRemoved.length} file(s).`,\n would_delete: input.entry_ids!.length,\n files_to_remove: allRemoved,\n next_steps: ['Call contentrain_bulk again with dry_run:false and confirm:true to execute'],\n }, null, 2) }],\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: gitReport({ branch, action: gitResult.action, commit: gitResult.commit, sync: gitResult.sync, base_advance: gitResult.base_advance, remote_push: gitResult.remote_push }),\n ...(gitResult.warning ? { warning: gitResult.warning } : {}),\n ...(divergenceNextSteps(gitResult).length > 0 ? { next_steps: divergenceNextSteps(gitResult) } : {}),\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 ...normalizeOperationError(error, 'bulk_delete_entries'),\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 { z } from 'zod'\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type { ToolProvider } from '../server.js'\nimport { TOOL_ANNOTATIONS } from './annotations.js'\nimport { normalizeOperationError } from '../git/errors.js'\n\n/**\n * Media tools — a deterministic passthrough to the provider's optional\n * media facet (`RepoProvider.media`). Registration is capability-aware:\n * `createServer` skips all five tools when the provider has no media\n * stack, so the guard below is defense in depth only.\n *\n * The facet closes the discovery loop for external agents: list assets →\n * pick a `media/...` path → reference it via `contentrain_content_save`\n * (which normalizes it to an absolute delivery URL when `mediaBaseUrl`\n * is set). Ingest is URL-based — MCP has no binary channel — and the\n * provider implementation owns SSRF/MIME/size policy for the fetch.\n */\n\nfunction mediaUnavailable(tool: string) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `${tool} requires a provider with a media stack.`,\n capability_required: 'media',\n hint: 'Media tools are available when MCP is driven by a media-capable provider (e.g. Studio MCP Cloud). Local and plain git providers do not expose one.',\n }) }],\n isError: true as const,\n }\n}\n\nfunction mediaError(error: unknown, stage: string) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n ...normalizeOperationError(error, stage),\n }) }],\n isError: true as const,\n }\n}\n\nfunction ok(payload: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(payload) }] }\n}\n\nexport function registerMediaTools(\n server: McpServer,\n provider: ToolProvider,\n _projectRoot: string | undefined,\n): void {\n // ─── contentrain_media_list ───\n server.tool(\n 'contentrain_media_list',\n 'List media assets from the provider media stack. Returns storage paths (media/...) usable in media/image/file fields via contentrain_content_save, plus delivery URLs when available. Supports search, tag filter, and cursor pagination.',\n {\n search: z.string().optional().describe('Substring match on filename/path/alt'),\n tag: z.string().optional().describe('Only assets carrying this tag'),\n limit: z.number().int().min(1).max(100).optional().describe('Page size. Default: provider-defined'),\n cursor: z.string().optional().describe('Opaque cursor from a previous response'),\n },\n TOOL_ANNOTATIONS['contentrain_media_list']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_list')\n try {\n const result = await provider.media.list(input)\n return ok({\n assets: result.assets,\n next_cursor: result.nextCursor,\n total: result.total,\n })\n } catch (error) {\n return mediaError(error, 'media_list')\n }\n },\n )\n\n // ─── contentrain_media_get ───\n server.tool(\n 'contentrain_media_get',\n 'Get a single media asset by id, including its storage path, delivery URL, and metadata.',\n {\n id: z.string().describe('Asset id (from contentrain_media_list)'),\n },\n TOOL_ANNOTATIONS['contentrain_media_get']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_get')\n try {\n const asset = await provider.media.get(input.id)\n if (!asset) {\n return {\n content: [{ type: 'text' as const, text: JSON.stringify({\n error: `Media asset not found: ${input.id}`,\n hint: 'Use contentrain_media_list to discover asset ids.',\n }) }],\n isError: true as const,\n }\n }\n return ok({ asset })\n } catch (error) {\n return mediaError(error, 'media_get')\n }\n },\n )\n\n // ─── contentrain_media_ingest ───\n server.tool(\n 'contentrain_media_ingest',\n 'Ingest a media asset from a source URL. The provider fetches the URL server-side under its own SSRF/MIME/size policy and stores the asset in the media stack. Returns the stored asset with its media/... path.',\n {\n url: z.string().url().describe('Source URL to fetch server-side'),\n filename: z.string().optional().describe('Target filename override'),\n alt: z.string().optional().describe('Alt text for the asset'),\n tags: z.array(z.string()).max(20).optional().describe('Tags to attach'),\n },\n TOOL_ANNOTATIONS['contentrain_media_ingest']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_ingest')\n try {\n const asset = await provider.media.ingest(input)\n return ok({ status: 'ingested', asset })\n } catch (error) {\n return mediaError(error, 'media_ingest')\n }\n },\n )\n\n // ─── contentrain_media_update ───\n server.tool(\n 'contentrain_media_update',\n 'Update metadata (alt text, tags, filename) of an existing media asset. Does not touch the binary.',\n {\n id: z.string().describe('Asset id to update'),\n alt: z.string().optional().describe('New alt text'),\n tags: z.array(z.string()).max(20).optional().describe('Replacement tag list'),\n filename: z.string().optional().describe('New filename'),\n },\n TOOL_ANNOTATIONS['contentrain_media_update']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_update')\n try {\n const { id, ...patch } = input\n const asset = await provider.media.update(id, patch)\n return ok({ status: 'updated', asset })\n } catch (error) {\n return mediaError(error, 'media_update')\n }\n },\n )\n\n // ─── contentrain_media_delete ───\n server.tool(\n 'contentrain_media_delete',\n 'Delete a media asset from the media stack. Content entries referencing its path are NOT rewritten — check references before deleting.',\n {\n id: z.string().describe('Asset id to delete'),\n confirm: z.literal(true).describe('Must be true to confirm deletion'),\n },\n TOOL_ANNOTATIONS['contentrain_media_delete']!,\n async (input) => {\n if (!provider.media) return mediaUnavailable('contentrain_media_delete')\n try {\n await provider.media.delete(input.id)\n return ok({ status: 'deleted', id: input.id })\n } catch (error) {\n return mediaError(error, 'media_delete')\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'\nimport { isToolAvailable, TOOL_REQUIREMENTS } from './tools/availability.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 { registerVocabularyTools } from './tools/vocabulary.js'\nimport { registerWorkflowTools } from './tools/workflow.js'\nimport { registerReconcileTools } from './tools/reconcile.js'\nimport { registerNormalizeTools } from './tools/normalize.js'\nimport { registerBulkTools } from './tools/bulk.js'\nimport { registerDoctorTools } from './tools/doctor.js'\nimport { registerMediaTools } from './tools/media.js'\nimport packageJson from '../package.json' with { type: 'json' }\n\n/**\n * Default MCP `instructions` surfaced to clients at initialize time.\n * Deliberately kept under 512 characters — directory listings and client\n * UIs truncate longer strings. Override via `CreateServerOptions.instructions`.\n */\nexport const DEFAULT_INSTRUCTIONS\n = 'Contentrain is git-native content governance: models define structure; '\n + 'content is canonical JSON/Markdown on a dedicated branch. Start with '\n + 'contentrain_status; call contentrain_describe_format before creating '\n + 'models or content, and contentrain_describe before editing a model\\'s '\n + 'entries. Every write lands on an isolated cr/* branch and content_save '\n + 'validates before committing. Destructive tools require confirm:true; '\n + 'preview contentrain_apply with dry_run:true first. Content decisions '\n + 'stay with the agent.'\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) are not registered when no\n * projectRoot is available.\n */\n projectRoot?: string\n /**\n * MCP `instructions` string sent to clients in the `initialize` response.\n * Defaults to `DEFAULT_INSTRUCTIONS`; pass an empty string to omit\n * instructions entirely.\n */\n instructions?: string\n}\n\n/**\n * `McpServer` variant that silently skips registration for tools named in\n * `skipTools`. Register functions call `server.tool(...)` unconditionally;\n * this subclass is what keeps capability-gated tools out of `tools/list`\n * when the provider (or missing projectRoot) could never satisfy them.\n */\nclass CapabilityFilteredMcpServer extends McpServer {\n private readonly _skipTools: ReadonlySet<string>\n\n constructor(\n serverInfo: ConstructorParameters<typeof McpServer>[0],\n options: ConstructorParameters<typeof McpServer>[1],\n skipTools: ReadonlySet<string>,\n ) {\n super(serverInfo, options)\n this._skipTools = skipTools\n }\n\n // McpServer.tool has six overloads; a rest-args override is the only\n // signature that satisfies all of them. Skipped tools return undefined —\n // register functions discard the handle, so nothing downstream observes it.\n override tool(...args: unknown[]): ReturnType<McpServer['tool']> {\n if (this._skipTools.has(args[0] as string)) {\n return undefined as unknown as ReturnType<McpServer['tool']>\n }\n return (McpServer.prototype.tool as (...toolArgs: unknown[]) => ReturnType<McpServer['tool']>).apply(this, args)\n }\n}\n\n/**\n * Create an MCP server instance with every *available* Contentrain tool\n * 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.\n *\n * Tool listing is capability-aware: tools whose requirements\n * (`TOOL_REQUIREMENTS`) cannot be met by the resolved provider +\n * projectRoot pair are not registered, so `tools/list` only advertises\n * tools that can actually succeed. With a `LocalProvider` (stdio and CLI\n * flows) all 19 tools remain registered — behavior there is unchanged.\n */\nexport function createServer(projectRoot: string): McpServer\nexport function createServer(opts: CreateServerOptions): McpServer\nexport function createServer(input: string | CreateServerOptions): McpServer {\n const { provider, projectRoot, instructions } = resolveServerContext(input)\n\n const skipTools = new Set(\n Object.keys(TOOL_REQUIREMENTS).filter(name => !isToolAvailable(name, provider, projectRoot)),\n )\n\n const server = new CapabilityFilteredMcpServer(\n {\n name: 'contentrain-mcp',\n version: packageJson.version,\n },\n { instructions: instructions || undefined },\n skipTools,\n )\n\n registerContextTools(server, provider, projectRoot)\n registerSetupTools(server, provider, projectRoot)\n registerModelTools(server, provider, projectRoot)\n registerContentTools(server, provider, projectRoot)\n registerVocabularyTools(server, provider)\n registerWorkflowTools(server, provider, projectRoot)\n registerReconcileTools(server, provider, projectRoot)\n registerNormalizeTools(server, provider, projectRoot)\n registerBulkTools(server, provider, projectRoot)\n registerDoctorTools(server, provider, projectRoot)\n registerMediaTools(server, provider, projectRoot)\n\n return server\n}\n\nfunction resolveServerContext(input: string | CreateServerOptions): {\n provider: ToolProvider\n projectRoot: string | undefined\n instructions: string\n} {\n if (typeof input === 'string') {\n return { provider: new LocalProvider(input), projectRoot: input, instructions: DEFAULT_INSTRUCTIONS }\n }\n const instructions = input.instructions ?? DEFAULT_INSTRUCTIONS\n if (input.provider) {\n let projectRoot = input.projectRoot\n // Structural, not nominal: any worktree-backed provider exposes a\n // projectRoot, and a test double should be able to as well.\n if (!projectRoot) {\n const maybeRoot = (input.provider as { projectRoot?: unknown }).projectRoot\n if (typeof maybeRoot === 'string') projectRoot = maybeRoot\n }\n return { provider: input.provider, projectRoot, instructions }\n }\n if (input.projectRoot) {\n return { provider: new LocalProvider(input.projectRoot), projectRoot: input.projectRoot, instructions }\n }\n throw new Error('createServer: either `provider` or `projectRoot` must be provided')\n}\n"],"mappings":"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ACV,qBAAqB;KACnB,aAAa;KACb,QAAQ;MACN,4BAA4B;MAC5B,6BAA6B;MAC7B,gCAAgC;MAChC,uCAAuC;MACvC,6CAA6C;MAC7C,iCAAiC;MAClC;KACF;IACD,kBAAkB;KAChB,aAAa;KACb,UAAU;MACR,IAAI;MACJ,MAAM;MACN,MAAM;MACN,QAAQ;MACR,MAAM;MACN,aAAa;MACd;KACD,UAAU;MACR,aAAa;MACb,QAAQ;MACR,cAAc;MACd,iBAAiB;MAClB;KACD,MAAM;KACP;IACD,aAAa;KACX,YAAY;MACV,aAAa;MACb,gBAAgB;MAChB,MAAM;MACN,SAAS;MACV;KACD,WAAW;MACT,aAAa;MACb,gBAAgB;MACjB;KACD,UAAU;MACR,aAAa;MACb,gBAAgB;MAChB,mBAAmB;OACjB;OACA;OACA;OACA;OACA;OACA;OACD;MACF;KACD,YAAY;MACV,aAAa;MACb,gBAAgB;MAChB,MAAM;MACP;KACF;IACD,YAAY;KACV,aAAa;KACb,iBAAiB;KACjB,gBAAgB;KAChB,QAAQ;MACN,QAAQ;MACR,QAAQ;MACR,YAAY;MACZ,aAAa;MACb,SAAS;MACT,YAAY;MACZ,WAAW;MACZ;KACF;IACD,mBAAmB;KACjB,aAAa;KACb,YAAY;MACV,MAAM;OACJ,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACD,QAAQ;OACN,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACD,WAAW;OACT,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACD,MAAM;OACJ,aAAa;OACb,cAAc;OACd,YAAY;OACb;MACF;KACF;IACD,eAAe;KACb,aACE;KAGF,cAAc;KACd,YAAY;KACZ,cAAc;KACf;IACD,yBAAyB;KACvB,aAAa;KACb,OAAO;MACL;MACA;MACA;MACA;MACA;MACD;KACF;IACD,uBAAuB;KACrB,aAAa;KACb,SAAS;KACT,MAAM;KACP;IACD,iBAAiB,EACf,OAAO;KACL;KACA;KACA;KACA;KACA;KACA;KACD,EACF;IACD,YAAY;KACV,aAAa;KACb,MAAM;KACN,QAAQ;MACN,SAAS;MACT,OAAO;MACR;KAID,SA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"}