ODP MCP server
Click on "Deploy 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., "@ODP MCP serverget the customer profile for jane.doe@example.com"
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.
ODP MCP server
An MCP server for Optimizely Data Platform (ODP). It gives MCP clients (Claude Desktop, Claude Code, and others) read access to ODP data through the ODP GraphQL API and the read endpoints of the ODP REST API.
It is written in TypeScript and runs as one long-running Docker container. The container holds no configuration and no secrets. Each client sends its own ODP API key and region with every request, so any number of clients, for any number of ODP accounts, can share one server.
This is an unofficial community project. It is not affiliated with, endorsed by or supported by Optimizely. Optimizely and Optimizely Data Platform are trademarks of their owner.
What it can do
Area | Tools |
GraphQL |
|
Customers |
|
Lists |
|
Schema |
|
Real-time segments |
|
Recommendations |
|
Compliance |
|
Exports (bulk reads) |
|
GraphQL is the main way to query customer data. The server reads each account's schema through introspection, so custom objects and fields are available without extra setup. A GraphQL page holds at most 1,000 records. For larger reads, use the export tools. They write files to ODP's S3 bucket; segment exports return presigned download URLs.
The server is read-only. GraphQL mutations are rejected unless the operator sets ODP_MCP_ALLOW_MUTATIONS=true. The REST tools that change data (upload events, update customers, subscribe, create segments, and so on) are not included yet.
Related MCP server: Dynamics 365 Finance & Operations MCP Server
How clients authenticate
The MCP endpoint is POST /mcp (Streamable HTTP, stateless). Every request must carry the caller's ODP credentials as headers:
Header | Required | Value |
| yes | The ODP account's private API key (in ODP: Settings > APIs). |
| no |
|
| only if the operator set |
|
The server forwards the key to ODP as x-api-key and never stores or logs it. One connection works with one ODP account. To use several accounts, add one MCP server entry per account in your client, each with its own key.
If a header is missing or invalid, the server returns HTTP 400 with a message saying what is wrong. If the key is wrong, ODP rejects the call and the tool returns ODP's error.
Run the server
docker compose up -d --buildThe container is named odp-mcp and uses the unless-stopped restart policy, so it comes back whenever Docker starts. To have it available after a reboot, turn on Docker Desktop's Start Docker Desktop when you sign in setting. The port is published on 127.0.0.1 only.
To check that it is running:
docker compose ps # STATUS should show "healthy"
curl http://localhost:3000/health # {"status":"ok"}
docker compose logs -f odp-mcpTo stop it, run docker compose down. After changing the source code, run docker compose up -d --build again.
Prebuilt image
Every push to main publishes a multi-arch image (linux/amd64, linux/arm64) to the GitHub Container Registry, tagged latest and sha-<short commit>. To run it without cloning the repo:
docker run -d --name odp-mcp --restart unless-stopped -p 127.0.0.1:3000:3000 ghcr.io/jacobpretorius/opti.odp.mcp:latestOptional operator settings
No settings are required. ODP_MCP_AUTH_TOKEN is a secret, so put it in a .env file next to docker-compose.yml (the file is gitignored and Compose reads it automatically), as ODP_MCP_AUTH_TOKEN=<long random string>. Set the others under environment: in docker-compose.yml:
Variable | Default | Purpose |
| not set | If set (at least 16 characters), clients must also send |
|
| Allow GraphQL mutations. |
|
| Show the export-job start tools. |
|
| Listen address inside the container. |
| not set | Testing only: send every ODP call to this URL instead of the regional ODP host. |
Connect Claude Desktop
Claude Desktop cannot send custom headers to an HTTP MCP server on its own:
claude_desktop_config.jsononly launches local commands.Settings > Connectors > Add custom connector connects from Anthropic's cloud, so it cannot reach
localhost. It also supports only OAuth, not custom headers. See custom connectors.
The standard workaround is mcp-remote. It is a small local bridge that Claude Desktop launches. It forwards MCP traffic to the server's HTTP endpoint and adds your headers. It requires Node.js on the machine running Claude Desktop, and it is fetched automatically by npx.
Find the full path to
npx(Claude Desktop does not load your shell'sPATH):which npxOpen Claude Desktop > Settings > Developer > Edit Config. This opens
~/Library/Application Support/Claude/claude_desktop_config.jsonon macOS, or%APPDATA%\Claude\claude_desktop_config.jsonon Windows.Add the server. Replace the
npxpath, the API key and the region:{ "mcpServers": { "odp": { "command": "npx", "args": [ "-y", "mcp-remote@latest", "http://localhost:3000/mcp", "--transport", "http-only", "--header", "X-ODP-API-Key:${ODP_API_KEY}", "--header", "X-ODP-Region:${ODP_REGION}" ], "env": { "ODP_API_KEY": "your-odp-private-api-key", "ODP_REGION": "us" } } } }Quit and restart Claude Desktop. The
odpserver and its tools then appear in the chat box's tools menu.
Notes:
Where the values go.
mcp-remotefills in${ODP_API_KEY}and${ODP_REGION}fromenv. The key lives only in this file. Keep the header arguments free of spaces (Name:${VAR}), because some clients mishandle spaces insideargs.Several accounts: add one entry per account, for example
"odp-us-brand"and"odp-eu-brand", each with its ownenv.Server token: if the server has
ODP_MCP_AUTH_TOKENset, add"--header", "Authorization:${ODP_MCP_AUTH}"toargsand"ODP_MCP_AUTH": "Bearer <token>"toenv.Remote server: if the server is not on
localhostand is served over plain HTTP,mcp-remotealso needs--allow-http. Only do this on a trusted network; use HTTPS otherwise.Keeping the key out of the process list:
mcp-remotecan read headers from a file instead, using"--header-file", "/path/to/odp-headers.txt". The file has oneName: valueper line.Troubleshooting: Claude Desktop's log for the server is at
~/Library/Logs/Claude/mcp-server-odp.logon macOS. The server's own log isdocker compose logs odp-mcp. If you use nvm, thenpxpath changes when you switch Node versions.
Connect Claude Code and other HTTP clients
Clients that support HTTP MCP servers with custom headers can connect directly, without a bridge:
claude mcp add --transport http odp http://localhost:3000/mcp \
--header "X-ODP-API-Key: your-odp-private-api-key" \
--header "X-ODP-Region: us"Raw HTTP, for example to test from a script:
curl -s http://localhost:3000/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H 'mcp-protocol-version: 2025-06-18' \
-H 'x-odp-api-key: your-odp-private-api-key' \
-H 'x-odp-region: us' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"odp_list_lists","arguments":{}}}'Example prompts
"Show me the ODP GraphQL schema and describe the Customer type."
"Find the customer jane@example.com, with their last orders and whether they're subscribed to the newsletter list."
"List customers whose email matches 'jacob*'."
"Which real-time segments exist, and is vuid 7bfa… in
active_visitors?""Export all customers with an email and CCPA opt-out status, then check when the export is done."
Useful GraphQL patterns:
# Filtering
{ customers(filter: "email =~ 'jacob*'") { edges { node { email first_name } } } }
# Facets: the top 10 values of a text field
{ customers { facets { first_name { name count } } } }
# List subscription
{ customer(email: "a@b.com") { list_member(list_id: "newsletter") { subscribed } } }
# Real-time segment membership
{ customer(vuid: "…") { audiences(subset: ["active_visitors"]) { edges { node { name } } } } }Security notes
A private API key can read all customer data (PII) in its account. Every request carries one, so any traffic that leaves
localhostmust use HTTPS. Put a TLS reverse proxy in front of the container.The server holds keys only in memory, for the duration of a request. It never writes or logs them. It caches introspected schemas per account under a SHA-256 hash of the key, never the key itself.
Without
ODP_MCP_AUTH_TOKEN, anyone who can reach the port can use the server as a relay to the ODP API. They still need a valid ODP key to get any data. Compose publishes the port on127.0.0.1only. Set a token before exposing it more widely.Error messages leave out query strings, because they can contain customer identifiers. Tool results contain whatever ODP returns.
Development
The build and runtime both happen inside Docker, so a local Node install is not needed. The multi-stage Dockerfile compiles the TypeScript and ships only dist/ and the production dependencies. It runs as the non-root node user.
src/
index.ts HTTP server: header parsing, per-request MCP server
settings.ts optional env settings, region hosts, fixed limits
odp-client.ts fetch wrapper: auth header, timeouts, retries, errors
context.ts per-request tool context, result and error formatting
graphql-schema.ts introspection cache, read-only guard, schema helpers
tools/graphql.ts GraphQL tools
tools/rest.ts REST GET tools (declarative table)
tools/exports.ts export job toolsTo add a REST read endpoint, add an entry to GET_TOOLS in src/tools/rest.ts.
To test without a real ODP account, run a mock ODP API on the host and point the container at it with ODP_MCP_UPSTREAM_OVERRIDE=http://host.docker.internal:<port>.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for Statsig API - interact with Statsig's feature flags, experiments, and analytics
Unified MCP server for 70+ eCommerce platforms: products, orders, customers, and more.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Related MCP Servers
- AlicenseNot gradedqualityNot gradedmaintenanceProvides read-only access to TrustLayer's public API, enabling users to query and retrieve data about parties, documents, projects, and other TrustLayer entities through MCP-compatible tools.MIT
- AlicenseNot gradedqualityDmaintenanceProvides a secure gateway to the Dynamics 365 F\&O OData API, enabling LLMs and MCP clients to query and manage entities such as customers, system users, and more.1MIT
- FlicenseAqualityCmaintenanceAn MCP server for reading Sitecore XM Cloud content via GraphQL, exposing tools to get item details and list children, with OAuth authentication and path-based access control.2-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents and MCP clients to safely access upstream MCP servers through centralized policy enforcement, including allow/deny/approval decisions, schema pinning, circuit breakers, and audit logging.MIT