Contentrain MCP

Extraia, governe e envie conteúdo estruturado do seu código-fonte.

Documentação

@contentrain/mcp

npm version GitHub source Docs

Mecanismo MCP agnóstico de provedor para Contentrain — local-first por padrão, com backends opcionais para GitHub e GitLab e transporte HTTP para drivers remotos como o Studio.

Comece por aqui:

Contentrain é uma infraestrutura de governança de conteúdo gerado por IA:

  • o agente produz decisões de conteúdo
  • o MCP aplica fluxos de trabalho determinísticos de sistema de arquivos e git
  • humanos revisam e mesclam
  • o sistema mantém schema, locale e serialização consistentes

Este pacote é o núcleo de runtime por trás da integração MCP da Contentrain. Ele pode ser usado como:

  • um servidor MCP stdio (contentrain-mcp)
  • um servidor incorporável (createServer(projectRoot))
  • um kit de ferramentas de baixo nível para config, modelos, conteúdo, validação, varredura e fluxo de transações git

Instalação

pnpm add @contentrain/mcp

Requisitos:

  • Node.js 22+
  • git disponível na máquina

Suporte opcional de parser para varredura de código-fonte de maior qualidade:

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

Eles estão listados como dependências opcionais. O scanner ainda funciona sem eles, mas a detecção de Vue/Astro/Svelte é mais forte quando estão instalados.

O Que Ele Faz

@contentrain/mcp gerencia um diretório .contentrain/ no seu projeto e expõe ferramentas MCP para:

  • inicialização de projeto
  • criação e exclusão de modelos
  • salvar, excluir e listar conteúdo
  • validação e correção automática
  • fluxos de varredura e aplicação de normalização
  • operações em lote
  • envio de branch, merge em modo de revisão e conscientização de saúde de branch
  • verificação de saúde do projeto (doctor)

