optimizely-cms-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@optimizely-cms-mcpFind content with 'product' in the title"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Optimizely MCP Server
A Model Context Protocol (MCP) server for Optimizely CMS, providing AI assistants with comprehensive access to Optimizely's GraphQL API and Content Management API.
Version
Current Version: 2.0.0-beta Status: Beta / Pre-Release
This is an active development version and is not yet a release candidate. Features are subject to change, and additional testing is required before production use.
Features
Core Capabilities
Discovery-First Architecture: Zero hardcoded assumptions about content types or fields
Dynamic Schema Introspection: Discovers available content types and fields at runtime
Unified Content Retrieval: Get any content by URL, key, GUID, or search term in one call
Visual Builder Support: Full support for Optimizely Visual Builder pages with composition structure
Content Management: Create and manage content via interactive wizard
Intelligent Field Mapping: Pattern-based field matching with confidence scoring
GraphQL & CMA Integration: Direct access to both Graph API (read) and Content Management API (write)
Smart Caching: Built-in caching for improved performance
Type Safety: Full TypeScript support with runtime validation
API Support
Graph API: Fast content retrieval, search, and discovery
Content Management API: Content creation, updates, and draft access
Dual Authentication: Supports both Graph (single key, HMAC) and CMA (OAuth2) authentication
Installation
# Clone the repository
git clone https://github.com/your-org/optimizely-mcp-server.git
cd optimizely-mcp-server
# Install dependencies
npm install
# Build the project
npm run buildConfiguration
Create a .env file in the project root:
# Server Configuration
SERVER_NAME=optimizely-mcp-server
SERVER_VERSION=1.0.0
TRANSPORT=stdio
# Optimizely Graph Configuration
GRAPH_ENDPOINT=https://cg.optimizely.com/content/v2
GRAPH_AUTH_METHOD=single_key # Options: single_key, hmac, basic, bearer, oidc
GRAPH_SINGLE_KEY=your-single-key
# For HMAC auth:
# GRAPH_APP_KEY=your-app-key
# GRAPH_SECRET_KEY=your-secret-key
# Content Management API Configuration
CMA_BASE_URL=https://api.cms.optimizely.com/preview3
CMA_CLIENT_ID=your-client-id # Get from Settings > API Keys in CMS
CMA_CLIENT_SECRET=your-client-secret
CMA_GRANT_TYPE=client_credentials
CMA_TOKEN_ENDPOINT=https://api.cms.optimizely.com/oauth/token
CMA_IMPERSONATE_USER= # Optional: User email to impersonate (see Impersonation section)
# Optional Configuration
CACHE_TTL=300000 # Cache TTL in milliseconds (default: 5 minutes)
LOG_LEVEL=info # Options: debug, info, warn, error
MAX_RETRIES=3
TIMEOUT=30000Running the Server
Development Mode
# Run with hot reloading
npm run dev
# Run with debug logging
LOG_LEVEL=debug npm run devProduction Mode
# Build and run
npm run build
npm start
# Or run directly
node dist/index.jsTesting the Server
# Run all unit tests
npm test
# Run tests with coverage
npm run test:coverage
# Type checking
npm run typecheck
# Linting
npm run lintHow MCP Servers Work
MCP servers communicate via stdio (standard input/output), not HTTP ports:
No port required - The server doesn't listen on any network port
Process-based - Claude Desktop spawns your server as a child process
JSON-RPC messages - Communication happens through stdin/stdout pipes
Secure - No network exposure, runs only when Claude needs it
MCP Client Configuration
Claude Desktop Setup
Step 1: Find your config file
Open the configuration file in a text editor:
Windows:
%APPDATA%\Claude\claude_desktop_config.jsonmacOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
For Windows, you can open it quickly with:
notepad %APPDATA%\Claude\claude_desktop_config.jsonStep 2: Add the server configuration
{
"mcpServers": {
"optimizely": {
"command": "node",
"args": ["%USERPROFILE%\\path\\to\\optimizely-mcp-server\\dist\\index.js"],
"env": {
"LOG_LEVEL": "error",
"GRAPH_ENDPOINT": "https://cg.optimizely.com/content/v2",
"GRAPH_AUTH_METHOD": "single_key",
"GRAPH_SINGLE_KEY": "your-key",
"CMA_BASE_URL": "https://api.cms.optimizely.com/preview3/experimental",
"CMA_CLIENT_ID": "your-client-id",
"CMA_CLIENT_SECRET": "your-client-secret",
"CMA_GRANT_TYPE": "client_credentials",
"CMA_TOKEN_ENDPOINT": "https://api.cms.optimizely.com/oauth/token",
"CMA_IMPERSONATE_USER": ""
}
}
}
}In JSON on Windows, you must use double backslashes (\). If your folder path has spaces, this still works because each argument is a separate JSON string.
Windows: %USERPROFILE% expands to your home directory (e.g., C:\Users\Alice). If Claude doesnβt expand it automatically, replace it with your actual path (e.g., C:\Users\Alice\path\to\optimizely-mcp-server\dist\index.js). In PowerShell, the equivalent is $env:USERPROFILE, but inside this JSON config you should keep %USERPROFILE% or use the full path.
macOS/Linux: the equivalent shortcut is ~ or $HOME (e.g., /Users/alice or /home/alice). If ~/$HOME isnβt expanded correctly, replace it with the full path.
Step 3: Restart Claude Desktop
After saving the config file:
Completely quit Claude Desktop (not just close the window)
Start Claude Desktop again
The Optimizely tools should now be available
Step 4: Verify it's working
In a new Claude conversation, try:
"Can you list the available Optimizely tools?"
"Use the health-check tool to test the connection"
Troubleshooting
If the server doesn't load:
Check the file path is correct and uses proper escaping (
\\for Windows)Ensure you've built the project (
npm run build)Verify the
dist/index.jsfile existsCheck Claude's logs for errors
Other MCP Clients
For other MCP-compatible clients, use the stdio transport configuration:
{
"name": "optimizely",
"transport": {
"type": "stdio",
"command": "node",
"args": ["/path/to/optimizely-mcp-server/dist/index.js"]
},
"env": {
// Environment variables as above
}
}Available Tools (14 Total)
π Core Discovery & Retrieval Tools
These tools use the Graph API to dynamically discover your CMS structure and retrieve content without hardcoded assumptions.
help- π START HERE! Get context-aware help and learn the discovery-first workflowExamples:
help({}),help({"topic": "workflow"})
get- π― UNIFIED TOOL - Get content by ANY identifier in ONE callReplaces the old
searchβlocateβretrieveworkflowAuto-discovers fields and returns complete content
β Supports Visual Builder pages with full composition structure
Examples:
get({"identifier": "/"}),get({"identifier": "Article 4"})
discover- Find content types and fields dynamicallyNo hardcoded assumptions about your CMS structure
Examples:
discover({"target": "types"}),discover({"target": "fields", "contentType": "ArticlePage"})
analyze- Deep analysis of content type requirementsUnderstand fields, constraints, and defaults
Example:
analyze({"contentType": "ArticlePage"})
search- Intelligent content search with auto-discoveryβ οΈ Note:
getis usually better for most use casesExample:
search({"query": "mcp", "contentTypes": ["ArticlePage"]})
locate- Find specific content by ID, key, or pathβ οΈ Note:
getis usually better for most use casesExample:
locate({"identifier": "/news/article-1"})
retrieve- Get full content from Content Management APIβ οΈ Note:
getis usually better (uses faster Graph API)Use only when
getsuggests it or you need CMA-specific dataExample:
retrieve({"identifier": "12345"})
π§ Utility Tools (3)
health-check- Check API connectivity and server healthget-config- Get current server configuration (sanitized)get-documentation- Get documentation for available tools by category
π§ Content Management Tools (CMA API)
These tools use the Content Management API for write operations and detailed content access:
content_creation_wizard- Interactive content creation with discoveryEssential for creating new content
Example:
content_creation_wizard({"step": "start"})
content-test-api- Test CMA connectivity and endpointsValidates authentication and permissions
Example:
content-test-api({})
Note: The retrieve tool (listed in Core Tools above) also uses CMA for accessing draft content and version history.
β οΈ Deprecated Tools (Being Removed)
These Graph API discovery tools are duplicates of the new discover tool and will be removed in a future version:
graph-introspection- Usediscoverinsteadtype-discover- Usediscover({"target": "types"})insteadtype-match- Usediscoverinsteadcontent_type_analyzer- Useanalyzeinsteadgraph_discover_types- Usediscover({"target": "types"})insteadgraph_discover_fields- Usediscover({"target": "fields"})insteadgraph-query- Usegetorsearchinstead
Key Architecture Principles
Discovery-First Design
Unlike traditional integrations that hardcode content types and field names, this MCP server:
Never hardcodes content types - No assumptions about "ArticlePage", "StandardPage", etc.
Never hardcodes field mappings - No predefined paths like "SeoSettings.MetaTitle"
Discovers everything dynamically - Uses introspection to understand your CMS
Adapts to any CMS configuration - Works with custom content types and fields
Intelligent Field Mapping
The server uses pattern matching and similarity scoring to:
Map user-friendly field names to actual CMS fields
Handle nested properties automatically
Generate appropriate default values based on field types
Provide confidence scores for mappings
Recommended Workflows
Simple Content Retrieval (Most Common)
1. get({"identifier": "homepage"}) # That's it! One call gets everything.The get tool automatically:
Detects identifier type (search term, URL, key, or GUID)
Finds the content
Discovers all available fields
Returns complete content including Visual Builder composition
Advanced Discovery Workflow
1. help({}) # Learn the workflow
2. discover({"target": "types"}) # Find content types
3. discover({"target": "fields", "contentType": "..."}) # Get fields
4. get({"identifier": "..."}) # Retrieve contentContent Creation Workflow
1. discover({"target": "types"}) # Find available types
2. analyze({"contentType": "ArticlePage"}) # Understand requirements
3. content_creation_wizard({...}) # Create with guidanceVisual Builder Support
The get tool fully supports Optimizely Visual Builder (formerly known as Visual Experience Composer) pages:
Features
β Automatic Detection - Recognizes Visual Builder pages by interface (
_IExperience)β Complete Composition Retrieval - Returns full structure in a single call
β Nested Structure - Handles grids, rows, columns, and components
β Component Content - Includes inline component data directly in composition
β Recursive Depth - Supports any level of nesting
Understanding Component Types
Visual Builder components come in two types:
1. Inline Components (Embedded Content)
Key:
nullor not presentContent Location: Stored directly in the composition structure
Access: Content is already included in the
getresponseExample: Text components with content like "Welcome to our site"
{
"component": {
"_metadata": {
"types": ["Text", "_Component"],
"key": null // β NULL = inline
},
"Content": "Welcome Text" // β Content is here
}
}Important: Do NOT try to retrieve inline components separately - the content is already provided!
2. Referenced Components (Separate Content Items)
Key: Valid GUID (e.g., "f7e7f5c9-1e77-4884-a8fc-a9c9ae56560c")
Content Location: Stored as separate content items in CMS
Access: Use
get({"identifier": "component-key"})to retrieve full detailsExample: Shared components like Site Settings, reusable blocks
{
"component": {
"_metadata": {
"types": ["ArticleList", "_Component"],
"key": "f7e7f5c91e774884a8fca9c9ae56560c" // β Has key
}
// May include basic fields, use get() for full content
}
}Best Practices
When working with Visual Builder pages:
First, retrieve the page with
get({"identifier": "/"})Inspect the composition structure for components
For inline components (null key): Content is already in the response β
For referenced components (has key): Use
get({"identifier": "key"})to fetch full details
Example Usage
// Get a Visual Builder homepage
get({"identifier": "/"})
// Returns complete structure with inline content:
{
"content": {
"_metadata": { ... },
"composition": {
"nodes": [
{
"key": "grid-id",
"displayName": "Welcome Section",
"nodes": [
{
"component": {
"_metadata": {
"types": ["Text"],
"key": null // Inline - content included
},
"Content": "Welcome to our site"
}
},
{
"component": {
"_metadata": {
"types": ["ArticleList"],
"key": "f7e7f5c9..." // Referenced - fetch separately
}
}
}
]
}
]
}
}
}Known Limitations
Performance - Large compositions may take longer to retrieve due to nested structure
Referenced Component Details - Basic metadata only; full content requires separate
get()callDisplay Settings - Not included in current implementation (can be added if needed)
Important Notes
Content Indexing Delay
After creating new content using the content_creation_wizard or other creation tools:
Immediate availability in CMA: Content is immediately available via
retrievetoolGraph API indexing delay: Content may take 1-5 minutes to appear in Graph API results
Tool behavior: The
getandsearchtools use Graph API and will return "not found" for newly created content until indexing completes
Best practice: After creating content, wait a few minutes before attempting to retrieve it with get or search. Alternatively, use the retrieve tool which queries the CMA directly and has no indexing delay.
Draft vs Published Content
Graph API: Only returns published content
CMA API: Returns both draft and published content
New content: Created in draft status by default
To make content searchable via
get/search, it must be published first
Development
Project Structure
optimizely-mcp-server/
βββ src/
β βββ index.ts # Server entry point
β βββ register.ts # Tool registration
β βββ config.ts # Configuration management
β βββ clients/ # API clients
β β βββ graph-client.ts
β β βββ cma-client.ts
β βββ logic/ # Tool implementations
β β βββ utility/
β β βββ graph/
β β βββ content/
β βββ types/ # TypeScript types
β βββ utils/ # Utilities
βββ tests/ # Test files
βββ dist/ # Built output
βββ package.jsonAdding New Tools
Create tool implementation in
src/logic/Add tool registration in appropriate section
Add TypeScript types if needed
Write tests in
tests/Update documentation
Testing Guidelines
Unit tests for all tool implementations
Integration tests for API clients
Mock external API calls
Test error scenarios
Maintain >80% coverage
Testing & Debugging
Unit Tests
Run automated tests with Vitest:
# Run unit tests
npm test
# Run with coverage report
npm run test:coverageUnit tests are located in /tests/ and cover:
GraphQL client functionality
CMA client operations
Health check features
Integration Testing & Debugging
Test your setup with these npm scripts:
# Check credentials are valid
npm run check:credentials
# Test MCP tools
npm run test:tools
# Test with debug output
npm run test:tools:debug
# Test GraphQL connection
npm run debug:graph
# Validate API key format
npm run validate:keyTroubleshooting
Common Issues
Authentication Errors
Verify your API credentials in
.envFor CMA: Create API keys in Settings > API Keys in your Optimizely CMS instance
Check token expiration for CMA (tokens expire after 5 minutes)
Ensure correct auth method for Graph
Connection Issues
Verify network connectivity
Check firewall settings
Confirm API endpoints are accessible
Build Errors
Run
npm installto ensure dependenciesCheck Node.js version (>=18 required)
Clear
dist/and rebuild
403 Forbidden Errors (Content Creation)
This typically means insufficient permissions
See the Impersonation section below for a solution
Verify the user has content creation rights
Check the target container allows the content type
Debug Mode
Enable debug logging for troubleshooting:
LOG_LEVEL=debug npm startHealth Check
Test server connectivity:
# Using the built tool
echo '{"method": "tools/call", "params": {"name": "health_check"}}' | node dist/index.jsUser Impersonation
If you encounter 403 Forbidden errors when creating content, you can use user impersonation to execute API calls as a specific user who has the necessary permissions.
When to Use Impersonation
Use impersonation when:
The API client lacks content creation permissions
You need to test with different user permission levels
You want actions attributed to a specific user
Setup Instructions
Enable Impersonation in Optimizely CMS:
Log into Optimizely CMS as an administrator
Navigate to Settings > API Clients
Find your API client
Enable the "Allow impersonation" option
Save the changes
Configure the MCP Server:
# In your .env file CMA_IMPERSONATE_USER=user@example.comUpdate Claude Desktop Config (if using environment variables):
{ "mcpServers": { "optimizely": { "env": { "CMA_IMPERSONATE_USER": "user@example.com", // ... other settings } } } }
How It Works
When impersonation is configured:
Authentication requests use JSON format with
act_asfieldAll content operations execute as the impersonated user
Created content shows the impersonated user as the author
Testing Impersonation
Test that impersonation is working:
# Run the impersonation test script
node scripts/test-impersonation-final.jsThis will create test content and show which user created it.
Security Best Practices
Only enable impersonation when necessary
Use accounts with minimal required permissions
Regularly review API client permissions
Monitor API usage logs for unusual activity
Contributing
Fork the repository
Create a feature branch
Make your changes
Add tests
Run
npm testandnpm run typecheckSubmit a pull request
License
MIT
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/first3things/optimizely-cms-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server