The dotCMS MCP (Model Context Protocol) Server enables AI assistants to interact directly with dotCMS content management capabilities. Built with xmcp, this server provides sandbox-isolated code execution for exploring the dotCMS REST API specification and executing authenticated API calls.
For Production Use:
For Testing & Development:
For Local Development:
[!WARNING] This MCP server requires an API token with write permissions for Content Types, Content, and Workflows. Only use tokens with the minimum required permissions and secure them properly.
This integration requires an API Token with content management permissions:
For detailed instructions, please refer to the dotCMS API Documentation.
Before setting up the MCP server, you need these environment variables to connect to your dotCMS instance:
| Variable | Required | Description | Example |
|---|---|---|---|
DOTCMS_URL | ✅ | Your dotCMS instance URL | https://demo.dotcms.com |
AUTH_TOKEN | ✅ | API authentication token (created in setup step) | your-api-token-here |
SANDBOX_TIMEOUT | ❌ | Sandbox execution timeout in ms (default: 15000) | 15000 |
DEBUG | ❌ | When set to any truthy value, emits diagnostic logs to stderr (e.g. context cache load events) | 1 |
Get up and running with the dotCMS MCP Server in minutes.
The server runs on both Node.js (≥20) and Bun — the correct sandbox implementation is selected automatically at runtime.
[!NOTE] This version is currently in beta. Once stable, replace
@dotcms/mcp-server@betawith@dotcms/mcp-serverin the examples below.
Add the MCP server to your Claude Desktop configuration file. The configuration file location varies by operating system:
~/Library/Application Support/Claude/claude_desktop_config.json%APPDATA%\Claude\claude_desktop_config.jsonUsing npx (Node.js):
{
"mcpServers": {
"dotcms": {
"command": "npx",
"args": ["-y", "@dotcms/mcp-server@beta"],
"env": {
"DOTCMS_URL": "https://your-dotcms-instance.com",
"AUTH_TOKEN": "your-api-token"
}
}
}
}Using bunx (Bun):
{
"mcpServers": {
"dotcms": {
"command": "bunx",
"args": ["@dotcms/mcp-server@beta"],
"env": {
"DOTCMS_URL": "https://your-dotcms-instance.com",
"AUTH_TOKEN": "your-api-token"
}
}
}
}Add the MCP server to your Cursor configuration. Open Cursor Settings and navigate to "Features" > "Model Context Protocol" or create/edit the configuration file:
Using npx (Node.js):
{
"mcpServers": {
"dotcms": {
"command": "npx",
"args": ["-y", "@dotcms/mcp-server@beta"],
"env": {
"DOTCMS_URL": "https://your-dotcms-instance.com",
"AUTH_TOKEN": "your-api-token"
}
}
}
}Using bunx (Bun):
{
"mcpServers": {
"dotcms": {
"command": "bunx",
"args": ["@dotcms/mcp-server@beta"],
"env": {
"DOTCMS_URL": "https://your-dotcms-instance.com",
"AUTH_TOKEN": "your-api-token"
}
}
}
}Example interactions:
You: "Create a new blog post about AI in content management"
AI: [Searches the API spec, then executes the appropriate API calls]
You: "Show me all my content types"
AI: [Discovers and displays your content schemas]
You: "Generate a React component for my Product content type"
AI: [Analyzes your Product fields and generates a complete component]The dotCMS MCP Server provides tools that enable comprehensive content management through AI:
Tool: search
Purpose: Explore the dotCMS REST API specification using JavaScript code that runs in an isolated sandbox.
The spec global contains the full dereferenced OpenAPI spec with paths object.
// List all available endpoint paths
return Object.keys(spec.paths)
// Find endpoints related to content types
return Object.entries(spec.paths)
.filter(([path]) => path.includes('contenttype'))
.map(([path, methods]) => ({ path, methods: Object.keys(methods) }))Tool: execute
Purpose: Execute authenticated API calls against your dotCMS instance using JavaScript code in an isolated sandbox.
// List content types
const result = await api.request({ path: '/api/v1/contenttype' })
return result
// Search content with Elasticsearch
const result = await api.request({
method: 'POST',
path: '/api/v1/es/search',
body: { query: 'contentType:webPageContent +languageId:1' }
})
return pick(result.contentlets, ['identifier', 'title', 'modDate'])Helper utilities available: pick(arr, fields), table(arr), count(arr, field), sum(arr, field), first(arr, n)
Tool: download_assets
Purpose: Download a dotCMS asset folder to the MCP server filesystem while returning only a small JSON manifest.
{
"path": "/application/themes/travel",
"dest": "/absolute/local/path/themes/travel",
"recursive": true,
"overwrite": "skip",
"include": "*.vtl,*.scss"
}The tool enumerates file assets with /api/content/_search, downloads bytes with /api/v2/assets/{identifier} through the server-side runtime, writes files under dest, and preserves relative folder structure. File bytes are not returned to the model.
Tool: upload_assets
Purpose: Upload a local directory to dotCMS file assets using /api/v2/assets/publish or /api/v2/assets/save.
{
"src": "/absolute/local/path/themes/travel",
"dest": "//demo.dotcms.com/application/themes/travel",
"include": "*.vtl,*.scss",
"publish": true,
"verify": true
}The upload destination must be host-qualified. When publish and verify are true, the tool checks live status with /api/v1/content/{identifier} and retries publish for files that did not become live.
Both search and execute automatically pre-load a minimal snapshot of the connected dotCMS instance and inject it into the sandbox as globals. The AI does not need to make discovery calls before doing work.
Available globals in both tools:
| Global | Shape | Source |
|---|---|---|
contentTypes | { id, name, variable, baseType, host?, folder? }[] | /api/v1/contenttype |
sites | { identifier, hostname, isDefault, archived }[] | /api/v1/site |
languages | { id, languageCode, countryCode, language, country, isoCode }[] | /api/v2/languages |
currentUser | { userId, email, givenName?, surname?, admin, roles? } | null | /api/v1/users/current |
// No round trip needed to find a content type by variable
const blog = contentTypes.find(c => c.variable === 'Blog')
// Resolve the default site without an extra call
const defaultSite = sites.find(s => s.isDefault)How it works
contentTypes) yields an empty array for that global, but the others still populate.api adapter as execute.Known limitations
execute do not refresh the cache. Re-read from the API directly when you need post-mutation state.For developers who want to contribute or modify the MCP server:
# Clone the dotCMS repository
git clone https://github.com/dotCMS/core.git
cd core/core-web
# Install dependencies
pnpm install
# Build the server. The build regenerates the OpenAPI spec first (via dependsOn) — by
# default from https://demo.dotcms.com; see "Building against a local dotCMS instance" below.
pnpm nx build mcp-server[!NOTE] Files are located in
core-web/apps/mcp-server(tools/config) andcore-web/libs/sdk/ai(runtime primitives + spec). We use Nx monorepo.
The processed spec lives at libs/sdk/ai/src/generated/spec.json. It is build-generated and git-ignored — the build/serve/test targets regenerate it via dependsOn, so you rarely run this by hand. The source is resolved in this order: an explicit CLI arg, then DOTCMS_SPEC_URL, then ${DOTCMS_URL}/api/openapi.json, then the demo instance.
# Defaults to https://demo.dotcms.com/api/openapi.json
pnpm nx run sdk-ai:generate-spec
# Override with a different instance — CLI arg (URL or local file path):
pnpm nx run sdk-ai:generate-spec -- http://localhost:8080/api/openapi.json
pnpm nx run sdk-ai:generate-spec -- ./openapi.jsonThere is nothing to commit — the spec is regenerated at build time. CI builds it from the demo instance by default.
The search tool exposes whatever endpoints are in the bundled spec.json, so to describe your
local instance you must regenerate the spec as part of the build.
[!IMPORTANT] Don't run
generate-specand thenbuildas two separate steps. Thebuildtarget re-runsgenerate-specitself (viadependsOn), and with no source set it falls back to the demo instance — overwriting the spec you just generated. Pass the source so the build's owngenerate-specuses it.
Set DOTCMS_SPEC_URL — it's an environment variable, so it flows into the generate-spec task
that build runs automatically (a CLI -- arg would not). One command:
# Regenerate the spec from your local instance AND build, in one step
DOTCMS_SPEC_URL=http://localhost:8080/api/openapi.json pnpm nx build mcp-server
# Then run it against the same instance (provide a local API token)
npx @modelcontextprotocol/inspector \
-e DOTCMS_URL=http://localhost:8080 \
-e AUTH_TOKEN=your-local-api-token \
node dist/apps/mcp-server/stdio.js[!NOTE] The spec source (what
searchdescribes) andDOTCMS_URL(where the tools send requests at runtime) are separate. Point them at the same instance so the spec matches what the API serves. IfDOTCMS_URLis already exported in your shell,generate-specwill reuse it (${DOTCMS_URL}/api/openapi.json) and you can dropDOTCMS_SPEC_URLentirely.
After a successful build:
npx @modelcontextprotocol/inspector -e DOTCMS_URL=https://demo.dotcms.com -e AUTH_TOKEN=the-api-token node dist/apps/mcp-server/stdio.jsThe built server works with both node and bun — the correct sandbox is selected automatically.
Using Node.js:
{
"mcpServers": {
"dotcms": {
"command": "node",
"args": ["/path/to/dotcms/core/core-web/dist/apps/mcp-server/stdio.js"],
"env": {
"DOTCMS_URL": "your-dotcms-url",
"AUTH_TOKEN": "your-api-token"
}
}
}
}Using Bun:
{
"mcpServers": {
"dotcms": {
"command": "bun",
"args": ["/path/to/dotcms/core/core-web/dist/apps/mcp-server/stdio.js"],
"env": {
"DOTCMS_URL": "your-dotcms-url",
"AUTH_TOKEN": "your-api-token"
}
}
}
}apps/mcp-server/ # MCP server (thin xmcp wrappers)
├── src/
│ ├── tools/
│ │ ├── search.ts # API spec exploration tool
│ │ ├── execute.ts # API execution tool
│ │ ├── download_assets.ts
│ │ └── upload_assets.ts
│ ├── lib/
│ │ └── assets-transfer.ts
│ └── prompts/ # Prompt templates (xmcp convention)
├── xmcp.config.ts # xmcp bundler configuration
├── jest.config.ts # Test configuration
└── project.json # Nx project configuration
libs/sdk/ai/ # Portable runtime primitives
├── scripts/
│ └── generate-spec.ts # OpenAPI spec processor (run manually to refresh)
├── src/
│ ├── lib/
│ │ ├── executor.ts # Sandbox executor orchestration
│ │ ├── http-client.ts # Authenticated HTTP adapter
│ │ ├── spec.ts # OpenAPI spec loader
│ │ ├── types.ts # TypeScript type definitions
│ │ └── sandbox/ # Sandbox isolation (dual-runtime)
│ │ ├── index.ts # Runtime detection factory
│ │ ├── interface.ts # Sandbox interface
│ │ ├── bun-worker.ts # Bun Web Worker sandbox
│ │ └── node-worker.ts # Node.js worker_threads sandbox
│ └── generated/
│ └── spec.json # Committed processed OpenAPI spec
└── project.json # Nx project configurationxmcp Framework: The server uses xmcp for MCP protocol handling:
src/tools/schema, metadata, and a default handler functionSandbox Isolation: Code execution is sandboxed using Workers with dual-runtime support:
Blob + URL.createObjectURL)worker_threads (new Worker(code, { eval: true }))typeof globalThis.BunBuild-time Spec Processing: The OpenAPI spec is pre-processed at build time:
generate-spec target dereferences $ref pointers and filters to relevant endpoints# Build for production (regenerates the spec from demo by default; see local-build section)
pnpm nx build mcp-server
# Development mode (with hot reload)
pnpm nx serve mcp-server
# Lint the code
pnpm nx lint mcp-server
# Run all tests
pnpm nx test mcp-server
# Run tests in watch mode
pnpm nx test mcp-server --watch
# Refresh the OpenAPI spec (run when dotCMS API changes, then commit spec.json)
# Defaults to https://demo.dotcms.com/api/openapi.json
pnpm nx run sdk-ai:generate-specWhen adding new MCP tools:
.ts file in src/tools/schema (Zod), metadata (ToolMetadata), and a default handlerWe offer multiple channels to get help with the dotCMS MCP Server:
When reporting issues, please include:
GitHub pull requests are the preferred method to contribute code to dotCMS. We welcome contributions to the dotCMS MCP Server! If you'd like to contribute, please follow these steps:
git checkout -b feature/amazing-mcp-feature)apps/mcp-server directorypnpm nx test mcp-server)git commit -m 'Add amazing MCP feature')git push origin feature/amazing-mcp-feature)dotCMS is available under either the Business Source License 1.1 (BSL) or a commercial license.
Under the BSL, dotCMS can be used at no cost by individual developers, small businesses or agencies under $5M in total finances, and by larger organizations in non-production environments. Every BSL release automatically converts to GPL v3 four years after its release date. For full terms and FAQs, visit dotcms.com/bsl and dotcms.com/bsl-faq.
Production use in larger organizations, along with access to managed cloud, SLAs, support, and enterprise capabilities, is available under a commercial license from dotCMS. For details on commercial plans, features, and support options, see dotcms.com/pricing.
betaFound an issue with this documentation? View the source