contentrain_status
Project Status
Get full project status (read-only). Returns config, models, context. Do NOT manually edit .contentrain/ based on this output.
无需输入参数。

io.github.Contentrain由 GitHub 账号 Contentrain 发布v3.12.3更新于 Nov 4, 2026
Git content governance, 23 deterministic MCP tools. Hosted: docs.contentrain.io/developer/mcp-cloud
以 Git 为原生的内容治理服务器,让助手定义内容模型、保存与发布多语言内容、校验内容并管理评审分支。
Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。
把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。
阅读 https://sourceweft.com/mcp/io.github.Contentrain%2Fcontentrain/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。把它添加到你客户端的 mcpServers 配置中。 客户端会在首次连接时完成 OAuth 登录。
{
"mcpServers": {
"contentrain": {
"type": "http",
"url": "https://studio.contentrain.io/api/mcp/remote"
}
}
}@contentrain/mcp[npm version] [GitHub source] [Docs]
Provider-agnostic MCP engine for Contentrain — local-first by default, with optional GitHub and GitLab backends and an HTTP transport for remote drivers such as Studio.
Start here:
Contentrain is a content governance ecosystem for teams and AI agents. In it, this engine works like this:
This package is the runtime core behind Contentrain's MCP integration. It can be used as:
contentrain-mcp)createServer(projectRoot))Requirements:
22+Optional parser support for higher-quality source scanning:
@vue/compiler-sfc@astrojs/compilersvelteThey are listed as optional dependencies. The scanner still works without them, but Vue/Astro/Svelte detection is stronger when they are installed.
@contentrain/mcp manages a .contentrain/ directory in your project and exposes MCP tools for:
All write operations are designed around git-backed safety:
contentrain branch serves as the content state single source of truthcontentrain (branch name: cr/{scope}/{target}/{timestamp}-{suffix}, e.g. cr/content/blog-post/1730000000-a1b2; bulk locale copies, locale-scoped status changes and scaffold add a {locale} segment before the timestamp)contentrain, baseBranch advanced via update-ref, .contentrain/ files selectively synced to developer's working tree when the base branch is the one checked outCONTENTRAIN_BRANCH env → repository.default_branch → remote HEAD → main → master → checked-out branch → main (fallback). A write made on a feature branch never merges, moves or pushes that branchcontentrain tip: JSON changes are carried over key by key, so entries another writer pushed are kept; a same-value conflict writes nothing and returns CONTENT_WORKING_TREE_STALE.contentrain/** is read from the contentrain ref instead (source files still from the working tree); read tools report it as content_sourceremoteBranchCleanup: false in config.jsongit.push: false or CONTENTRAIN_NO_PUSH=1): nothing is ever pushed or deleted on the remote, and every result says so — see Git environment and local modegit block names the resolved base_branch; when it is not the checked-out branch, the result's warning says your working tree was left alonegit.verify: true or CONTENTRAIN_VERIFY=1 runs them, and a rejecting hook fails the write with its output — see Commit hookscontentrain branch after merge (locally by the transaction layer; in remote flows by the orchestrator that owns the merge)23 MCP tools, plus 5 media tools on a media-capable provider such as Studio, with annotations (readOnlyHint, destructiveHint, idempotentHint, and openWorldHint: false everywhere except contentrain_media_ingest, which fetches a caller-supplied URL server-side) for client safety hints.
Tool listing is capability-aware. tools/list only advertises tools the resolved provider + projectRoot pair can actually satisfy. A local stdio server lists the 23 core tools; a session driven by a remote provider (GitHub/GitLab, no local checkout) lists only the remote-safe subset — status, describe, describe_format, model_save, model_delete, content_save, content_delete, content_list, content_status, vocabulary_save, vocabulary_delete, validate. The requirement map lives in TOOL_REQUIREMENTS (@contentrain/mcp/tools/availability).
The five contentrain_media_* tools are a deterministic passthrough to the provider's optional media facet (RepoProvider.media) and are registered only when the provider exposes one (e.g. Studio MCP Cloud). Local stdio servers and plain GitHub/GitLab providers never list them. Ingest is URL-based (MCP has no binary channel); the provider implementation owns SSRF/MIME/size policy for the fetch.
This auto-creates the correct MCP config file for your IDE. See CLI docs for details.
If CONTENTRAIN_PROJECT_ROOT is omitted, the current working directory is used.
These apply to the local provider (stdio, LocalProvider):
Local mode keeps every write on local branches: no branch push, no review push, no push after an auto-merge, approve or reconcile, and no remote branch delete or prune. Fetching is not a push and still happens when a remote exists. Turn it on for a project with "git": { "push": false } in .contentrain/config.json, or for one run with CONTENTRAIN_NO_PUSH=1.
Precedence: the env wins over the config in both directions. CONTENTRAIN_NO_PUSH=1 turns pushing off even when config allows it; CONTENTRAIN_NO_PUSH=0 turns it back on over git.push: false. With neither, pushing is on.
It is never a silent success. Each write's git block reports remote_push: "disabled" with remote_note: "not pushed (local mode)", and next_steps carries a LOCAL MODE line. contentrain_submit returns an error instead of pushing; land review branches with contentrain_merge. contentrain_merge and contentrain_branch_delete delete the local branch as usual and leave any remote copy untouched; contentrain_branch_delete reports remote_skipped: "local-mode", contentrain_merge reports remote: { deleted: false, skipped: "local-mode" }.
By default Contentrain's machine commits (the cr/* content commit, the context.json commit, a reconcile merge commit, the seed commit of an empty repo) skip the repository's commit hooks (pre-commit, prepare-commit-msg, commit-msg). That keeps existing projects working: these commits are made in a temporary worktree without your node_modules, and their [contentrain] … subject fails a strict commitlint. Merges still run the merge hooks and pushes still run pre-push, as before.
To run commit hooks on every machine commit, set "git": { "verify": true } in .contentrain/config.json, or CONTENTRAIN_VERIFY=1 for one run (CONTENTRAIN_VERIFY=0 skips them over the config). With hooks on:
context.json commit (the content is already on contentrain) is reported in the result's warning, not swallowed;npx lint-staged needs the tool available there.createServer also accepts an options object: { provider, projectRoot?, instructions? }. instructions sets the MCP instructions string clients receive at initialize (defaults to defaultInstructions for the tools the server registers — with every tool available, DEFAULT_INSTRUCTIONS — kept under 512 characters; pass '' to omit). The HTTP entries (startHttpMcpServer, startHttpMcpServerWith) take the same instructions option.
Typical agent workflow:
contentrain_statuscontentrain_initcontentrain_model_save or contentrain_scaffoldcontentrain_content_savecontentrain_validatecontentrain_scan then contentrain_applycontentrain_submitNormalize is intentionally split into two phases:
contentrain_scan finds candidate strings.
contentrain_apply with mode: "extract":
cr/normalize/extract/{timestamp}-{suffix})contentrain_apply with mode: "reuse":
cr/normalize/reuse/{model or domain}/{timestamp}-{suffix})This split keeps content extraction separate from source rewriting.
Normalize (contentrain_scan and contentrain_apply) requires local disk access — AST scanners walk the source tree and patch files in place. It runs only on a LocalProvider (stdio transport, or HTTP transport configured with a LocalProvider).
Remote providers such as GitHubProvider expose astScan: false, sourceRead: false, and sourceWrite: false, so a server built on one does not register contentrain_scan or contentrain_apply at all (see TOOL_REQUIREMENTS). A provider that can read the source tree but not write it lists contentrain_apply; its reuse mode then returns a uniform capability error:
Agents driving a remote transport should fall back to a local transport (or a local checkout) before invoking normalize.
MCP supports three backends behind the same RepoProvider contract:
@octokit/rest ships as an optional peer dependency.@gitbeaker/rest ships as an optional peer dependency. Supports gitlab.com and self-hosted CE / EE.Each remote provider implements the same surface: reader (readFile / listDirectory / fileExists), writer (applyPlan — one atomic commit), branch ops (list / create / delete / diff / merge / isMerged / getDefaultBranch). mergeBranch goes straight through on GitHub; on GitLab it opens an MR and immediately accepts it so the final MergeResult shape matches either way. The GitHub provider also takes a full commit SHA as applyPlan's base (and createBranch's fromRef) for compare-and-set writes: a missing branch forks from that commit, and a branch that has moved away from it refuses the write with PROVIDER_CONFLICT.
Capabilities: sourceRead, sourceWrite, astScan, localWorktree are all false; pushRemote, branchProtection, pullRequestFallback are true. Normalize (scan / apply) is not registered on a GitLabProvider server — fall back to a local transport for those flows.
There is no Bitbucket provider. A Bitbucket checkout works through the LocalProvider path like any other local Git repository.
The package also exposes low-level modules for embedding and advanced use:
@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 — plan APIs (including planReconcile and its bindRef reader adapter) plus content-root-relative path helpers: contentDirPath, contentFilePath, documentFilePath, metaFilePath@contentrain/mcp/core/overlay-reader@contentrain/mcp/core/migration — the allowlisted migration write path: createMigrationWriter, scopeHash, decide. See Migration writes below@contentrain/mcp/util/detect@contentrain/mcp/util/fs@contentrain/mcp/git/transaction@contentrain/mcp/git/branch-lifecycle — branch health/cleanup plus the remote cr/* lifecycle: deleteRemoteBranch, listRemoteCrBranches, pruneMergedRemoteBranches, isRefMerged, classifyMergedBranches@contentrain/mcp/git/base-branch — resolveBaseBranch, the one base-branch resolver every MCP write and contentrain status / serve use (env → repository.default_branch → <remote>/HEAD → main → master → checked-out branch → main); resolveBaseBranchSource (the same, plus which rung answered); resolveInitDefaultBranch (what init records as repository.default_branch, or null)@contentrain/mcp/git/errors@contentrain/mcp/git/reconcile — reconcileBranches, the local reconcile executor@contentrain/mcp/tools/annotations@contentrain/mcp/templates@contentrain/mcp/providers/local@contentrain/mcp/providers/github@contentrain/mcp/providers/gitlabThese are intended for Contentrain tooling and advanced integrations, not for direct manual editing of .contentrain/ files.
A migration does not deliver content, it delivers a codebase: src/, public/,
package.json, a lockfile, deploy configuration. The content write path was
never built for any of that, and widening it would quietly move a security
boundary the whole product rests on — "Contentrain only writes .contentrain/"
is a promise, not an implementation detail.
@contentrain/mcp/core/migration is the gate instead. It wraps a provider whose
applyPlan can already write any path, and puts consent in front of it.
What it guarantees:
Every refusal happens before the provider is called, and a plan is refused whole: a plan with one path outside the scope writes nothing at all, not even its allowed part. A half-applied migration is harder to recover from than one that never started.
The path matcher is a security control and is written to fail closed. Traversal,
absolute paths, backslash separators, percent-encoding, empty segments and
control characters are refused before any pattern is consulted; patterns are
anchored at both ends, so src/* does not cover src/a/b and nothing matches
by being a prefix; and regex metacharacters in a pattern are literal, so an
allowlist entry cannot silently cover more than it reads.
Key design decisions in this package:
RepoProvider contract (remote providers get the remote-safe subset); media tools ride the provider's optional media facet@octokit/rest, @gitbeaker/rest) are optional peer dependencies — pulled in only when their provider is usedTOOL_REQUIREMENTS is the mapFrom the monorepo root:
contentrain — CLI and local review tooling@contentrain/query — generated runtime query SDK@contentrain/rules — IDE/agent rules and prompts@contentrain/types — shared schema and model typesFull documentation at ai.contentrain.io/packages/mcp.
Contentrain is a content governance ecosystem for teams and AI agents. This package is one of the open-source tools (docs). Studio is the team surface for review and roles; it drives this engine over the HTTP transport and hosts it for external agents as MCP Cloud, and the contentrain_media_* tools need a provider with a media stack such as Studio's. Migrate moves WordPress sites to Astro, and Bridge is a free WordPress plugin that exports a site as a Contentrain store.
MIT
model.locales)contentrain_validate checks an i18n: true model's parity against every locale
in config.locales.supported. A model that declares locales — a subset of that
list — is checked against the subset instead, which is how a partially-translated
site states the truth rather than failing on translations it never had. Absent, as
on every model that predates the field, means the whole project list.
A translation that was never written is a warning: an entry of a collection
that one locale has and another lacks (Entry parity), like a document's missing
translation. A dictionary key missing from a locale (Key parity) is a warning
too. A locale with no content file (Locale file missing) is an error. Every message
names the list it was evaluated against, so the two cases read apart:
contentrain_model_save accepts locales and rejects a locale outside
config.locales.supported. contentrain_validate fix:true reports a broken
declaration but never invents one: narrowing a model's coverage is a content
decision, and the only value the tool could derive — the locales that happen to
have files today — would write the current gaps into the schema and silence the
errors that reveal them.
Structural contentrain_model_save edits preserve existing top-level form and
comments blocks. These blocks belong to the runtime provider; the structural
tool does not enable or change them. Its response lists preserved_blocks when
configuration was carried forward.
来源:packages/mcp/README.md,提交 b3e0f74
Project Status
Get full project status (read-only). Returns config, models, context. Do NOT manually edit .contentrain/ based on this output.
无需输入参数。
Describe Model
Get full schema of a single model (read-only). Do NOT manually create content files — use contentrain_content_save instead.
model必填localeinclude_sampleDescribe Format
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.
无需输入参数。
Initialize Project
Initialize .contentrain/ structure. Changes are auto-committed to git — do NOT manually create .contentrain/ files.
stackdomainslocalesScaffold Template
Template-based project setup. Available templates: blog, landing, docs, ecommerce, saas, i18n, mobile. Changes are auto-committed to git.
localestemplate必填with_sample_contentSave Model
Create or update a model definition. Changes are auto-committed to git — do NOT manually edit .contentrain/ files after calling this tool.
id必填i18n必填kind必填name必填domain必填fieldslocalesdescriptiontitle_field必填content_pathlocale_strategyDelete Model
Delete a model and its content/meta. Changes are auto-committed to git — do NOT manually edit .contentrain/ files.
model必填confirm必填Save Content
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.
model必填entries必填Delete Content
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.
idkeysslugmodel必填localeconfirm必填List Content
List content entries (read-only). Each entry carries its publish state under "meta" — status, plus publish_at/expire_at when set — read from the listed locale's meta; singletons and dictionaries carry it at the top level. Local and remote sessions return the same shape. Change status with contentrain_content_status. Returns data from .contentrain/ — do NOT manually create or modify content files.
limitmodel必填filterlocaleoffsetresolveChange Entry Status
Change the status of content entries — this is how an entry is published, unpublished or archived. Address records the way their meta is laid out: COLLECTION — "entry_ids"; DOCUMENT — "slugs"; SINGLETON and DICTIONARY — neither (one record per locale). i18n models change every supported locale unless "locale" is given. Setting status "published" validates the targeted entries first; if they have errors, nothing is committed. Records already at the requested status are reported as unchanged and not rewritten. contentrain_content_list shows each entry's current status under "meta". Changes are committed to git and follow the project's review or auto-merge workflow — do NOT manually edit .contentrain/meta files.
model必填slugslocalestatus必填entry_idsSave Vocabulary Terms
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.
terms必填Delete Vocabulary Terms
Remove canonical vocabulary terms by slug. Content already using a term is not touched — the vocabulary only advises. Changes are auto-committed to git.
terms必填confirm必填Validate Project
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. Content without meta gets meta with status published, matching what delivery already serves. A fix that would change whether an entry is delivered is listed in visibility_changes and left on its branch for review instead of auto-merged.
fixmodelSubmit Branches
Push cr/* branches to remote. MCP is push-only — PR creation is handled by the platform. Do NOT manually push or create PRs.
messagebranchesMerge Branch
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").
modelbranchlatestlocaleconfirm必填List Branches
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).
remoteunmerged_onlyDelete Branch
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.
branch必填confirm必填Reconcile Diverged Branches
Reconcile a diverged contentrain ↔ base-branch pair with a content-aware three-way merge. DRY RUN (default, dry_run:true): reports what would merge and which conflicts need a decision — touches nothing. EXECUTE (dry_run:false): performs the merge as a two-parent commit on contentrain and fast-forwards the base branch. Everything one side changed merges mechanically (entry-, key-, term+locale-level); only the same item changed differently on both sides becomes a conflict. Answer conflicts by passing resolutions (from the dry-run ids) and running again — a resolution whose values changed since the dry-run is dropped and the conflict re-reported. Recommended workflow: always run dry_run first, review the summary and conflicts, then execute.
dry_runresolutionsScan Source Code
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.
modelimitpathsoffsetexcludeincludemax_lengthmin_lengthApply Normalize
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).
mode必填scopedry_runpatchesextractionsBulk Operations
Batch operations on content entries: copy a locale, or delete many collection entries. All operations are auto-committed to git. To publish or change status, use contentrain_content_status. 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. Recommended workflow: preview with dry_run:true, review the counts, then call again to execute.
model必填confirmdry_runentry_idsoperation必填source_localetarget_localeProject Health Report
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.
usage已满足适用检查项的 100%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
pnpm add @contentrain/mcpnpx contentrain setup claude-code # or: cursor, vscode, windsurf, copilotCONTENTRAIN_PROJECT_ROOT=/path/to/project npx -y @contentrain/mcpimport { 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){ "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."}pnpm add @gitbeaker/restimport { 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/httpimport { 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 commitpnpm --filter @contentrain/mcp buildpnpm --filter @contentrain/mcp testpnpm --filter @contentrain/mcp typecheckpnpm exec oxlint packages/mcp/src packages/mcp/testsLocale 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])