Todas as operações de escrita são projetadas em torno da segurança baseada em git:

  • um branch dedicado contentrain serve como fonte única de verdade do estado do conteúdo
  • cada escrita cria um worktree temporário em um branch de feature bifurcado de contentrain (nome do branch: cr/{operation}/{model}/{locale}/{timestamp}-{suffix})
  • auto-merge: o feature é mesclado em contentrain, o baseBranch é avançado via update-ref, os arquivos .contentrain/ são sincronizados seletivamente para a árvore de trabalho do desenvolvedor quando o branch base é o que está com checkout
  • baseBranch = env CONTENTRAIN_BRANCH → repository.default_branch → HEAD remoto → main → master → branch com checkout (último recurso). Uma escrita feita em um branch de feature nunca mescla, move ou envia esse branch
  • os planos são lidos da árvore de trabalho, mas commitados no tip do contentrain buscado: as mudanças JSON são transportadas chave por chave, então entradas enviadas por outro escritor são mantidas; um conflito de mesmo valor não escreve nada e retorna CONTENT_WORKING_TREE_STALE
  • em um branch de feature com checkout, onde escritas não atualizam, .contentrain/** é lido da ref contentrain (arquivos de origem ainda vêm da árvore de trabalho); ferramentas de leitura o reportam como content_source
  • revisão: branch de feature enviado para o remoto para revisão da equipe; uma vez mesclado (ou excluído), sua cópia remota também é removida — melhor esforço, opte por sair com remoteBranchCleanup: false no config.json
  • a detecção de branch mesclado sobrevive a reescritas do histórico da base (verificação de ancestralidade com fallback de patch-id), então rebases/squashes não deixam branches obsoletos presos
  • a árvore de trabalho do desenvolvedor nunca é mutada durante operações git do MCP (sem stash, sem checkout, sem merge)
  • context.json nunca chega a branches de feature — ele é regenerado no branch contentrain após o merge (localmente pela camada de transação; em fluxos remotos pelo orquestrador que possui o merge)
  • saída JSON canônica — chaves ordenadas, indentação de 2 espaços, nova linha final
  • validação + dicas de próximos passos exibidas ao chamador

Superfície de Ferramentas

27 ferramentas MCP — 22 principais + 5 de mídia — com anotações (readOnlyHint, destructiveHint, idempotentHint e openWorldHint: false em todos os lugares, exceto contentrain_media_ingest, que busca uma URL fornecida pelo chamador no lado do servidor) para dicas de segurança do cliente.

A listagem de ferramentas é consciente de capacidade. tools/list só anuncia ferramentas que o par provedor resolvido + projectRoot pode realmente atender. Um servidor stdio local lista as 22 ferramentas principais; uma sessão dirigida por um provedor remoto (GitHub/GitLab, sem checkout local) lista apenas o subconjunto seguro para remoto — status, describe, describe_format, model_save, model_delete, content_save, content_delete, content_list, validate. O mapa de requisitos vive em TOOL_REQUIREMENTS (@contentrain/mcp/tools/availability).

FerramentaPropósitoSomente leituraDestrutiva
contentrain_statusStatus do projeto, config, modelos, saúde de branch, contextoSim—
contentrain_describeSchema completo e dados de exemplo para um modeloSim—
contentrain_describe_formatReferência de contrato de formato de arquivo e armazenamentoSim—
contentrain_doctorRelatório de saúde do projeto (env, estrutura, modelos, órfãos, branches locais + remotos, SDK)Sim—
contentrain_initCriar estrutura .contentrain/ e config base——
contentrain_scaffoldAplicar um template inicial como blog, docs, landing, saas——
contentrain_model_saveCriar ou atualizar uma definição de modelo——
contentrain_model_deleteExcluir uma definição de modelo—Sim
contentrain_content_saveSalvar entradas de conteúdo para qualquer tipo de modelo——
contentrain_content_deleteExcluir entradas de conteúdo—Sim
contentrain_content_listLer entradas de conteúdoSim—
contentrain_validateValidar conteúdo do projeto, opcionalmente corrigir automaticamente problemas estruturais——
contentrain_submitEnviar branches cr/* para o remoto, depois podar preguiçosamente sobras locais + remotas mescladas——
contentrain_mergeMesclar um branch em modo de revisão em contentrain localmente (por branch exato ou modelo); exclui sua cópia remota——
contentrain_reconcileMerge de três vias consciente de conteúdo de um par divergente contentrain ↔ base (dry_run primeiro, resoluções depois)——
contentrain_branch_listListar branches cr/* pendentes com status de merge (remote: true adiciona visão remota)Sim—
contentrain_branch_deleteExcluir um branch cr/* obsoleto/falho localmente e no remoto (branch contentrain protegido)—Sim
contentrain_scanVarredura de strings hardcoded baseada em grafo e candidatosSim—
contentrain_applyExecução de extração/reutilização de normalização com suporte a dry-run——
contentrain_bulkCópia de locale em lote, atualizações de status e exclusões (dry_run pré-visualiza)——
contentrain_media_listListar ativos de mídia (busca, filtro de tag, paginação por cursor)Sim—
contentrain_media_getObter um ativo de mídia por idSim—
contentrain_media_ingestIngerir um ativo de uma URL de origem (provedor busca no lado do servidor)——
contentrain_media_updateAtualizar metadados do ativo (alt, tags, nome do arquivo)——
contentrain_media_deleteExcluir um ativo da pilha de mídia—Sim

As cinco ferramentas contentrain_media_* são um passthrough determinístico para a faceta de mídia opcional do provedor (RepoProvider.media) e são registradas somente quando o provedor expõe uma (ex.: Studio MCP Cloud). Servidores stdio locais e provedores GitHub/GitLab simples nunca as listam. A ingestão é baseada em URL (MCP não tem canal binário); a implementação do provedor é dona da política de SSRF/MIME/tamanho para a busca.

Início Rápido

Configurar via CLI (recomendado)

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

Isso cria automaticamente o arquivo de config MCP correto para sua IDE. Veja documentação da CLI para detalhes.

Executar como servidor MCP autônomo

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

Se CONTENTRAIN_PROJECT_ROOT for omitido, o diretório de trabalho atual é usado.

Incorporar o servidor no seu próprio processo

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 também aceita um objeto de opções: { provider, projectRoot?, instructions? }. instructions define a string instructions do MCP que os clientes recebem em initialize (padrão para um DEFAULT_INSTRUCTIONS embutido, mantido abaixo de 512 caracteres; passe '' para omitir).

Exemplo de Fluxo MCP

Fluxo de trabalho típico do agente:

  1. Chame contentrain_status
  2. Se necessário, chame contentrain_init
  3. Crie modelos com contentrain_model_save ou contentrain_scaffold
  4. Salve conteúdo com contentrain_content_save
  5. Valide com contentrain_validate
  6. Para strings hardcoded, use contentrain_scan e depois contentrain_apply
  7. Envie branches de revisão com contentrain_submit

Fluxo de Normalização

A normalização é intencionalmente dividida em duas fases:

1. Extração

contentrain_scan encontra strings candidatas.

contentrain_apply com mode: "extract":

  • cria ou atualiza modelos
  • escreve entradas de conteúdo
  • registra rastreamento de origem
  • cria um branch de revisão (cr/normalize/extract/{domain}/{timestamp})

2. Reutilização

contentrain_apply com mode: "reuse":

  • aplica patches em arquivos de origem usando expressões fornecidas pelo agente
  • adiciona imports quando necessário
  • aplica segurança de caminho de patch e verificações de escopo
  • cria um branch de revisão separado (cr/normalize/reuse/{model}/{locale}/{timestamp})

Essa divisão mantém a extração de conteúdo separada da reescrita de código-fonte.

Requisitos de transporte / provedor

A normalização (contentrain_scan e contentrain_apply) requer acesso ao disco local — scanners AST percorrem a árvore de origem e aplicam patches nos arquivos no lugar. Ela roda apenas em um LocalProvider (transporte stdio, ou transporte HTTP configurado com um LocalProvider).

Provedores remotos como GitHubProvider expõem astScan: false, sourceRead: false e sourceWrite: false. Chamar essas ferramentas através de um provedor remoto retorna um erro de capacidade 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."
}

Agentes dirigindo um transporte remoto devem recorrer a um transporte local (ou um checkout local) antes de invocar a normalização.

Provedores Remotos

O MCP suporta três backends por trás do mesmo contrato RepoProvider:

  • LocalProvider — simple-git + worktree. Toda ferramenta (normalização incluída) funciona nele. O transporte stdio usa este por padrão.
  • GitHubProvider — Octokit sobre as APIs Git Data + Repos. Sem clone, sem worktree. @octokit/rest é enviado como dependência opcional de peer.
  • GitLabProvider — gitbeaker sobre a API REST do GitLab. Sem clone, sem worktree. @gitbeaker/rest é enviado como dependência opcional de peer. Suporta gitlab.com e CE / EE auto-hospedados.

Cada provedor remoto implementa a mesma superfície: leitor (readFile / listDirectory / fileExists), escritor (applyPlan — um commit atômico), operações de branch (list / create / delete / diff / merge / isMerged / getDefaultBranch). mergeBranch passa direto no GitHub; no GitLab ele abre um MR e imediatamente o aceita para que a forma final do MergeResult corresponda de qualquer maneira. O provedor GitHub também aceita um SHA de commit completo como applyPlan do base (e createBranch do fromRef) para escritas compare-and-set: um branch ausente bifurca desse commit, e um branch que se moveu para longe dele recusa a escrita com PROVIDER_CONFLICT.

GitLab — instalação e 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 são todos false; pushRemote, branchProtection, pullRequestFallback são true. Normalização / varredura / aplicação rejeitam com erro de capacidade no GitLabProvider — recorra a um transporte local para esses fluxos.

Bitbucket — em breve

Suporte para Bitbucket Cloud + Data Center está no roadmap. Até o provedor ser lançado, use a ferramenta contentrain_describe_format para dirigir operações de conteúdo da Contentrain manualmente a partir de um checkout Bitbucket via caminho LocalProvider.

Exportações Principais

O pacote também expõe módulos de baixo nível para incorporação e uso avançado:

  • @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 plano (incluindo planReconcile e seu adaptador de leitura bindRef) além de auxiliares de caminho relativos à raiz de conteúdo: contentDirPath, contentFilePath, documentFilePath, metaFilePath
  • @contentrain/mcp/core/overlay-reader
  • @contentrain/mcp/core/migration — o caminho de escrita de migração na lista de permissões: createMigrationWriter, scopeHash, decide. Veja Escritas de migração abaixo
  • @contentrain/mcp/util/detect
  • @contentrain/mcp/util/fs
  • @contentrain/mcp/git/transaction
  • @contentrain/mcp/git/branch-lifecycle — saúde/limpeza de branch além do ciclo de vida remoto cr/*: deleteRemoteBranch, listRemoteCrBranches, pruneMergedRemoteBranches, isRefMerged, classifyMergedBranches
  • @contentrain/mcp/git/base-branch — resolveBaseBranch, o único resolvedor de branch base que toda escrita MCP e contentrain status / serve usam (env → repository.default_branch → origin/HEAD → main → master → branch com checkout); resolveBaseBranchSource (o mesmo, mais qual degrau respondeu); resolveInitDefaultBranch (o que init registra como repository.default_branch, ou null)
  • @contentrain/mcp/git/errors
  • @contentrain/mcp/git/reconcile — reconcileBranches, o executor de reconciliação local
  • @contentrain/mcp/tools/annotations
  • @contentrain/mcp/templates
  • @contentrain/mcp/providers/local
  • @contentrain/mcp/providers/github
  • @contentrain/mcp/providers/gitlab

Estes são destinados às ferramentas do Contentrain e integrações avançadas, não para edição manual direta de arquivos .contentrain/.

Escritas de migração

Uma migração não entrega conteúdo, ela entrega um codebase: src/, public/, package.json, um lockfile, configuração de deploy. O caminho de escrita de conteúdo nunca foi construído para nada disso, e ampliá-lo moveria silenciosamente uma fronteira de segurança na qual todo o produto se apoia — "Contentrain só escreve .contentrain/" é uma promessa, não um detalhe de implementação.

@contentrain/mcp/core/migration é o portão em vez disso. Ele envolve um provedor cujo applyPlan já pode escrever qualquer caminho, e coloca consentimento na frente dele.

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

O que ele garante:

EscopoApenas caminhos cobertos por um padrão da lista de permissões. .contentrain/** não é implícito — uma migração que quer a loja pede por ela, e a pessoa que aprova vê que ela pede
ConsentimentoUma aprovação, vinculada ao escopo exato por scope_hash. Uma aprovação de src/** não pode ser reproduzida contra um escopo que também tenha .github/workflows/**
BranchUma ref migration/*. O branch contentrain nunca é um alvo e nunca é uma base — inclusive através do padrão de ApplyPlanInput, que uma migração não deve herdar
TrilhaUm MigrationAuditEntry por arquivo: etapa, caminho, ação, o padrão que permitiu, ator, timestamp, commit
DesfazerUm branch, entregue como um PR, revertível com um único git revert

Toda recusa acontece antes de o provedor ser chamado, e um plano é recusado inteiro: um plano com um caminho fora do escopo não escreve nada, nem mesmo sua parte permitida. Uma migração meio aplicada é mais difícil de recuperar do que uma que nunca começou.

O correspondente de caminhos é um controle de segurança e foi escrito para falhar fechado. Travessia, caminhos absolutos, separadores de barra invertida, codificação percentual, segmentos vazios e caracteres de controle são recusados antes de qualquer padrão ser consultado; padrões são ancorados em ambas as extremidades, então src/* não cobre src/a/b e nada corresponde por ser um prefixo; e metacaracteres regex em um padrão são literais, então uma entrada da lista de permissões não pode silenciosamente cobrir mais do que lê.

Restrições de design

Decisões-chave de design neste pacote:

  • local-first por padrão — transporte stdio + LocalProvider funciona sem qualquer dependência de rede
  • motor agnóstico de provedor — as mesmas ferramentas principais rodam sobre LocalProvider, GitHubProvider ou GitLabProvider atrás de um único contrato RepoProvider; ferramentas de mídia usam a faceta de mídia opcional do provedor
  • SDKs de provedores remotos (@octokit/rest, @gitbeaker/rest) são dependências pares opcionais — puxadas apenas quando seu provedor é usado
  • armazenamento de conteúdo somente JSON
  • fluxo de escrita apoiado em git (transação de worktree localmente, commit atômico único sobre as APIs Git Data / REST remotamente)
  • serialização canônica — saída deterministicamente em bytes, chaves ordenadas, nova linha final
  • camada MCP agnóstica de framework
  • o agente decide semântica de conteúdo, MCP impõe execução determinística
  • portões de capacidade — ferramentas que precisam de acesso à árvore de origem (normalize, scan, apply, doctor) rejeitam com um erro uniforme capability_required em provedores remotos

Desenvolvimento

Da raiz do 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

Pacotes relacionados

  • contentrain — CLI e ferramentas de revisão local
  • @contentrain/query — SDK de consulta de runtime gerado
  • @contentrain/rules — regras e prompts de IDE/agente
  • @contentrain/types — tipos de esquema e modelo compartilhados

Documentação

Documentação completa em ai.contentrain.io/packages/mcp.

Licença

MIT

Cobertura de locale (model.locales)

contentrain_validate verifica a paridade de um modelo i18n: true contra cada locale em config.locales.supported. Um modelo que declara locales — um subconjunto dessa lista — é verificado contra o subconjunto em vez disso, que é como um site parcialmente traduzido declara a verdade em vez de falhar em traduções que nunca teve. Ausente, como em todo modelo que precede o campo, significa a lista inteira do projeto.

A severidade é inalterada e ainda segue o tipo: uma tradução ausente é um aviso em um modelo document e um erro em um modelo collection. Cada mensagem nomeia a lista contra a qual foi avaliada, então os dois casos se leem separados:

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 aceita locales e rejeita um locale fora de config.locales.supported. contentrain_validate fix:true relata uma declaração quebrada, mas nunca inventa uma: estreitar a cobertura de um modelo é uma decisão de conteúdo, e o único valor que a ferramenta poderia derivar — os locales que por acaso têm arquivos hoje — escreveria as lacunas atuais no esquema e silenciaria os erros que as revelam.

Configuração de modelo em runtime

Edições estruturais de contentrain_model_save preservam blocos form e comments de nível superior existentes. Esses blocos pertencem ao provedor de runtime; a ferramenta estrutural não os habilita ou altera. Sua resposta lista preserved_blocks quando a configuração foi transportada adiante.