Skip to main content
Glama
first3things

optimizely-cms-mcp

by first3things

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.

Related MCP server: Sitecore MCP Server

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 build

Configuration

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=30000

Running the Server

Development Mode

# Run with hot reloading
npm run dev

# Run with debug logging
LOG_LEVEL=debug npm run dev

Production Mode

# Build and run
npm run build
npm start

# Or run directly
node dist/index.js

Testing the Server

# Run all unit tests
npm test

# Run tests with coverage
npm run test:coverage

# Type checking
npm run typecheck

# Linting
npm run lint

How 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.json

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Linux: ~/.config/Claude/claude_desktop_config.json

For Windows, you can open it quickly with:

notepad %APPDATA%\Claude\claude_desktop_config.json

Step 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:

  1. Completely quit Claude Desktop (not just close the window)

  2. Start Claude Desktop again

  3. 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:

  1. Check the file path is correct and uses proper escaping (\\ for Windows)

  2. Ensure you've built the project (npm run build)

  3. Verify the dist/index.js file exists

  4. Check 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.

  1. help - πŸš€ START HERE! Get context-aware help and learn the discovery-first workflow

    • Examples: help({}), help({"topic": "workflow"})

  2. get - 🎯 UNIFIED TOOL - Get content by ANY identifier in ONE call

    • Replaces the old search β†’ locate β†’ retrieve workflow

    • Auto-discovers fields and returns complete content

    • βœ… Supports Visual Builder pages with full composition structure

    • Examples: get({"identifier": "/"}), get({"identifier": "Article 4"})

  3. discover - Find content types and fields dynamically

    • No hardcoded assumptions about your CMS structure

    • Examples: discover({"target": "types"}), discover({"target": "fields", "contentType": "ArticlePage"})

  4. analyze - Deep analysis of content type requirements

    • Understand fields, constraints, and defaults

    • Example: analyze({"contentType": "ArticlePage"})

  5. search - Intelligent content search with auto-discovery

    • ⚠️ Note: get is usually better for most use cases

    • Example: search({"query": "mcp", "contentTypes": ["ArticlePage"]})

  6. locate - Find specific content by ID, key, or path

    • ⚠️ Note: get is usually better for most use cases

    • Example: locate({"identifier": "/news/article-1"})

  7. retrieve - Get full content from Content Management API

    • ⚠️ Note: get is usually better (uses faster Graph API)

    • Use only when get suggests it or you need CMA-specific data

    • Example: retrieve({"identifier": "12345"})

πŸ”§ Utility Tools (3)

  • health-check - Check API connectivity and server health

  • get-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:

  1. content_creation_wizard - Interactive content creation with discovery

    • Essential for creating new content

    • Example: content_creation_wizard({"step": "start"})

  2. content-test-api - Test CMA connectivity and endpoints

    • Validates 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 - Use discover instead

  • type-discover - Use discover({"target": "types"}) instead

  • type-match - Use discover instead

  • content_type_analyzer - Use analyze instead

  • graph_discover_types - Use discover({"target": "types"}) instead

  • graph_discover_fields - Use discover({"target": "fields"}) instead

  • graph-query - Use get or search instead

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

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 content

Content Creation Workflow

1. discover({"target": "types"})              # Find available types
2. analyze({"contentType": "ArticlePage"})    # Understand requirements
3. content_creation_wizard({...})             # Create with guidance

Visual 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: null or not present

  • Content Location: Stored directly in the composition structure

  • Access: Content is already included in the get response

  • Example: 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 details

  • Example: 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:

  1. First, retrieve the page with get({"identifier": "/"})

  2. Inspect the composition structure for components

  3. For inline components (null key): Content is already in the response βœ…

  4. 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() call

  • Display 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 retrieve tool

  • Graph API indexing delay: Content may take 1-5 minutes to appear in Graph API results

  • Tool behavior: The get and search tools 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.json

Adding New Tools

  1. Create tool implementation in src/logic/

  2. Add tool registration in appropriate section

  3. Add TypeScript types if needed

  4. Write tests in tests/

  5. 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:coverage

Unit 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:key

Troubleshooting

Common Issues

  1. Authentication Errors

    • Verify your API credentials in .env

    • For 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

  2. Connection Issues

    • Verify network connectivity

    • Check firewall settings

    • Confirm API endpoints are accessible

  3. Build Errors

    • Run npm install to ensure dependencies

    • Check Node.js version (>=18 required)

    • Clear dist/ and rebuild

  4. 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 start

Health Check

Test server connectivity:

# Using the built tool
echo '{"method": "tools/call", "params": {"name": "health_check"}}' | node dist/index.js

User 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

  1. 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

  2. Configure the MCP Server:

    # In your .env file
    CMA_IMPERSONATE_USER=user@example.com
  3. Update 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_as field

  • All 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.js

This 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

  1. Fork the repository

  2. Create a feature branch

  3. Make your changes

  4. Add tests

  5. Run npm test and npm run typecheck

  6. Submit a pull request

License

MIT

Related MCP Connectors

Related MCP Servers