Contentrain MCP
Extrae, gobierna y distribuye contenido estructurado desde tu base de código.
Documentación
@contentrain/mcp
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/compilersvelte
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
contentrainsirve 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
contentrainobtenida: 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 devuelveCONTENT_WORKING_TREE_STALE - en una rama de características verificada, cuyas escrituras no actualizan,
.contentrain/**se lee de la referenciacontentrainen su lugar (los archivos fuente aún provienen del árbol de trabajo); las herramientas de lectura lo informan comocontent_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: falseen 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
contentraindespué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).
| Herramienta | Propósito | Solo lectura | Destructiva |
|---|---|---|---|
contentrain_status | Estado del proyecto, configuración, modelos, salud de ramas, contexto | Sí | — |
contentrain_describe | Esquema completo y datos de muestra para un modelo | Sí | — |
contentrain_describe_format | Referencia del contrato de formato de archivo y almacenamiento | Sí | — |
contentrain_doctor | Informe de salud del proyecto (entorno, estructura, modelos, huérfanos, ramas locales + remotas, SDK) | Sí | — |
contentrain_init | Crear estructura .contentrain/ y configuración base | — | — |
contentrain_scaffold | Aplicar una plantilla inicial como blog, docs, landing, saas | — | — |
contentrain_model_save | Crear o actualizar una definición de modelo | — | — |
contentrain_model_delete | Eliminar una definición de modelo | — | Sí |
contentrain_content_save | Guardar entradas de contenido para cualquier tipo de modelo | — | — |
contentrain_content_delete | Eliminar entradas de contenido | — | Sí |
contentrain_content_list | Leer entradas de contenido | Sí | — |
contentrain_validate | Validar contenido del proyecto, opcionalmente corregir automáticamente problemas estructurales | — | — |
contentrain_submit | Empujar ramas cr/* al remoto, luego podar perezosamente los restos fusionados locales + remotos | — | — |
contentrain_merge | Fusionar una rama en modo de revisión en contentrain localmente (por rama exacta o modelo); elimina su copia remota | — | — |
contentrain_reconcile | Fusión de tres vías consciente del contenido de un par divergente contentrain ↔ base (dry_run primero, resoluciones después) | — | — |
contentrain_branch_list | Listar ramas cr/* pendientes con estado de fusión (remote: true agrega vista remota) | Sí | — |
contentrain_branch_delete | Eliminar una rama cr/* obsoleta/fallida localmente y en el remoto (rama contentrain protegida) | — | Sí |
contentrain_scan | Escaneo de cadenas codificadas basado en grafos y candidatos | Sí | — |
contentrain_apply | Ejecución de extracción/reutilización de normalización con soporte de dry-run | — | — |
contentrain_bulk | Copia masiva de configuraciones regionales, actualizaciones de estado y eliminaciones (dry_run previsualiza) | — | — |
contentrain_media_list | Listar activos de medios (búsqueda, filtro de etiquetas, paginación por cursor) | Sí | — |
contentrain_media_get | Obtener un activo de medios por id | Sí | — |
contentrain_media_ingest | Ingestionar un activo desde una URL fuente (el proveedor obtiene del lado del servidor) | — | — |
contentrain_media_update | Actualizar metadatos del activo (alt, etiquetas, nombre de archivo) | — | — |
contentrain_media_delete | Eliminar 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:
- Llame a
contentrain_status - Si es necesario, llame a
contentrain_init - Cree modelos con
contentrain_model_saveocontentrain_scaffold - Guarde contenido con
contentrain_content_save - Valide con
contentrain_validate - Para cadenas codificadas, use
contentrain_scany luegocontentrain_apply - 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/restse envía como dependencia opcional entre pares. - GitLabProvider — gitbeaker sobre la API REST de GitLab. Sin clon, sin worktree.
@gitbeaker/restse 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 (incluyendoplanReconciley su adaptador de lecturabindRef) 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 ycontentrain status/serve(env →repository.default_branch→origin/HEAD→main→master→ rama verificada);resolveBaseBranchSource(lo mismo, más qué peldaño respondió);resolveInitDefaultBranch(lo queinitregistra comorepository.default_branch, onull)@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:
| Alcance | Solo 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 |
| Consentimiento | Una 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/** |
| Rama | Una 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 |
| Rastro | Un MigrationAuditEntry por archivo: paso, ruta, acción, el patrón que lo permitió, actor, marca de tiempo, commit |
| Deshacer | Una 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_requireden 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ó.