Contentrain MCP

Extrae, gobierna y distribuye contenido estructurado desde tu base de código.

Documentación

@contentrain/mcp

npm version GitHub source Docs

Motor MCP agnóstico del proveedor para Contentrain: local-first por defecto, con backends opcionales de GitHub y GitLab, y un transporte HTTP para controladores remotos como Studio.

Comience aquí:

Contentrain es infraestructura de gobernanza de contenido generado por IA:

  • el agente produce decisiones de contenido
  • MCP aplica flujos de trabajo deterministas de sistema de archivos y git
  • los humanos revisan y fusionan
  • el sistema mantiene consistencia de esquema, configuración regional y serialización

Este paquete es el núcleo de ejecución detrás de la integración MCP de Contentrain. Se puede usar como:

  • un servidor MCP stdio (contentrain-mcp)
  • un servidor incrustable (createServer(projectRoot))
  • un kit de herramientas de bajo nivel para configuración, modelos, contenido, validación, escaneo y flujo de transacciones git

Instalación

pnpm add @contentrain/mcp

Requisitos:

  • Node.js 22+
  • git disponible en la máquina

Soporte opcional de analizadores para un escaneo de código fuente de mayor calidad:

  • @vue/compiler-sfc
  • @astrojs/compiler
  • svelte

Se enumeran como dependencias opcionales. El escáner sigue funcionando sin ellos, pero la detección de Vue/Astro/Svelte es más sólida cuando están instalados.

Qué Hace

@contentrain/mcp gestiona un directorio .contentrain/ en su proyecto y expone herramientas MCP para:

  • inicialización del proyecto
  • creación y eliminación de modelos
  • guardado, eliminación y listado de contenido
  • validación y corrección automática
  • flujos de escaneo y aplicación de normalización
  • operaciones masivas
  • envío de ramas, fusión en modo de revisión y conocimiento de la salud de las ramas
  • verificación de salud del proyecto (doctor)

