Package Exports
- contentstack-mcp
- contentstack-mcp/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (contentstack-mcp) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Contentstack Content Management API MCP
This MCP provides a set of tools to interact with Contentstack's Content Management API.
Installation
NPM Package
npm install contentstack-mcp
Manual Installation
- Clone the repository:
git clone https://github.com/SamueleReply/contentstack-mcp.git
cd contentstack-mcp
- Install dependencies:
npm install
- Create a
.env
file in the root directory with your Contentstack credentials:
CONTENTSTACK_API_KEY=your_api_key
CONTENTSTACK_MANAGEMENT_TOKEN=your_management_token
CONTENTSTACK_DELIVERY_TOKEN=your_delivery_token
CONTENTSTACK_REGION=NA # Optional, defaults to NA
MCP Server Configuration
To use this package as an MCP server in Cursor, add the following configuration to your .cursor/mcp.json
file:
{
"mcpServers": {
"contentstack": {
"command": "npx",
"args": [
"contentstack-mcp@latest"
],
"env": {
"CONTENTSTACK_API_KEY": "your_api_key",
"CONTENTSTACK_MANAGEMENT_TOKEN": "your_management_token",
"CONTENTSTACK_REGION": "NA",
"CONTENTSTACK_DELIVERY_TOKEN": "your_delivery_token"
}
}
}
}
Replace the environment variables with your actual Contentstack credentials.
Available MCP Tools
When configured as an MCP server, the following tools are available in Cursor:
contentstack_get_content_types
- Get all content typescontentstack_get_content_type
- Get a specific content typecontentstack_create_content_type
- Create a new content type with custom schemacontentstack_update_content_type
- Update an existing content type schemacontentstack_field_types_reference
- Get comprehensive reference documentation for all available Contentstack field types with examplesmcp_list_tools
- Get a list of all available MCP tools with their schemascontentstack_get_entries
- Get entries for a content typecontentstack_get_entry
- Get a specific entry (supports environment and locale)contentstack_create_entry
- Create a new entry (supports environment and locale)contentstack_update_entry
- Update an entry (supports environment and locale)contentstack_delete_entry
- Delete an entry (supports environment and locale)contentstack_get_assets
- Get assetscontentstack_get_environments
- Get all environmentscontentstack_publish_entry
- Publish an entry (supports environment and locale)contentstack_unpublish_entry
- Unpublish an entry (supports environment and locale)contentstack_get_languages
- Get all languages (locales) available in the stackcontentstack_localize_entry
- Localize an entry to a specific locale
Region Support
The MCP supports multiple Contentstack regions. By default, it uses the North America (NA) region. You can specify a different region for each API call.
Available regions:
NA
- North America (default)EU
- EuropeAZURE_NA
- Azure North AmericaAZURE_EU
- Azure EuropeGCP_NA
- GCP North AmericaGCP_EU
- GCP Europe
Available Tools
Content Types
getContentTypes(config)
- Get all content typesgetContentType(uid, config)
- Get a specific content typecreateContentType(data, config)
- Create a new content typeupdateContentType(uid, data, config)
- Update an existing content type
Entries
getEntries(contentTypeUid, query, config)
- Get all entries of a content typegetEntry(contentTypeUid, entryUid, options, config)
- Get a specific entrycreateEntry(contentTypeUid, data, options, config)
- Create a new entryupdateEntry(contentTypeUid, entryUid, data, options, config)
- Update an existing entrydeleteEntry(contentTypeUid, entryUid, options, config)
- Delete an entry
Assets
getAssets(query, config)
- Get all assetsgetAsset(assetUid, config)
- Get a specific assetuploadAsset(data, config)
- Upload a new asset
Environments
getEnvironments(config)
- Get all environmentsgetEnvironment(uid, config)
- Get a specific environment
Publishing
publishEntry(data, options, config)
- Publish an entryunpublishEntry(data, options, config)
- Unpublish an entry
Languages and Localization
getLanguages(config)
- Get all languages (locales) available in the stacklocalizeEntry(contentTypeUid, entryUid, data, options, config)
- Localize an entry to a specific locale
Environment and Locale Support
Entry operations (getEntry
, createEntry
, updateEntry
, deleteEntry
) and publishing operations (publishEntry
, unpublishEntry
) now support environment and locale parameters through an options
object:
// Get an entry with specific environment and locale
const entry = await cs.getEntry('content_type_uid', 'entry_uid', {
environment: 'development',
locale: 'en-us'
});
// Create an entry with environment and locale
const newEntry = await cs.createEntry('content_type_uid', entryData, {
environment: 'development',
locale: 'en-us'
});
// Update an entry with environment and locale
const updatedEntry = await cs.updateEntry('content_type_uid', 'entry_uid', updateData, {
environment: 'development',
locale: 'en-us'
});
// Delete an entry with environment and locale
await cs.deleteEntry('content_type_uid', 'entry_uid', {
environment: 'development',
locale: 'en-us'
});
// Publish an entry with environment and locale
const publishResult = await cs.publishEntry({
entry: {
uid: 'entry_uid',
content_type: 'content_type_uid',
version: 1
},
environments: ['development']
}, {
environment: 'development',
locale: 'en-us'
});
// Unpublish an entry with environment and locale
const unpublishResult = await cs.unpublishEntry({
entry: {
uid: 'entry_uid',
content_type: 'content_type_uid'
},
environments: ['development']
}, {
environment: 'development',
locale: 'en-us'
});
The options
parameter supports:
environment
: Target environment namelocale
: Target locale code (e.g., 'en-us', 'fr-fr')- Any other query parameters supported by the Contentstack API
Field Types Reference Tool
The contentstack_field_types_reference
tool provides comprehensive documentation and examples for all available Contentstack field types. This is especially useful when creating or updating content types.
Usage
Get all field types:
// Via MCP tool
const allFieldTypes = await mcpClient.callTool('contentstack_field_types_reference', {});
// Response includes: overview, text, json, number, boolean, isodate, file, link, reference, group, blocks, global_field, extension
Get specific field type:
// Via MCP tool
const textFieldInfo = await mcpClient.callTool('contentstack_field_types_reference', {
fieldType: 'text'
});
// Returns detailed information about text fields including all variants:
// - Single Line Textbox
// - Multi Line Textbox
// - Rich Text Editor (HTML)
// - Markdown
// - Select Dropdown
Content Type Reuse Strategy ⚡
IMPORTANT: Before creating new content types or fields, always check what already exists!
Best Practices for Reusing Content Types
- Check First: Use
contentstack_get_content_types
to see existing content types - Use References: Connect content types with reference fields instead of duplicating structures
- Global Fields: Reuse common field groups (SEO, social sharing) across multiple content types
- Avoid Duplication: If you need "author" info, check if an "author" content type exists - reference it instead of creating duplicate fields
When to Use References vs Groups
Use Reference Field | Use Group Field |
---|---|
Entity needs independent management (authors, categories) | Data is tightly coupled to parent (address in contact form) |
Needs to be shared across multiple entries | Only used within this specific content type |
Has its own workflow and permissions | Simple nested data structure |
Example: Blog → Author (reference) | Example: Person → Address (group) |
Available Field Types
Field Type | Description | Use Cases | Reuse Notes |
---|---|---|---|
text |
Text content with multiple variants | Titles, descriptions, content, select dropdowns | - |
number |
Numeric values | Prices, quantities, ratings | - |
boolean |
True/false toggle | Feature flags, published status | - |
isodate |
Date and time | Publication dates, event dates | - |
file |
Media assets | Images, videos, documents | - |
link |
URL with title | External links, CTAs | - |
reference |
Entry references | Related content, categories | Use to reuse existing content types |
json |
JSON Rich Text Editor | Structured rich text content | - |
group |
Nested field groups | Address details, coordinates | Use only for tightly coupled data |
blocks |
Modular content blocks | Page sections, flexible layouts | - |
global_field |
Reusable field groups | SEO metadata, social sharing | Check if global fields exist first |
extension |
Custom field extensions | Custom widgets, integrations | - |
Key Field Properties
All fields share these common properties:
display_name
- Human-readable label (NOT "title")uid
- Unique identifier in snake_casedata_type
- Field type (NOT "type")mandatory
- Boolean for required fields (NOT "required")unique
- Boolean for unique valuesmultiple
- Boolean for multiple valuesnon_localizable
- Boolean for shared values across localesfield_metadata
- Field-specific configuration
Example: Creating a Content Type with Various Field Types
const complexContentType = await cs.createContentType({
content_type: {
title: "Blog Post",
uid: "blog_post",
schema: [
// Text - Single Line
{
data_type: "text",
display_name: "Title",
uid: "title",
mandatory: true,
unique: true
},
// Text - Multi Line
{
data_type: "text",
display_name: "Excerpt",
uid: "excerpt",
field_metadata: {
multiline: true
}
},
// JSON RTE
{
data_type: "json",
display_name: "Content",
uid: "content",
field_metadata: {
allow_json_rte: true,
rich_text_type: "advanced"
},
reference_to: ["sys_assets"]
},
// Select Dropdown
{
data_type: "text",
display_name: "Status",
uid: "status",
display_type: "dropdown",
enum: {
advanced: false,
choices: [
{ value: "draft" },
{ value: "published" }
]
}
},
// Number
{
data_type: "number",
display_name: "Read Time (minutes)",
uid: "read_time"
},
// Boolean
{
data_type: "boolean",
display_name: "Featured",
uid: "featured",
field_metadata: {
default_value: false
}
},
// Date
{
data_type: "isodate",
display_name: "Publish Date",
uid: "publish_date"
},
// File/Asset
{
data_type: "file",
display_name: "Featured Image",
uid: "featured_image"
},
// Reference
{
data_type: "reference",
display_name: "Related Posts",
uid: "related_posts",
reference_to: ["blog_post"],
field_metadata: {
ref_multiple: true
},
multiple: true
},
// Group
{
data_type: "group",
display_name: "Author Info",
uid: "author_info",
schema: [
{
data_type: "text",
display_name: "Author Name",
uid: "author_name"
},
{
data_type: "text",
display_name: "Bio",
uid: "bio",
field_metadata: {
multiline: true
}
}
]
}
]
}
});
Content Type Management
Creating Content Types
You can create content types with various field types including text, number, select fields, JSON RTE, custom asset fields, and taxonomy fields.
Basic Content Type Example
const newContentType = await cs.createContentType({
content_type: {
title: "Blog Post",
uid: "blog_post",
description: "Blog posts for the website",
schema: [
{
display_name: "Title",
uid: "title",
data_type: "text",
mandatory: true,
unique: true,
field_metadata: {
_default: true
}
},
{
display_name: "Content",
uid: "content",
data_type: "text",
field_metadata: {
multiline: true
}
},
{
display_name: "Publish Date",
uid: "publish_date",
data_type: "isodate",
mandatory: true
}
]
}
});
Content Type with Select Field (Simple Values)
const contentTypeWithSelect = await cs.createContentType({
content_type: {
title: "Product",
uid: "product",
schema: [
{
display_name: "Title",
uid: "title",
data_type: "text",
mandatory: true,
unique: true
},
{
display_name: "Priority",
uid: "priority",
data_type: "text",
field_metadata: {
description: "Product priority level"
},
enum: {
advanced: false,
choices: [
{ value: "1" },
{ value: "2" },
{ value: "3" }
]
}
}
]
}
});
Content Type with Select Field (Key-Value Pairs)
const contentTypeWithAdvancedSelect = await cs.createContentType({
content_type: {
title: "Location",
uid: "location",
schema: [
{
display_name: "Title",
uid: "title",
data_type: "text",
mandatory: true,
unique: true
},
{
display_name: "Region",
uid: "region",
data_type: "text",
enum: {
advanced: true,
choices: [
{ key: "New York", value: "NY" },
{ key: "India", value: "IN" },
{ key: "Australia", value: "AUS" }
]
}
}
]
}
});
Content Type with JSON RTE (Rich Text Editor)
const contentTypeWithRTE = await cs.createContentType({
content_type: {
title: "Article",
uid: "article",
schema: [
{
display_name: "Title",
uid: "title",
data_type: "text",
mandatory: true,
unique: true
},
{
data_type: "json",
display_name: "JSON RTE Content",
uid: "json_rte_content",
field_metadata: {
allow_json_rte: true,
rich_text_type: "advanced",
description: "Rich text content with embedded entries",
default_value: ""
},
reference_to: [
"blog_post",
"product"
],
non_localizable: false,
multiple: false,
mandatory: false,
unique: false
}
]
}
});
Content Type with Custom Asset Field
const contentTypeWithAsset = await cs.createContentType({
content_type: {
title: "Media Gallery",
uid: "media_gallery",
schema: [
{
display_name: "Title",
uid: "title",
data_type: "text",
mandatory: true,
unique: true
},
{
display_name: "Gallery Images",
uid: "gallery_images",
data_type: "file",
multiple: true,
mandatory: false
}
]
}
});
Content Type with Taxonomy Fields
const contentTypeWithTaxonomy = await cs.createContentType({
content_type: {
title: "Categorized Content",
uid: "categorized_content",
schema: [
{
display_name: "Title",
uid: "title",
data_type: "text",
mandatory: true,
unique: true
},
{
uid: "taxonomies",
taxonomies: [
{
taxonomy_uid: "taxonomy_1",
max_terms: 5,
mandatory: true,
non_localizable: false
},
{
taxonomy_uid: "taxonomy_2",
max_terms: 10,
mandatory: false,
non_localizable: false
}
],
multiple: true
}
]
}
});
Content Type with Field Visibility Rules
const contentTypeWithRules = await cs.createContentType({
content_type: {
title: "Conditional Form",
uid: "conditional_form",
schema: [
{
display_name: "Title",
uid: "title",
data_type: "text",
mandatory: true,
unique: true
},
{
display_name: "Show Details",
uid: "show_details",
data_type: "boolean",
mandatory: false
},
{
display_name: "Details",
uid: "details",
data_type: "text",
mandatory: false
}
],
field_rules: [
{
conditions: [
{
operand_field: "show_details",
operator: "equals",
value: true
}
],
match_type: "all",
actions: [
{
action: "show",
target_field: "details"
}
]
}
]
}
});
Updating Content Types
To update a content type, you must provide the complete schema including all existing fields plus any new fields:
// First, get the existing content type
const existingContentType = await cs.getContentType('blog_post');
// Modify the schema (add a new field, for example)
existingContentType.content_type.schema.push({
display_name: "Author",
uid: "author",
data_type: "text",
mandatory: false
});
// Update the content type
const updatedContentType = await cs.updateContentType('blog_post', {
content_type: existingContentType.content_type
});
Field Visibility Rule Operators by Data Type
When creating field visibility rules, use these operators based on the operand field's data type:
- Text:
matches
,does_not_match
,starts_with
,ends_with
,contains
- Number:
equals
,not_equals
,less_than
,greater_than
,less_than_or_equals
,greater_than_or_equals
- Date:
equals
,not_equals
,before_date
,after_date
(use ISO format) - Boolean:
is
,is_not
- Select:
is
,is_not
- Reference:
is
,is_not
Usage Example
As a Node.js Library
const contentstack = require('contentstack-cursor-mcp');
// Initialize with default configuration from .env
const cs = contentstack.initialize();
// Or initialize with custom configuration
const csCustom = contentstack.initialize({
region: 'EU',
apiKey: 'your_api_key',
managementToken: 'your_management_token',
deliveryToken: 'your_delivery_token'
});
// Get all content types
async function example() {
try {
const contentTypes = await cs.getContentTypes();
console.log(contentTypes);
} catch (error) {
console.error(error);
}
}
// Get entries with query parameters
async function getEntriesExample() {
try {
const entries = await cs.getEntries('content_type_uid', {
limit: 10,
skip: 0,
environment: 'production'
});
console.log(entries);
} catch (error) {
console.error(error);
}
}
// Get entry with environment and locale
async function getEntryWithOptions() {
try {
const entry = await cs.getEntry('content_type_uid', 'entry_uid', {
environment: 'development',
locale: 'en-us',
include_schema: true
});
console.log(entry);
} catch (error) {
console.error(error);
}
}
// Get all available languages/locales
async function getLanguagesExample() {
try {
const languages = await cs.getLanguages();
console.log('Available languages:', languages);
} catch (error) {
console.error(error);
}
}
// Localize an entry to a specific locale
async function localizeEntryExample() {
try {
const localizedEntry = await cs.localizeEntry('content_type_uid', 'entry_uid', {
entry: {
title: 'Titre localisé',
description: 'Description en français'
}
}, {
locale: 'fr-fr'
});
console.log('Localized entry:', localizedEntry);
} catch (error) {
console.error(error);
}
}
As an MCP Server in Cursor
Once configured in your .cursor/mcp.json
, you can use the Contentstack tools directly in Cursor by asking questions like:
- "Get all content types from Contentstack"
- "Show me entries for the 'blog_post' content type"
- "Get the entry with UID 'xyz123' from content type 'product' in the development environment"
- "Create a new blog post entry with title 'My New Post'"
- "Create a new content type called 'Product' with fields for title, description, price, and category"
- "Update the 'blog_post' content type to add an 'author' field"
- "Add field visibility rules to the 'contact_form' content type"
- "Create a content type with a select field for product categories"
- "Get all available languages in the stack"
- "Localize the entry 'xyz123' from content type 'blog_post' to French locale"
Configuration
Each API call accepts an optional configuration object with the following properties:
region
: Contentstack region (NA, EU, AZURE_NA, AZURE_EU, GCP_NA, GCP_EU)apiKey
: Contentstack API KeymanagementToken
: Contentstack Management TokendeliveryToken
: Contentstack Delivery Token
Error Handling
All API calls are wrapped in try-catch blocks and will throw errors with meaningful messages if something goes wrong. The error message will include the specific error message from the Contentstack API if available.
Testing
Run the test suite to verify your configuration:
npm test
This will test various API endpoints and verify that your credentials and configuration are working correctly.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.