@mybe/contensa-mcp
v1.2.1
Published
MCP (Model Context Protocol) server for Contensa CMS - enables AI assistants to interact with your CMS
Downloads
2,011
Maintainers
Readme
Contensa MCP Server
MCP (Model Context Protocol) server for Contensa CMS. This enables AI assistants like Claude (via Cursor, Claude Desktop, etc.) to interact directly with your CMS.
Features
- Content Type Management: Create, read, update, delete content types
- Field Management: Add, modify, remove fields from content types
- Content Entry Management: Create, read, update, delete, publish content entries
- Locale Management: Create and manage multilingual content variants with AI translation
- Environment Management: Create, manage, and merge/sync environments
- Media Management: List and manage media assets
- AI-Powered Features: Generate schemas and field suggestions using AI
Installation
Install globally to use with MCP clients like Cursor or Claude Desktop:
npm install -g @mybe/contensa-mcpOr use directly with npx (no installation needed):
npx @mybe/contensa-mcpFor development (if using the monorepo):
cd packages/mcp-server
npm install
npm run buildConfiguration
For Cursor
Add to your Cursor MCP settings (~/.cursor/mcp.json or workspace .cursor/mcp.json):
{
"mcpServers": {
"contensa": {
"command": "contensa-mcp",
"env": {
"CONTENSA_API_KEY": "your-api-key-here",
"CONTENSA_MCP_TOKEN": "mcp_live_a3f8c2d1...",
"CONTENSA_USER_ID": "your-user-id-here",
"CONTENSA_PROJECT_ID": "your-default-project-id"
}
}
}
}Alternative (if not installed globally, use npx):
{
"mcpServers": {
"contensa": {
"command": "npx",
"args": ["@mybe/contensa-mcp"],
"env": {
"CONTENSA_API_KEY": "your-api-key-here",
"CONTENSA_MCP_TOKEN": "mcp_live_a3f8c2d1...",
"CONTENSA_USER_ID": "your-user-id-here",
"CONTENSA_PROJECT_ID": "your-default-project-id"
}
}
}
}Or if running from the monorepo:
{
"mcpServers": {
"contensa": {
"command": "node",
"args": ["/path/to/mybe-cms/packages/mcp-server/dist/cli.js"],
"env": {
"CONTENSA_API_KEY": "your-api-key-here",
"CONTENSA_MCP_TOKEN": "mcp_live_a3f8c2d1...",
"CONTENSA_USER_ID": "your-user-id-here",
"CONTENSA_PROJECT_ID": "your-default-project-id"
}
}
}
}For Claude Desktop
Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"contensa": {
"command": "contensa-mcp",
"env": {
"CONTENSA_API_KEY": "your-api-key-here",
"CONTENSA_MCP_TOKEN": "mcp_live_a3f8c2d1...",
"CONTENSA_USER_ID": "your-user-id-here",
"CONTENSA_PROJECT_ID": "your-default-project-id"
}
}
}
}Alternative (if not installed globally, use npx):
{
"mcpServers": {
"contensa": {
"command": "npx",
"args": ["@mybe/contensa-mcp"],
"env": {
"CONTENSA_API_KEY": "your-api-key-here",
"CONTENSA_MCP_TOKEN": "mcp_live_a3f8c2d1...",
"CONTENSA_USER_ID": "your-user-id-here",
"CONTENSA_PROJECT_ID": "your-default-project-id"
}
}
}
}Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| CONTENSA_API_KEY | Yes | Your Contensa API key (project-level authentication) |
| CONTENSA_MCP_TOKEN | Yes | Your Contensa MCP token (per-user authentication with scopes) |
| CONTENSA_USER_ID | Yes | Your user ID (required for creating/updating content) |
| CONTENSA_PROJECT_ID | No | Default project ID |
| CONTENSA_BASE_URL | No | Custom API base URL (defaults to Contensa production API or localhost in dev) |
MCP Token and API Key - Why Both Are Required
The MCP server requires both CONTENSA_MCP_TOKEN and CONTENSA_API_KEY to function properly. Here's why:
MCP Token (X-MCP-Token header):
- Per-user, per-project authentication
- Carries fine-grained scopes (e.g.,
content:read,content:write) - Provides user context for operations
- Can expire and be revoked individually
- Tracks last-used timestamp for audit purposes
API Key (X-API-Key header):
- Project-level authentication
- Provides full project access
- Required for certain backend operations
- No expiration
How They Work Together:
The MCP server sends both headers in every request. The backend API uses a dual authentication system (dualAuth middleware) with the following priority:
Authorization: Bearer <session>— Dashboard users (highest priority)X-MCP-Token: mcp_live_...— MCP agents (per-user with scopes)X-API-Key: ck_...— Project-level API keys (fallback)
This dual-token approach ensures:
- ✅ User-level permissions and scoping via MCP token
- ✅ Project-level access validation via API key
- ✅ Audit trail of which user performed which operations
- ✅ Fine-grained access control based on token scopes
Getting Your Tokens:
MCP Token:
- Log in to your Contensa dashboard
- Go to Settings → MCP Tokens
- Create a new MCP token with appropriate scopes (e.g.,
content:read,content:write) - Copy the token (shown only once) - format:
mcp_live_<64 hex characters>
API Key:
- Log in to your Contensa dashboard
- Go to Settings → API Keys
- Generate a new API key for your project
- Copy the key - format:
ck_<...>
API Base URL
The MCP server automatically determines the API base URL:
- Development (
NODE_ENV=development): Useshttp://localhost:3001/api/v1 - Production: Uses Contensa's production API endpoint
You can override the default by setting CONTENSA_BASE_URL for custom API endpoints or different environments:
{
"mcpServers": {
"contensa": {
"command": "node",
"args": ["./packages/mcp-server/dist/cli.js"],
"env": {
"CONTENSA_API_KEY": "your-api-key",
"CONTENSA_MCP_TOKEN": "mcp_live_a3f8c2d1...",
"CONTENSA_USER_ID": "your-user-id",
"CONTENSA_BASE_URL": "https://your-custom-api.com/api/v1"
}
}
}
}Available Tools
Project Management
| Tool | Description |
|------|-------------|
| contensa_list_projects | List all projects |
| contensa_get_project | Get project details |
| contensa_set_active_project | Set the active project |
Content Type Management
| Tool | Description |
|------|-------------|
| contensa_list_content_types | List all content types in a project |
| contensa_get_content_type | Get content type details with fields |
| contensa_create_content_type | Create a new content type |
| contensa_update_content_type | Update a content type |
| contensa_delete_content_type | Delete a content type |
Field Management
| Tool | Description |
|------|-------------|
| contensa_list_fields | List fields of a content type |
| contensa_create_field | Add a field to a content type |
| contensa_update_field | Update a field |
| contensa_delete_field | Delete a field |
Content Entry Management
| Tool | Description |
|------|-------------|
| contensa_list_content_entries | List entries of a content type |
| contensa_get_content_entry | Get a content entry |
| contensa_create_content_entry | Create a new entry |
| contensa_update_content_entry | Update an entry |
| contensa_delete_content_entry | Delete an entry |
| contensa_publish_content_entry | Publish an entry |
| contensa_unpublish_content_entry | Unpublish an entry |
Locale Management
| Tool | Description |
|------|-------------|
| contensa_get_supported_locales | Get all supported locales in the system |
| contensa_list_locale_variants | List all locale variants of an entry |
| contensa_create_locale_variant | Create a new locale variant with optional AI translation |
Media Management
| Tool | Description |
|------|-------------|
| contensa_list_media_assets | List media assets |
| contensa_get_media_asset | Get media asset details |
| contensa_delete_media_asset | Delete a media asset |
Environment Management
| Tool | Description |
|------|-------------|
| contensa_list_environments | List all environments for a project |
| contensa_get_environment | Get environment details |
| contensa_create_environment | Create a new environment (empty or cloned) |
| contensa_update_environment | Update environment details |
| contensa_delete_environment | Delete an environment |
| contensa_set_active_environment | Set the active environment for content operations |
| contensa_merge_environment | Merge content from one environment to another |
| contensa_sync_environment | Sync content between environments (in development) |
AI Features
| Tool | Description |
|------|-------------|
| contensa_suggest_fields | AI-powered field suggestions |
| contensa_generate_schema | AI-powered schema generation |
Usage Examples
Once configured, you can interact with your CMS through natural language:
Example 1: Create a Blog Content Type
"Create a new content type called 'Blog Post' with fields for title, content, author, and published date"The AI will use:
contensa_create_content_typeto create "Blog Post"contensa_create_fieldmultiple times to add each field
Example 2: Create Content
"Create a new blog post titled 'Hello World' with content 'This is my first post'"The AI will use:
contensa_list_content_typesto find the Blog Post typecontensa_list_fieldsto get the field API namescontensa_create_content_entryto create the entry with proper field names
Example 3: Publish Content
"Publish all draft blog posts"The AI will use:
contensa_list_content_entrieswith status filtercontensa_publish_content_entryfor each draft
Example 4: AI Schema Generation
"Generate a schema for an e-commerce product catalog"The AI will use:
contensa_generate_schemato get AI suggestionscontensa_create_content_typeto create the typecontensa_create_fieldto add each suggested field
Example 5: Environment Merging
"Merge the dev environment to master"The AI will use:
contensa_list_environmentsto find the dev and master environment IDscontensa_merge_environmentwith dryRun=true to preview conflictscontensa_merge_environmentwith dryRun=false to perform the merge
Example 6: Preview Merge Conflicts
"Show me what conflicts would occur if I merge staging to production"The AI will use:
contensa_list_environmentsto find environment IDscontensa_merge_environmentwith dryRun=true to preview conflicts without making changes
Example 7: Create Multilingual Content
"Create a French version of the blog post with ID 'post-123' and translate the content using AI"The AI will use:
contensa_get_content_entryto get the original entrycontensa_create_locale_variantwith translateContent=true to create and translate
Example 8: Create Locale Variants for Referenced Content
"Create a Spanish version of the product 'prod-456' and also create Spanish versions of all referenced entries"The AI will use:
contensa_create_locale_variantwith translateReferences=true to recursively create variants
Creating Content Entries
Creating content entries requires understanding the correct payload format and field structure.
Required Configuration
Before creating content entries, ensure you have:
- API Key: Set in
CONTENSA_API_KEYenvironment variable - User ID: Set in
CONTENSA_USER_IDenvironment variable (required for all write operations) - Project ID: Either set as active project or provided in each request
Example configuration:
{
"mcpServers": {
"contensa": {
"command": "npx",
"args": ["@mybe/contensa-mcp"],
"env": {
"CONTENSA_API_KEY": "your-api-key-here",
"CONTENSA_USER_ID": "your-user-id-here",
"CONTENSA_PROJECT_ID": "your-default-project-id"
}
}
}
}Payload Format
The contensa_create_content_entry tool requires:
| Parameter | Required | Description |
|-----------|----------|-------------|
| contentTypeId | Yes | The ID of the content type |
| data | Yes | Object with field values (keys must be field API names) |
| projectId | No* | Project ID (uses active project if not provided) |
| userId | No* | User ID (uses config userId if not provided) |
| status | No | Entry status: "draft" (default) or "published" |
| locale | No | Locale code (default: "en-US") |
*Required but can be set via configuration or active project/user
Field API Names
CRITICAL: The data object keys must use field API names, NOT:
- ❌ Field IDs (UUIDs)
- ❌ Display names (e.g., "Title", "Description")
- ✅ API names (e.g., "title", "description", "author_name")
Getting Field API Names
Always use contensa_list_fields to get the correct API names:
// Example response from contensa_list_fields
{
"fields": [
{
"id": "field-uuid-123",
"api_name": "title", // ← Use this as the key
"kind": "short-text",
"is_required": true
},
{
"id": "field-uuid-456",
"api_name": "content", // ← Use this as the key
"kind": "long-text",
"is_required": true
},
{
"id": "field-uuid-789",
"api_name": "author_name", // ← Use this as the key
"kind": "short-text",
"is_required": false
}
]
}Example: Creating a Blog Post
Step 1: Get the content type and fields
"List all fields for the Blog Post content type"Response shows:
title(short-text, required)content(long-text, required)author_name(short-text, optional)published_date(date, optional)
Step 2: Create the entry with correct field API names
{
"contentTypeId": "blog-post-id",
"data": {
"title": "Hello World", // ✅ Correct: using API name
"content": "This is my first post", // ✅ Correct: using API name
"author_name": "John Doe" // ✅ Correct: using API name
},
"status": "draft"
}Common Errors and Solutions
Error: "Missing required fields"
Cause: Required fields are not provided in the data object, or keys don't match field API names.
Solution:
- Use
contensa_list_fieldsto get all required fields - Ensure all required fields are in the
dataobject - Verify keys match the
api_nameproperty exactly
Example error response:
{
"message": "Missing required fields",
"missingFields": ["title", "content"]
}Error: "Project ID is required"
Cause: No active project set and no projectId provided.
Solution:
- Set active project:
contensa_set_active_project - Or provide
projectIdin the request - Or set
CONTENSA_PROJECT_IDin configuration
Error: "User ID is required"
Cause: No CONTENSA_USER_ID in configuration and no userId provided.
Solution:
- Add
CONTENSA_USER_IDto your MCP configuration - Or provide
userIdin each request
Error: "Invalid data format"
Cause: The data parameter is not an object or is a stringified JSON.
Solution:
Ensure data is a proper object:
// ✅ Correct
{ "data": { "title": "Hello" } }
// ❌ Wrong
{ "data": "{\"title\": \"Hello\"}" }Handling Optional Fields
IMPORTANT: When creating content entries, handle optional fields correctly to avoid frontend errors:
- ✅ Correct: Omit optional fields entirely from the
dataobject if you don't have values - ❌ Wrong: Pass empty objects
{}for optional fields (causes "Failed to construct 'URL'" errors) - ❌ Wrong: Pass
nullorundefinedfor optional fields
Example - Correct approach:
// User says "I'll add the image later"
{
"contentTypeId": "blog-post-id",
"data": {
"title": "Hello World",
"content": "This is my first post"
// ✅ image field is omitted entirely
}
}Example - Wrong approach:
// ❌ This causes frontend errors!
{
"contentTypeId": "blog-post-id",
"data": {
"title": "Hello World",
"content": "This is my first post",
"image": {} // ❌ Empty object causes "Invalid URL" errors
}
}Why this matters: Empty objects for media fields cause the frontend to attempt constructing URLs from invalid data, resulting in errors. The MCP server now automatically removes empty objects, null, and undefined values before sending data to the API.
Field Value Types
Different field types expect different value formats:
| Field Type | Value Format | Example | Notes |
|------------|--------------|---------|-------|
| short-text | String | "Hello World" | Omit if optional and no value |
| long-text | String | "Long content..." | Omit if optional and no value |
| number | Number | 42 | Omit if optional and no value |
| date | ISO 8601 string | "2024-01-15T10:30:00Z" | Omit if optional and no value |
| boolean | Boolean | true or false | Omit if optional and no value |
| list | Array of strings | ["tag1", "tag2"] | Empty arrays [] are valid |
| media | Media asset ID (string) | "media-uuid-123" | Never use {} - omit if no value |
| reference | Entry ID (string) | "entry-uuid-456" | Omit if optional and no value |
| references-many | Array of entry IDs | ["entry-1", "entry-2"] | Empty arrays [] are valid |
Complete Example Workflow
User: "Create a blog post about AI with title 'The Future of AI' and content 'AI is transforming...'"
AI Assistant:
1. Lists content types to find "Blog Post" → gets contentTypeId
2. Lists fields for Blog Post → gets field API names and requirements
3. Creates entry:
{
"contentTypeId": "blog-post-id",
"data": {
"title": "The Future of AI",
"content": "AI is transforming..."
},
"status": "draft"
}
4. Returns success with entry IDBest Practices
- Always get field API names first: Use
contensa_list_fieldsbefore creating entries - Validate required fields: Check which fields are required before submitting
- Use correct data types: Match the field type's expected value format
- Set active project: Use
contensa_set_active_projectto avoid repeating projectId - Handle errors gracefully: Check error messages for missing fields and retry with corrections
Field Types
Available field types for contensa_create_field:
| Type | Description |
|------|-------------|
| short-text | Single line text |
| long-text | Multi-line text / rich content |
| number | Numeric values |
| date | Date/datetime values |
| boolean | True/false values |
| media | Images and files |
| list | Array of values |
| reference | Link to another content entry |
| references-many | Multiple links to content entries |
Environment Merging
The MCP server supports merging content between environments (e.g., dev → staging → master).
Merge Strategies
When merging environments, you can specify how to handle conflicts:
| Strategy | Description |
|----------|-------------|
| skip | Skip conflicting items (default) |
| replace | Replace target with source content |
| merge | Attempt to merge changes |
| create-new | Create new entries for conflicts |
Dry Run Mode
Always preview changes before merging to production:
"Do a dry run merge from dev to master"This will show you:
- Number of content types to be merged
- Number of entries to be merged
- Any conflicts that would occur
- Errors that would prevent the merge
Master Environment Protection
By default, merging into the master environment requires explicit approval (requireMasterApproval: false). This prevents accidental overwrites of production content.
Merge Workflow Example
Preview the merge:
"Show me what would happen if I merge dev to master"Review conflicts and decide on resolution strategies
Execute the merge:
"Merge dev to master, replacing conflicts and disabling master approval"
Sync vs Merge
- Merge (
contensa_merge_environment): Fully functional, supports dry-run, conflict resolution strategies - Sync (
contensa_sync_environment): Currently in development, will support three-way merge logic
Locale Management
The MCP server provides comprehensive support for creating and managing multilingual content.
Supported Locales
The system supports 31 locales across multiple regions:
Default: en-US (English - United States)
Europe: fr-FR, es-ES, de-DE, it-IT, pt-PT, nl-NL, sv-SE, pl-PL, ru-RU, en-GB
Americas: pt-BR, es-MX, es-AR, en-CA, fr-CA
Asia-Pacific: ja-JP, ko-KR, zh-CN, zh-TW, hi-IN, th-TH, vi-VN, id-ID, ms-MY, fil-PH, bn-BD, en-AU
Middle East & Africa: ar-SA, tr-TR, he-IL
Use contensa_get_supported_locales to get the full list with locale codes, names, and flags.
Creating Locale Variants
Locale variants allow you to create multilingual versions of your content. Each variant:
- Shares the same content structure (fields) as the original
- Has its own data in the target language
- Can optionally be AI-translated from the source locale
- Can reference the same entries or have locale-specific references
Translation Options
When creating a locale variant, you have two key options:
1. translateContent (default: true)
- true: Uses AI to translate text fields (short-text, long-text, list) to the target locale
- false: Copies content as-is without translation
Note: Reference fields are never translated - they maintain the same entry IDs across locales.
2. translateReferences (default: false)
- true: Recursively creates locale variants for all referenced entries
- false: Only creates a variant for the main entry
Warning: Setting translateReferences=true can create many entries if you have deeply nested references. Use with caution.
How Translation Works
The locale system uses AI-powered translation with the following behavior:
- Translatable Fields: Only text-based fields are translated (short-text, long-text, number, list)
- Reference Fields: Reference and references-many fields are copied as-is (same entry IDs)
- Retry Logic: Translation attempts up to 2 retries on failure
- Fallback: If translation fails after retries, content is copied without translation
- Metadata: Translated entries are marked with
is_translated: truein metadata
Edge Cases and Behavior
Existing Variants
If a locale variant already exists for an entry, the API returns a 409 Conflict error. You cannot create duplicate locale variants.
Circular References
The system prevents infinite loops from circular references by tracking visited entry IDs during recursive locale creation.
Partial Translation
If translation fails for some fields but succeeds for others, the variant is still created with whatever translation succeeded.
Referenced Entry Variants
When translateReferences=true:
- The system checks if a locale variant already exists for each referenced entry
- If it exists, it uses the existing variant
- If not, it creates a new variant (with or without translation based on settings)
- All created entries are returned in the response
Locale Workflow Example
Check supported locales:
"What locales are supported?"Create a simple locale variant:
"Create a French version of blog post 'post-123' with AI translation"Create variant without translation:
"Create a German version of product 'prod-456' but don't translate the content"Create variant with referenced entries:
"Create a Spanish version of the homepage entry and all its referenced content"List all variants:
"Show me all locale variants of entry 'post-123'"
Locale Validation
Locale codes must match the BCP-47 format (language-COUNTRY):
- ✅ Valid:
en-US,fr-FR,zh-CN - ❌ Invalid:
en,french,EN-us
The client automatically validates locale codes against the supported locales list.
Response Format
When creating a locale variant, the response includes:
variant: The created locale variant entrycreatedEntries: Array of all entries created (iftranslateReferences=true)totalCreated: Total number of entries created (including the main variant)message: Success message
Example response:
{
"variant": { "id": "new-variant-id", "locale": "fr-FR", ... },
"createdEntries": [
{ "id": "ref-1-fr", "title": "Referenced Entry 1" },
{ "id": "ref-2-fr", "title": "Referenced Entry 2" }
],
"totalCreated": 3,
"message": "Locale variant created successfully with references"
}Getting Your Credentials
MCP Token (Required)
Your MCP token provides per-user authentication with fine-grained scopes:
- Log in to your Contensa dashboard
- Go to Settings → MCP Tokens
- Click "Create New MCP Token"
- Set a name (e.g., "Claude Desktop", "Cursor")
- Select scopes based on your needs:
- Read-only AI assistant:
content:read,schema:read,media:read - Content editor:
content:read,content:write,schema:read - Full access: All scopes (use with caution)
- Read-only AI assistant:
- Set an expiration date (optional but recommended)
- Click "Create Token"
- Copy the token immediately - it will only be shown once
- Add it to your MCP configuration as
CONTENSA_MCP_TOKEN
Token Format: mcp_live_<64 hex characters>
Security Note: MCP tokens can be revoked individually without affecting other tokens or API keys. They also track usage for audit purposes.
API Key (Required)
Your API key provides project-level authentication:
- Log in to your Contensa dashboard
- Go to Settings → API Keys
- Generate a new API key for your project
- Copy the key and add it to your MCP configuration as
CONTENSA_API_KEY
Key Format: ck_<...>
User ID (Required)
Your User ID is required for creating and updating content. To find it:
- Log in to your Contensa dashboard
- Go to Settings → Profile or Account Settings
- Your User ID will be displayed in your profile information
- Copy the User ID and add it to your MCP configuration as
CONTENSA_USER_ID
Note: The User ID is a UUID that uniquely identifies your account. It's required for all write operations (creating/updating content, content types, fields, etc.).
Development
# Install dependencies
npm install
# Build
npm run build
# Run in development
npm run dev
# Start the server
npm startTroubleshooting
"API key is required" error
Make sure CONTENSA_API_KEY is set in your MCP configuration.
Tools not showing up
- Restart Cursor/Claude Desktop after updating the config
- Check the MCP server logs for errors
- Verify the path to the CLI is correct
Permission errors
Ensure your API key has the necessary permissions for the operations you're trying to perform.
License
MIT