Todas las operaciones de escritura están diseñadas en torno a la seguridad respaldada por git:

  • una rama dedicada contentrain sirve como única fuente de verdad del estado del contenido
  • cada escritura crea un árbol de trabajo temporal en una rama de características bifurcada de contentrain (nombre de rama: cr/{operation}/{model}/{locale}/{timestamp}-{suffix})
  • fusión automática: la característica se fusiona en contentrain, baseBranch avanza mediante update-ref, los archivos .contentrain/ se sincronizan selectivamente al árbol de trabajo del desarrollador cuando la rama base es la que está verificada
  • baseBranch = env CONTENTRAIN_BRANCH → repository.default_branch → HEAD remoto → main → master → rama verificada (último recurso). Una escritura realizada en una rama de características nunca fusiona, mueve o empuja esa rama
  • los planes se leen del árbol de trabajo pero se confirman en la punta de contentrain obtenida: los cambios JSON se transfieren clave por clave, por lo que las entradas que otro escritor empujó se conservan; un conflicto de mismo valor no escribe nada y devuelve CONTENT_WORKING_TREE_STALE
  • en una rama de características verificada, cuyas escrituras no actualizan, .contentrain/** se lee de la referencia contentrain en su lugar (los archivos fuente aún provienen del árbol de trabajo); las herramientas de lectura lo informan como content_source
  • revisión: la rama de características se empuja al remoto para revisión del equipo; una vez fusionada (o eliminada), su copia remota también se elimina — con el mejor esfuerzo, opte por no participar con remoteBranchCleanup: false en config.json
  • la detección de ramas fusionadas sobrevive a reescrituras del historial base (verificación de ascendencia con respaldo de patch-id), por lo que los rebases/squashes no dejan ramas obsoletas varadas
  • el árbol de trabajo del desarrollador nunca se muta durante las operaciones git de MCP (sin stash, sin checkout, sin merge)
  • context.json nunca llega a las ramas de características: se regenera en la rama contentrain después de la fusión (localmente por la capa de transacción; en flujos remotos por el orquestador que posee la fusión)
  • salida JSON canónica: claves ordenadas, sangría de 2 espacios, nueva línea final
  • validación + sugerencias de siguientes pasos expuestas al llamador

Superficie de Herramientas

27 herramientas MCP — 22 principales + 5 de medios — con anotaciones (readOnlyHint, destructiveHint, idempotentHint y openWorldHint: false en todas partes excepto contentrain_media_ingest, que obtiene una URL proporcionada por el llamador del lado del servidor) para sugerencias de seguridad del cliente.

El listado de herramientas es consciente de las capacidades. tools/list solo anuncia herramientas que el par proveedor resuelto + projectRoot puede satisfacer realmente. Un servidor stdio local lista las 22 herramientas principales; una sesión impulsada por un proveedor remoto (GitHub/GitLab, sin checkout local) lista solo el subconjunto seguro remoto: status, describe, describe_format, model_save, model_delete, content_save, content_delete, content_list, validate. El mapa de requisitos vive en TOOL_REQUIREMENTS (@contentrain/mcp/tools/availability).

HerramientaPropósitoSolo lecturaDestructiva
contentrain_statusEstado del proyecto, configuración, modelos, salud de ramas, contextoSí—
contentrain_describeEsquema completo y datos de muestra para un modeloSí—
contentrain_describe_formatReferencia del contrato de formato de archivo y almacenamientoSí—
contentrain_doctorInforme de salud del proyecto (entorno, estructura, modelos, huérfanos, ramas locales + remotas, SDK)Sí—
contentrain_initCrear estructura .contentrain/ y configuración base——
contentrain_scaffoldAplicar una plantilla inicial como blog, docs, landing, saas——
contentrain_model_saveCrear o actualizar una definición de modelo——
contentrain_model_deleteEliminar una definición de modelo—Sí
contentrain_content_saveGuardar entradas de contenido para cualquier tipo de modelo——
contentrain_content_deleteEliminar entradas de contenido—Sí
contentrain_content_listLeer entradas de contenidoSí—
contentrain_validateValidar contenido del proyecto, opcionalmente corregir automáticamente problemas estructurales——
contentrain_submitEmpujar ramas cr/* al remoto, luego podar perezosamente los restos fusionados locales + remotos——
contentrain_mergeFusionar una rama en modo de revisión en contentrain localmente (por rama exacta o modelo); elimina su copia remota——
contentrain_reconcileFusión de tres vías consciente del contenido de un par divergente contentrain ↔ base (dry_run primero, resoluciones después)——
contentrain_branch_listListar ramas cr/* pendientes con estado de fusión (remote: true agrega vista remota)Sí—
contentrain_branch_deleteEliminar una rama cr/* obsoleta/fallida localmente y en el remoto (rama contentrain protegida)—Sí
contentrain_scanEscaneo de cadenas codificadas basado en grafos y candidatosSí—
contentrain_applyEjecución de extracción/reutilización de normalización con soporte de dry-run——
contentrain_bulkCopia masiva de configuraciones regionales, actualizaciones de estado y eliminaciones (dry_run previsualiza)——
contentrain_media_listListar activos de medios (búsqueda, filtro de etiquetas, paginación por cursor)Sí—
contentrain_media_getObtener un activo de medios por idSí—
contentrain_media_ingestIngestionar un activo desde una URL fuente (el proveedor obtiene del lado del servidor)——
contentrain_media_updateActualizar metadatos del activo (alt, etiquetas, nombre de archivo)——
contentrain_media_deleteEliminar un activo de la pila de medios—Sí

Las cinco herramientas contentrain_media_* son un paso directo determinista a la faceta de medios opcional del proveedor (RepoProvider.media) y se registran solo cuando el proveedor expone una (por ejemplo, Studio MCP Cloud). Los servidores stdio locales y los proveedores simples de GitHub/GitLab nunca los listan. La ingestion se basa en URL (MCP no tiene canal binario); la implementación del proveedor es dueña de la política de SSRF/MIME/tamaño para la obtención.

Inicio Rápido

Configurar vía CLI (recomendado)

npx contentrain setup claude-code   # or: cursor, vscode, windsurf, copilot

Esto crea automáticamente el archivo de configuración MCP correcto para su IDE. Consulte documentación de CLI para más detalles.

Ejecutar como servidor MCP independiente

CONTENTRAIN_PROJECT_ROOT=/path/to/project npx contentrain-mcp

Si CONTENTRAIN_PROJECT_ROOT se omite, se usa el directorio de trabajo actual.

Incrustar el servidor en su propio proceso

import { createServer } from '@contentrain/mcp/server'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'

const server = createServer(process.cwd())
const transport = new StdioServerTransport()

await server.connect(transport)

createServer también acepta un objeto de opciones: { provider, projectRoot?, instructions? }. instructions establece la cadena instructions de MCP que los clientes reciben en initialize (por defecto un DEFAULT_INSTRUCTIONS integrado, mantenido bajo 512 caracteres; pase '' para omitirlo).

Flujo MCP de Ejemplo

Flujo de trabajo típico del agente:

  1. Llame a contentrain_status
  2. Si es necesario, llame a contentrain_init
  3. Cree modelos con contentrain_model_save o contentrain_scaffold
  4. Guarde contenido con contentrain_content_save
  5. Valide con contentrain_validate
  6. Para cadenas codificadas, use contentrain_scan y luego contentrain_apply
  7. Empuje ramas de revisión con contentrain_submit

Flujo de Normalización

La normalización se divide intencionalmente en dos fases:

1. Extracción

contentrain_scan encuentra cadenas candidatas.

contentrain_apply con mode: "extract":

  • crea o actualiza modelos
  • escribe entradas de contenido
  • registra el seguimiento de la fuente
  • crea una rama de revisión (cr/normalize/extract/{domain}/{timestamp})

2. Reutilización

contentrain_apply con mode: "reuse":

  • parchea archivos fuente usando expresiones proporcionadas por el agente
  • agrega importaciones cuando es necesario
  • aplica seguridad de ruta de parche y verificaciones de alcance
  • crea una rama de revisión separada (cr/normalize/reuse/{model}/{locale}/{timestamp})

Esta división mantiene la extracción de contenido separada de la reescritura de código fuente.

Requisitos de transporte / proveedor

La normalización (contentrain_scan y contentrain_apply) requiere acceso a disco local: los escáneres AST recorren el árbol fuente y parchean archivos en el lugar. Se ejecuta solo en un LocalProvider (transporte stdio, o transporte HTTP configurado con un LocalProvider).

Los proveedores remotos como GitHubProvider exponen astScan: false, sourceRead: false y sourceWrite: false. Llamar a estas herramientas a través de un proveedor remoto devuelve un error de capacidad uniforme:

{
  "error": "contentrain_scan requires local filesystem access.",
  "capability_required": "astScan",
  "hint": "This tool is unavailable when MCP is driven by a remote provider (e.g. GitHubProvider). Use a LocalProvider or the stdio transport."
}

Los agentes que impulsan un transporte remoto deben recurrir a un transporte local (o un checkout local) antes de invocar la normalización.

Proveedores Remotos

MCP admite tres backends detrás del mismo contrato RepoProvider:

  • LocalProvider — simple-git + worktree. Cada herramienta (incluida la normalización) funciona en él. El transporte stdio usa este por defecto.
  • GitHubProvider — Octokit sobre las API de Git Data + Repos. Sin clon, sin worktree. @octokit/rest se envía como dependencia opcional entre pares.
  • GitLabProvider — gitbeaker sobre la API REST de GitLab. Sin clon, sin worktree. @gitbeaker/rest se envía como dependencia opcional entre pares. Admite gitlab.com y CE / EE autoalojados.

Cada proveedor remoto implementa la misma superficie: lector (readFile / listDirectory / fileExists), escritor (applyPlan — un commit atómico), operaciones de rama (list / create / delete / diff / merge / isMerged / getDefaultBranch). mergeBranch pasa directamente en GitHub; en GitLab abre un MR y lo acepta inmediatamente para que la forma final de MergeResult coincida de cualquier manera. El proveedor de GitHub también toma un SHA de commit completo como applyPlan de base (y createBranch de fromRef) para escrituras de comparar-y-establecer: una rama faltante se bifurca de ese commit, y una rama que se ha movido lejos de él rechaza la escritura con PROVIDER_CONFLICT.

GitLab — instalación y uso

pnpm add @gitbeaker/rest
import { createGitLabProvider } from '@contentrain/mcp/providers/gitlab'
import { createServer } from '@contentrain/mcp/server'

const provider = await createGitLabProvider({
  auth: { type: 'pat', token: process.env.GITLAB_TOKEN! },
  project: {
    projectId: 'acme/site',             // or numeric project ID
    host: 'https://gitlab.company.com', // omit for gitlab.com
  },
})

const server = createServer({ provider })
// serve over stdio or the HTTP transport from @contentrain/mcp/server/http

Capacidades: sourceRead, sourceWrite, astScan, localWorktree son todas false; pushRemote, branchProtection, pullRequestFallback son true. Normalización / escaneo / aplicación rechazan con un error de capacidad en GitLabProvider: recurra a un transporte local para esos flujos.

Bitbucket — próximamente

El soporte de Bitbucket Cloud + Data Center está en la hoja de ruta. Hasta que el proveedor se publique, use la herramienta contentrain_describe_format para impulsar operaciones de contenido de Contentrain manualmente desde un checkout de Bitbucket a través de la ruta LocalProvider.

Exportaciones Principales

El paquete también expone módulos de bajo nivel para incrustación y uso avanzado:

  • @contentrain/mcp/server
  • @contentrain/mcp/server/http
  • @contentrain/mcp/core/config
  • @contentrain/mcp/core/context
  • @contentrain/mcp/core/model-manager
  • @contentrain/mcp/core/content-manager
  • @contentrain/mcp/core/validator
  • @contentrain/mcp/core/scanner
  • @contentrain/mcp/core/graph-builder
  • @contentrain/mcp/core/apply-manager
  • @contentrain/mcp/core/scan-config
  • @contentrain/mcp/core/doctor
  • @contentrain/mcp/core/contracts
  • @contentrain/mcp/core/ops — APIs de plan (incluyendo planReconcile y su adaptador de lectura bindRef) más ayudantes de rutas relativas a la raíz de contenido: contentDirPath, contentFilePath, documentFilePath, metaFilePath
  • @contentrain/mcp/core/overlay-reader
  • @contentrain/mcp/core/migration — la ruta de escritura de migración en lista blanca: createMigrationWriter, scopeHash, decide. Ver Escrituras de migración abajo
  • @contentrain/mcp/util/detect
  • @contentrain/mcp/util/fs
  • @contentrain/mcp/git/transaction
  • @contentrain/mcp/git/branch-lifecycle — salud/limpieza de ramas más el ciclo de vida remoto de cr/*: deleteRemoteBranch, listRemoteCrBranches, pruneMergedRemoteBranches, isRefMerged, classifyMergedBranches
  • @contentrain/mcp/git/base-branch — resolveBaseBranch, el único resolvedor de rama base que usan cada escritura MCP y contentrain status / serve (env → repository.default_branch → origin/HEAD → main → master → rama verificada); resolveBaseBranchSource (lo mismo, más qué peldaño respondió); resolveInitDefaultBranch (lo que init registra como repository.default_branch, o null)
  • @contentrain/mcp/git/errors
  • @contentrain/mcp/git/reconcile — reconcileBranches, el ejecutor de reconciliación local
  • @contentrain/mcp/tools/annotations
  • @contentrain/mcp/templates
  • @contentrain/mcp/providers/local
  • @contentrain/mcp/providers/github
  • @contentrain/mcp/providers/gitlab

Estos están destinados a las herramientas de Contentrain y a integraciones avanzadas, no para edición manual directa de archivos .contentrain/.

Escrituras de migración

Una migración no entrega contenido, entrega un código base: src/, public/, package.json, un archivo de bloqueo, configuración de despliegue. La ruta de escritura de contenido nunca fue construida para nada de eso, y ampliarla movería silenciosamente un límite de seguridad sobre el que descansa todo el producto — "Contentrain solo escribe .contentrain/" es una promesa, no un detalle de implementación.

@contentrain/mcp/core/migration es la puerta en su lugar. Envuelve un proveedor cuyo applyPlan ya puede escribir cualquier ruta, y pone el consentimiento delante de él.

import { createMigrationWriter, scopeHash } from '@contentrain/mcp/core/migration'

const scope = {
  allow: ['src/**', 'public/**', 'package.json', 'astro.config.mjs'],
  branch: 'migration/acme',
  base: 'main',
}

// Show the user `scope`, not the hash. Then bind their consent to it:
const approval = {
  scope_hash: await scopeHash(scope),
  approver: { kind: 'human', id: userId, role: 'owner' },
  approved_at: new Date().toISOString(),
}

const writer = createMigrationWriter(provider, scope, approval)
await writer.applyPlan({ ...plan, step: 'emit', actor })
writer.audit  // which file, in which step, by which actor, in which commit

Lo que garantiza:

AlcanceSolo rutas que cubre un patrón de lista blanca. .contentrain/** no es implícito — una migración que quiere la tienda lo solicita, y la persona que aprueba ve que lo hace
ConsentimientoUna aprobación, vinculada al alcance exacto por scope_hash. Una aprobación de src/** no puede reproducirse contra un alcance que también tiene .github/workflows/**
RamaUna ref migration/*. La rama contentrain nunca es un objetivo ni una base — incluso a través del valor predeterminado de ApplyPlanInput, que una migración no debe heredar
RastroUn MigrationAuditEntry por archivo: paso, ruta, acción, el patrón que lo permitió, actor, marca de tiempo, commit
DeshacerUna rama, entregada como un PR, reversible con un solo git revert

Cada rechazo ocurre antes de que se llame al proveedor, y un plan se rechaza completo: un plan con una ruta fuera del alcance no escribe nada en absoluto, ni siquiera su parte permitida. Una migración aplicada a medias es más difícil de recuperar que una que nunca comenzó.

El emparejador de rutas es un control de seguridad y está escrito para fallar de forma cerrada. Atravesamiento, rutas absolutas, separadores de barra invertida, codificación porcentual, segmentos vacíos y caracteres de control se rechazan antes de consultar cualquier patrón; los patrones están anclados en ambos extremos, por lo que src/* no cubre src/a/b y nada coincide por ser un prefijo; y los metacaracteres regex en un patrón son literales, por lo que una entrada de lista blanca no puede cubrir silenciosamente más de lo que lee.

Restricciones de diseño

Decisiones clave de diseño en este paquete:

  • local-primero por defecto — transporte stdio + LocalProvider funciona sin ninguna dependencia de red
  • motor agnóstico al proveedor — las mismas herramientas centrales se ejecutan sobre LocalProvider, GitHubProvider o GitLabProvider detrás de un único contrato RepoProvider; las herramientas de medios usan la faceta de medios opcional del proveedor
  • los SDK de proveedores remotos (@octokit/rest, @gitbeaker/rest) son dependencias opcionales — se incorporan solo cuando se usa su proveedor
  • almacenamiento de contenido solo JSON
  • flujo de trabajo de escritura respaldado por git (transacción de árbol de trabajo localmente, un solo commit atómico sobre las APIs de Git Data / REST de forma remota)
  • serialización canónica — salida determinista en bytes, claves ordenadas, nueva línea final
  • capa MCP agnóstica al framework
  • el agente decide la semántica del contenido, MCP impone la ejecución determinista
  • puertas de capacidad — las herramientas que necesitan acceso al árbol de fuentes (normalize, scan, apply, doctor) rechazan con un error uniforme capability_required en proveedores remotos

Desarrollo

Desde la raíz del monorepo:

pnpm --filter @contentrain/mcp build
pnpm --filter @contentrain/mcp test
pnpm --filter @contentrain/mcp typecheck
pnpm exec oxlint packages/mcp/src packages/mcp/tests

Paquetes relacionados

  • contentrain — CLI y herramientas de revisión local
  • @contentrain/query — SDK de consulta de runtime generado
  • @contentrain/rules — reglas y prompts de IDE/agente
  • @contentrain/types — tipos de esquema y modelo compartidos

Documentación

Documentación completa en ai.contentrain.io/packages/mcp.

Licencia

MIT

Cobertura de locales (model.locales)

contentrain_validate verifica la paridad de un modelo i18n: true contra cada locale en config.locales.supported. Un modelo que declara locales — un subconjunto de esa lista — se verifica contra el subconjunto en su lugar, que es cómo un sitio parcialmente traducido declara la verdad en lugar de fallar en traducciones que nunca tuvo. Ausente, como en todo modelo que precede al campo, significa la lista completa del proyecto.

La severidad no cambia y aún sigue el tipo: una traducción faltante es una advertencia en un modelo document y un error en un modelo collection. Cada mensaje nombra la lista contra la que se evaluó, por lo que los dos casos se leen por separado:

Locale file missing: tr.json (checked against the model's own locales [en, tr])
Entry parity: entry "a1b2c3" exists in en but missing in tr (checked against the project's supported locales [en, tr, da])

contentrain_model_save acepta locales y rechaza un locale fuera de config.locales.supported. contentrain_validate fix:true informa una declaración rota pero nunca inventa una: reducir la cobertura de un modelo es una decisión de contenido, y el único valor que la herramienta podría derivar — los locales que casualmente tienen archivos hoy — escribiría las brechas actuales en el esquema y silenciaría los errores que las revelan.

Configuración de modelo en runtime

Las ediciones estructurales de contentrain_model_save preservan los bloques existentes de nivel superior form y comments. Estos bloques pertenecen al proveedor de runtime; la herramienta estructural no los habilita ni los cambia. Su respuesta lista preserved_blocks cuando la configuración se trasladó.