MCP server for CMS 13
The Optimizely.Cms.Opal.Tools package includes a Model Context Protocol (MCP) server that exposes the same CMS tools available through Opal Chat to any MCP-compatible AI client. Manage CMS content, content types, and display templates from your development environment with an MCP client such as Claude Code or Cursor. The MCP server puts these tools in the editor where you write code, so you can create content types and display templates while building the templates that render them.
The MCP server shares the same tools and descriptions as the Opal protocol. See Available tools for CMS 13 for the full tool reference.
Prerequisites
- .NET 10.0 or later.
- Optimizely.Cms.Opal.Tools 13.2.0 or later.
- An authentication method configured (see Authentication and authorization).
Install and configure
Register the MCP server
Add the MCP server to your service collection in Startup.cs or Program.cs. Chain an authentication method to configure how MCP clients authenticate.
services.AddCmsMcpServer().WithClientSecretAuthentication();AddCmsMcpServer() registers the MCP server with stateless HTTP transport and all CMS tools. The chained authentication method (for example, WithClientSecretAuthentication()) configures how the server validates incoming requests.
Map the MCP endpoint
Map the MCP endpoint in your application pipeline and apply the matching authorization policy.
app.UseEndpoints(endpoints =>
{
endpoints.MapContent();
endpoints.MapMcp("/mcp").RequireClientSecretAuthorization();
});The convenience extension method RequireClientSecretAuthorization() applies the correct authorization policy for the authentication method.
Authentication and authorization
Every MCP tool call executes CMS operations on behalf of an authenticated user. The MCP server requires an authentication method that establishes a user identity so that all content operations respect CMS permissions.
Choose an authentication method
Method | Use case | Identity | Permissions |
|---|---|---|---|
Client secret | Local development, secure internal environments | Application identity with CmsAdmins role | Full access to all tools |
Custom | Integration with existing auth systems | Depends on implementation | Depends on identity mapping |
Optimizely Identity | Coming in a future release | Individual Opti ID user | Per-user CMS permissions |
Client secret authentication is designed for local development where the network is trusted and permission scoping is not required. For production environments, implement a custom authentication handler that integrates with your existing authentication system. Optimizely Identity (Opti ID) authentication with per-user permission scoping will be available in a future release.
How tool authorization works
When the MCP server receives a request:
- The authentication handler validates the credentials (secret or custom token).
- The handler creates a ClaimsPrincipal representing the authenticated identity.
- Each tool impersonates this identity for all CMS API calls.
- CMS enforces the standard permission model (the tool can only access content the impersonated user has permission to access).
All tools require an authorized identity. Some tools, such as content type management, require the CmsAdmins role. Other tools operate under the authenticated user's own content permissions.
Optimizely Identity authentication
Client secret authentication
Use WithClientSecretAuthentication() for local development and secure (non-public) environments. This method authenticates requests with a shared secret in the Authorization header.
services.AddCmsMcpServer().WithClientSecretAuthentication();endpoints.MapMcp("/mcp").RequireClientSecretAuthorization();Configure the secret in appsettings.json:
{
"Optimizely": {
"Cms": {
"Mcp": {
"Secret": "your-secret-value"
}
}
}
}Or use user secrets for local development:
dotnet user-secrets set "Optimizely:Cms:Mcp:Secret" "your-secret-value"MCP clients send the secret as the value of the Authorization header:
Authorization: your-secret-valueThe client secret handler creates an identity with the CmsAdmins role, which grants full access to all CMS tools. This is suitable for development where permission scoping is not required.
Custom authentication
For scenarios where client secret authentication is not appropriate, you can implement a custom authentication handler. This is useful when you want to authenticate MCP requests with an existing authentication system such as the CMS Management API's OAuth2 flow.
A custom authentication implementation follows a three-part pattern:
- Authentication handler – Create a class that extends AuthenticationHandler<AuthenticationSchemeOptions> and override HandleAuthenticateAsync(). The handler validates incoming credentials (for example, a JWT or API key) and returns a ClaimsPrincipal representing an authenticated identity that CMS can resolve.
- IMcpServerBuilder extension method – Register the authentication handler with AddScheme<>(), define an authorization policy with AddAuthorizationBuilder().AddPolicy(), and optionally register an ISynchronizedApplicationsRepository interceptor so the MCP application identity is visible in the CMS permission system.
- IEndpointConventionBuilder extension method – Apply the authorization policy to the MCP endpoint with RequireAuthorization().
Register and map using the same pattern as client secret authentication:
services.AddCmsMcpServer().WithYourCustomAuthentication();
endpoints.MapMcp("/mcp").RequireYourCustomAuthorization();The authenticated identity's CMS permissions determine which tools it can use. Some tools, such as content type management, require the CmsAdmins role. Other tools operate under the identity's own content permissions. Your authentication handler should verify the identity has the required permissions, not assign roles it does not have.
Connect an MCP client
After configuring the MCP server, connect your AI development tool by pointing it to the MCP endpoint. The configuration depends on the authentication method you chose.
Connect with client secret authentication
With client secret authentication, the MCP client sends the secret directly in the Authorization header. This works with any MCP client that supports streamable HTTP transport.
Claude Code
Add to .mcp.json or project settings:
{
"mcpServers": {
"optimizely-cms": {
"type": "http",
"url": "https://YOUR_CMS_HOST/mcp",
"headers": {
"Authorization": "your-secret-value"
}
}
}
}Cursor
Add to .cursor/mcp.json in your project root (or ~/.cursor/mcp.json for global configuration):
{
"mcpServers": {
"optimizely-cms": {
"type": "http",
"url": "https://YOUR_CMS_HOST/mcp",
"headers": {
"Authorization": "your-secret-value"
}
}
}
}Codex
Add to ~/.codex/config.toml (or .codex/config.toml in a trusted project directory):
[mcp_servers.optimizely-cms]
type = "http"
url = "https://YOUR_CMS_HOST/mcp"
[mcp_servers.optimizely-cms.headers]
Authorization = "your-secret-value"Other MCP clients
Any MCP client that supports streamable HTTP transport can connect. Configure the client with:
- Endpoint URL: https://YOUR_CMS_HOST/mcp
- Transport: Streamable HTTP (stateless)
- Authentication: The shared secret value in the Authorization header