Adobe Journey Optimizer Content API MCP server
Enables interaction with Adobe Journey Optimizer content through the Content API, supporting sandbox configuration and secure credential management.
README
Adobe Journey Optimizer Content API MCP server
This project exposes Adobe Journey Optimizer Content API operations as MCP tools.
Quickstart
Follow these steps if you want to get the server running quickly.
- Create a
configfolder. - Add
config/credentials.jsonwith your Adobe credentials export. - Add
config/settings.jsonwith your sandbox name. - Build the Docker image.
- Start the server in either HTTP mode or stdio mode.
- Point your MCP client at the running server.
If you are not sure which transport to use:
- Use HTTP when your client connects to a URL like
http://localhost:3000/mcp. - Use stdio when your client launches this server as a subprocess.
Prerequisites
- Docker, if you want to run the containerized server
- Node.js 20+, if you want to run the server directly
- Adobe Journey Optimizer credentials exported as JSON
- The Adobe sandbox name you want the server to use
Recommended workspace layout
my-workspace/
├── config/
│ └── credentials.json
├── docker-compose.yml
└── ajo-content-mcp-server/
├── Dockerfile
└── ...
Place the exported Adobe environment JSON at config/credentials.json and create a small config/settings.json for the sandbox name. Mount ./config into the container.
Dedicated settings file
Create config/settings.json before starting the server.
Create config/settings.json like this:
{
"sandboxName": "dev"
}
The container reads this file by default from /app/config/settings.json and uses it to populate AJO_SANDBOX_NAME.
You can override the path with:
AJO_SETTINGS_FILE=/custom/path/settings.json
Behavior
At startup, the container:
- Looks for
/app/config/settings.jsonand loads the sandbox name. - Looks for
/app/config/credentials.jsonand loads the Adobe credentials. - Converts those JSON files into generated env files.
- Sources the generated env files.
- Starts the MCP server.
You can override the JSON path with:
AJO_CREDENTIALS_FILE=/custom/path/credentials.json
The credentials loader expects the Adobe credentials export to provide values such as CLIENT_SECRET, API_KEY, ACCESS_TOKEN, SCOPES, TECHNICAL_ACCOUNT_ID, IMS, and IMS_ORG. It maps those into the internal AJO_* environment variables used by the app.
Build
docker build -t ajo-content-api-mcp-server .
Run
Choose one of the following runtime modes.
The default container mode is HTTP:
docker run --rm -it \
-p 3000:3000 \
-v "$(pwd)/config:/app/config:ro" \
ajo-content-api-mcp-server
Run in HTTP mode
docker run --rm -it \
-p 3000:3000 \
-e MCP_TRANSPORT=http \
-e MCP_PORT=3000 \
-v "$(pwd)/config:/app/config:ro" \
ajo-content-api-mcp-server
Run in stdio mode
docker run --rm -i \
-e MCP_TRANSPORT=stdio \
-v "$(pwd)/config:/app/config:ro" \
ajo-content-api-mcp-server
Use stdio mode when your MCP client launches the containerized server as a subprocess and communicates over stdin/stdout.
Run Directly with Node.js
Build the project first:
npm install
npm run build
Run in HTTP mode:
AJO_SETTINGS_FILE="$(pwd)/config/settings.json" \
AJO_CREDENTIALS_FILE="$(pwd)/config/credentials.json" \
MCP_TRANSPORT=http \
MCP_PORT=3000 \
node dist/index.js
Run in stdio mode:
AJO_SETTINGS_FILE="$(pwd)/config/settings.json" \
AJO_CREDENTIALS_FILE="$(pwd)/config/credentials.json" \
MCP_TRANSPORT=stdio \
node dist/index.js
Docker Compose
services:
ajo-content-mcp:
build: .
image: ajo-content-api-mcp-server
environment:
MCP_TRANSPORT: http
MCP_PORT: 3000
ports:
- "3000:3000"
volumes:
- ./config:/app/config:ro
ajo-content-mcp-stdio:
build: .
image: ajo-content-api-mcp-server
command: ["node", "dist/index.js"]
environment:
MCP_TRANSPORT: stdio
stdin_open: true
tty: true
volumes:
- ./config:/app/config:ro
With this layout, credentials.json and settings.json both live under the same mounted folder. Use ajo-content-mcp for HTTP clients and ajo-content-mcp-stdio for subprocess-based stdio clients.
Connecting to the MCP Server
This MCP server supports both stdio and HTTP-streamable transports.
Best settings for local LLMs like Gemma
If you are connecting a smaller local model, prefer these settings:
- Use
stdiotransport when possible. - Start with the simplified wrapper tools:
list_fragmentsget_fragmentcreate_fragmentpublish_fragmentlist_templatesget_template
- Use the discovery resources first if your client supports MCP resources:
overview://capabilitiesoverview://operationsoverview://examples
- Fall back to the generated OpenAPI tools only when you need a less common API operation.
Why this helps:
- local LLMs often handle flat tool arguments better than nested
path/query/headers/bodyobjects - some MCP clients discover servers more reliably when resources are exposed
stdiois usually simpler for local MCP clients than session-based HTTP transports
Example stdio startup for local clients:
AJO_SETTINGS_FILE="$(pwd)/config/settings.json" \
AJO_CREDENTIALS_FILE="$(pwd)/config/credentials.json" \
MCP_TRANSPORT=stdio \
node dist/index.js
Transport selection
MCP_TRANSPORT=stdio— runs as a stdio MCP serverMCP_TRANSPORT=http— runs the HTTP-streamable server at/mcp(default)
Example stdio startup:
MCP_TRANSPORT=stdio node dist/index.js
This mode is intended for MCP clients that launch the server as a subprocess.
HTTP mode
When MCP_TRANSPORT is unset or set to http, the server exposes an HTTP-streamable MCP endpoint at /mcp.
Default URL
http://localhost:3000/mcp
Environment variables
MCP_PORT— port the server listens on (default:3000)AJO_SETTINGS_FILE— optional custom path tosettings.jsonAJO_CREDENTIALS_FILE— optional custom path tocredentials.json
HTTP endpoints
-
POST /mcp- Used for initialization and normal MCP JSON-RPC requests.
- The client should send the MCP initialize request first when no
mcp-session-idheader is present. - Subsequent requests must include the
mcp-session-idheader returned by the server.
-
GET /mcp- Used to open the streamable event channel for the existing session.
- Must include the same
mcp-session-idheader from the initialization response.
-
DELETE /mcp- Used to terminate an active MCP session.
- Must include the same
mcp-session-idheader.
Example connection flow
- Start the server:
MCP_PORT=3000 docker run --rm -it \
-v "$(pwd)/config:/app/config:ro" \
ajo-content-api-mcp-server
- Initialize the session with a POST to
/mcp. - Open a GET stream to
/mcpusing the returnedmcp-session-id. - Send follow-up POST requests with the same
mcp-session-id. - Close the session with
DELETE /mcp.
The repository test files are lightweight smoke checks. The HTTP example test is skipped unless you manually run the MCP server locally, and the stdio test is skipped unless the required Adobe environment variables are present.
Example curl usage
# 1) Initialize the session and capture the returned MCP session ID
response=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"clientName":"ajo-mcp"},"id":1}' \
http://localhost:3000/mcp)
session_id=$(echo "$response" | jq -r '.result.sessionId')
# 2) Open the stream using the same session ID
curl -N -H "mcp-session-id: $session_id" http://localhost:3000/mcp
# 3) Send another MCP request over the same session
curl -X POST \
-H "Content-Type: application/json" \
-H "mcp-session-id: $session_id" \
-d '{"jsonrpc":"2.0","method":"someToolCall","params":{"input":{}},"id":2}' \
http://localhost:3000/mcp
Notes
- The server uses the HTTP-streamable transport, so the LLM or MCP client can receive streamed tool results and notifications.
- The server is scoped to the sandbox defined in
config/settings.jsonand authenticated viaconfig/credentials.json.
MCP Client Configuration Examples
Use the following examples based on the client you are configuring. For subprocess-based MCP clients, use MCP_TRANSPORT=stdio. For URL-based MCP clients, point them at http://localhost:3000/mcp.
Claude Desktop example
Example claude_desktop_config.json snippet for a local stdio launch:
{
"mcpServers": {
"ajo-content": {
"command": "node",
"args": ["/absolute/path/to/ajo-content-mcp-server/dist/index.js"],
"env": {
"MCP_TRANSPORT": "stdio",
"AJO_SETTINGS_FILE": "/absolute/path/to/config/settings.json",
"AJO_CREDENTIALS_FILE": "/absolute/path/to/config/credentials.json"
}
}
}
}
For an HTTP-connected client shape, use:
{
"mcpServers": {
"ajo-content": {
"url": "http://localhost:3000/mcp"
}
}
}
Codex example
Example MCP server entry for Codex using the local stdio process:
{
"mcpServers": {
"ajo-content": {
"command": "node",
"args": ["/absolute/path/to/ajo-content-mcp-server/dist/index.js"],
"env": {
"MCP_TRANSPORT": "stdio",
"AJO_SETTINGS_FILE": "/absolute/path/to/config/settings.json",
"AJO_CREDENTIALS_FILE": "/absolute/path/to/config/credentials.json"
}
}
}
}
During development, you can swap the args value to ["--import", "tsx", "/absolute/path/to/ajo-content-mcp-server/src/index.ts"].
Or with Docker:
{
"mcpServers": {
"ajo-content": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-e",
"MCP_TRANSPORT=stdio",
"-v",
"/absolute/path/to/config:/app/config:ro",
"ajo-content-api-mcp-server"
]
}
}
}
Cursor example
Example Cursor MCP configuration using the local stdio process:
{
"mcpServers": {
"ajo-content": {
"command": "node",
"args": ["/absolute/path/to/ajo-content-mcp-server/dist/index.js"],
"env": {
"MCP_TRANSPORT": "stdio",
"AJO_SETTINGS_FILE": "/absolute/path/to/config/settings.json",
"AJO_CREDENTIALS_FILE": "/absolute/path/to/config/credentials.json"
}
}
}
}
Available Tools for LLM Integration
This MCP server exposes the following tools to LLMs for managing Adobe Journey Optimizer content templates and fragments:
Content Template Tools
createTemplate
Create a new content template for use in campaigns and journeys.
- Input:
bodyobject matching the OpenAPI schema, for examplename,description,templateType,channels, andtemplate - Output: Created template with auto-generated ID and metadata
- Hint: Destructive operation
Example email template request body:
{
"body": {
"name": "Test Template",
"description": "Test Description",
"templateType": "html",
"channels": ["email"],
"template": {
"html": "<h1>Hello</h1>"
}
}
}
getTemplates
List all available content templates with optional filtering and pagination.
- Input: Optional filters (by name, channel, template type, creation date, etc.), pagination params
- Output: JSON text payload including
status, response headers, and returned data - Hint: Read-only operation
getTemplate
Fetch a specific content template by ID with full details.
- Input: Template ID, Accept header
- Output: Complete template details including audit information and ETag
- Hint: Read-only operation
putTemplate
Update an existing content template by ID.
- Input: Template ID, updated template payload, If-Match header (ETag for concurrency)
- Output: HTTP 204 No Content (success indicator)
- Hint: Idempotent operation
deleteTemplate
Delete a content template by ID.
- Input: Template ID
- Output: HTTP 204 No Content (success indicator)
- Hint: Destructive operation
patchTemplate
Patch specific fields of a content template using JSON Patch format (RFC 6902).
- Input: Template ID, JSON Patch operations, If-Match header
- Output: Updated template with new ETag
- Supported paths:
/name,/description,/parentFolderId - Hint: Idempotent operation
Content Fragment Tools
createFragment
Create a new reusable content fragment.
- Input: Fragment name, description, channel type, and content
- Output: Created fragment with auto-generated ID and metadata
- Hint: Destructive operation
getFragments
List all available content fragments with optional filtering and pagination.
- Input: Optional filters (by name, channel, fragment type, creation date, etc.), pagination params
- Output: Paginated list of fragment summaries
- Hint: Read-only operation
getFragment
Fetch a specific content fragment by ID with full details.
- Input: Fragment ID, Accept header
- Output: Complete fragment details including audit information and ETag
- Hint: Read-only operation
putFragment
Update an existing content fragment by ID.
- Input: Fragment ID, updated fragment payload, If-Match header (ETag for concurrency)
- Output: HTTP 204 No Content (success indicator)
- Hint: Idempotent operation
patchFragment
Patch specific fields of a content fragment using JSON Patch format (RFC 6902).
- Input: Fragment ID, JSON Patch operations, If-Match header
- Output: HTTP 204 No Content (success indicator)
- Supported paths:
/name,/description,/parentFolderId - Hint: Idempotent operation
publishFragment
Publish a content fragment to freeze its content and make it available for use in campaigns/journeys.
- Input: Fragment ID, publication request
- Output: HTTP 202 Accepted (async operation)
- Note: Publishing is asynchronous and may take a few seconds to complete
- Hint: Destructive operation
getLiveFragment
Fetch the content of a fragment's last successful publication.
- Input: Fragment ID
- Output: Published fragment content ready for use
- Note: Returns the frozen content from the most recent successful publication
- Hint: Read-only operation
getLastPublicationStatus
Fetch the status of the last publication request for a fragment.
- Input: Fragment ID
- Output: Publication status (successful, in progress, or error)
- Note: Use this to check async publication progress
- Hint: Read-only operation
Tool Usage Notes
- Authentication: All tools are automatically authenticated using the credentials loaded from
config/credentials.json - Sandbox Context: All operations are scoped to the sandbox specified in
config/settings.json - Request Tracking: Each request includes a unique
x-request-idheader for tracing - Concurrency Control: Update operations (
put,patch) use ETags for optimistic concurrency control - Async Operations: Publication operations are asynchronous; use
getLastPublicationStatusto monitor progress - Filtering: List operations support powerful filtering by various attributes (name regex, date ranges, creation info, etc.)
- Pagination: List operations support limit and offset-based pagination via
limitandstartparameters
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.