The Contentful MCP Server is a Model Context Protocol server that exposes Contentful's Content Management API (CMA) as structured tools for AI assistants. It enables LLMs to read, create, update, and manage content in Contentful spaces through a standardized protocol. The project is structured as an Nx monorepo with a thin server shell (mcp-server) and a shared tools library (mcp-tools).
graph TD
AI[AI Clients<br/>Claude, Cursor, VS Code Copilot, Codex] -->|stdio / MCP protocol| Server[@contentful/mcp-server]
Server --> Tools[@contentful/mcp-tools]
Tools -->|CMA REST API| CMA[Contentful Management API<br/>api.contentful.com]
Tools -->|contentful-export/import| Migration[Space-to-Space Migration]
RemoteMCP[Remote MCP Server<br/>cloud-hosted variant] --> Tools
| Package | Purpose |
|---|---|
packages/mcp-server |
Thin server shell — connects MCP SDK transport (stdio) to tool registrations. Published as @contentful/mcp-server on npm. Entry point: src/index.ts |
packages/mcp-tools |
All tool implementations, Zod schemas, CMA client utilities, and domain logic. Published as @contentful/mcp-tools on GitHub Packages. Consumed by both local and remote MCP servers |
| Directory | Purpose |
|---|---|
src/tools/entries/ |
CRUD + publish/unpublish/archive for content entries |
src/tools/content-types/ |
Content type management (list, get, create, update, publish) |
src/tools/assets/ |
Asset upload, metadata management, publish/unpublish |
src/tools/ai-actions/ |
AI Action creation, invocation, and lifecycle management |
src/tools/taxonomies/ |
Concept and concept scheme management (taxonomy system) |
src/tools/jobs/space-to-space-migration/ |
Multi-step space migration workflow |
src/tools/context/ |
Initial context tool + MCP instructions prompt |
src/tools/environments/ |
Environment CRUD |
src/tools/locales/ |
Locale management |
src/tools/spaces/ |
Space listing and details |
src/tools/orgs/ |
Organization listing and details |
src/tools/tags/ |
Tag management |
src/tools/releases/ |
Release CRUD, publish/unpublish/validate, scheduling, ReleaseAction reads |
src/tools/editor-interfaces/ |
Editor interface configuration |
src/config/ |
ContentfulConfig type definition |
src/types/ |
Zod schemas for fields, entries, rich text, taxonomies |
src/utils/ |
CMA client factory, response formatting, bulk operations, summarizer |
- AI client connects to
mcp-serverover stdio transport - Server registers all tools from
mcp-toolswith the MCP SDK - Client calls
get_initial_context→ returns space info, available content types, and agent instructions - Client invokes tools (e.g.,
search_entries,create_entry) → tool functions:- Validate input via Zod schemas
- Create a
contentful-managementclient with the configured access token - Call the Contentful CMA REST API
- Format and return the response
- Observability: All API calls include
X-Contentful-User-Agent-Tool: contentful-mcp/<version>header for tracking MCP-originated traffic
The migration workflow uses a multi-tool state machine:
space_to_space_migration_handler(enableWorkflow=true) → enables the workflow toolsspace_to_space_param_collection→ collects source/target space details from userexport_space→ usescontentful-exportto export source spaceimport_space→ usescontentful-importto import into target spacespace_to_space_migration_handler(enableWorkflow=false) → disables workflow tools
Migration tools are disabled by default and only enabled when the workflow is active.
- Space: Top-level container for content. Each MCP session targets one space (via
SPACE_IDenv var) but can list/switch spaces - Environment: Isolated content branch within a space (default:
master). All content operations target a specific environment - Content Type: Schema definition for entries — defines fields, validations, and appearance
- Entry: A content record conforming to a content type. Entries have lifecycle states: draft → published → archived
- Asset: Media file (image, document, etc.) with metadata. Uploaded via URL or base64 data URI
- AI Action: Custom AI-powered workflow definitions that can be invoked with variables
- Taxonomy: Hierarchical concept organization via Concept Schemes and Concepts
| Dependency | Why it's here |
|---|---|
@modelcontextprotocol/sdk |
MCP protocol implementation — server, transport, tool registration |
contentful-management |
Official CMA JavaScript client — all API calls to Contentful |
contentful-export / contentful-import |
Space migration utilities (lazy-loaded) |
zod |
Runtime schema validation for all tool inputs |
fast-xml-parser |
XML parsing for asset upload responses |
nx |
Monorepo orchestration — build caching, task dependencies, release management |
tsup |
TypeScript bundler — produces ESM output for both packages |
vitest |
Unit test framework |
| Variable | Purpose | Default |
|---|---|---|
CONTENTFUL_MANAGEMENT_ACCESS_TOKEN |
CMA personal access token (required) | — |
SPACE_ID |
Target Contentful space | — |
ENVIRONMENT_ID |
Target environment within the space | master |
CONTENTFUL_HOST |
CMA API hostname | api.contentful.com |
ORGANIZATION_ID |
Org ID (for org-level operations) | — |
APP_ID |
Contentful App ID (for app context) | — |
NODE_ENV |
Runtime mode (development enables logging) |
production |
Note: Some operational details have been generalized for this public repository. Internal team members: see internal runbook for full operational procedures.
- Distribution: Published to npm as
@contentful/mcp-servervia Nx Release on merge tomain - Versioning: Automated via Conventional Commits —
feat:triggers minor bump,fix:triggers patch - DXT file: A
.dxtpackage is built and attached to GitHub Releases for Claude Desktop installation - Release automation: Uses GitHub Actions with Vault-based secret retrieval for npm/GitHub Package tokens
- Invalid/expired CMA token: Server starts but all tool calls fail with 401. User must regenerate token
- Rate limiting: Heavy usage (especially bulk operations) can trigger CMA rate limits. Tools return the CMA error response directly
- Space/environment mismatch: If configured space or environment doesn't exist, tools fail on first call after
get_initial_context
[NEEDS TEAM INPUT]
[NEEDS TEAM INPUT]