Adobe Journey Optimizer Content API MCP server

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.

Category
Visit Server

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.

  1. Create a config folder.
  2. Add config/credentials.json with your Adobe credentials export.
  3. Add config/settings.json with your sandbox name.
  4. Build the Docker image.
  5. Start the server in either HTTP mode or stdio mode.
  6. 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:

  1. Looks for /app/config/settings.json and loads the sandbox name.
  2. Looks for /app/config/credentials.json and loads the Adobe credentials.
  3. Converts those JSON files into generated env files.
  4. Sources the generated env files.
  5. 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 stdio transport when possible.
  • Start with the simplified wrapper tools:
    • list_fragments
    • get_fragment
    • create_fragment
    • publish_fragment
    • list_templates
    • get_template
  • Use the discovery resources first if your client supports MCP resources:
    • overview://capabilities
    • overview://operations
    • overview://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/body objects
  • some MCP clients discover servers more reliably when resources are exposed
  • stdio is 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 server
  • MCP_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 to settings.json
  • AJO_CREDENTIALS_FILE — optional custom path to credentials.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-id header is present.
    • Subsequent requests must include the mcp-session-id header returned by the server.
  • GET /mcp

    • Used to open the streamable event channel for the existing session.
    • Must include the same mcp-session-id header from the initialization response.
  • DELETE /mcp

    • Used to terminate an active MCP session.
    • Must include the same mcp-session-id header.

Example connection flow

  1. Start the server:
MCP_PORT=3000 docker run --rm -it \
  -v "$(pwd)/config:/app/config:ro" \
  ajo-content-api-mcp-server
  1. Initialize the session with a POST to /mcp.
  2. Open a GET stream to /mcp using the returned mcp-session-id.
  3. Send follow-up POST requests with the same mcp-session-id.
  4. 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.json and authenticated via config/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: body object matching the OpenAPI schema, for example name, description, templateType, channels, and template
  • 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-id header for tracing
  • Concurrency Control: Update operations (put, patch) use ETags for optimistic concurrency control
  • Async Operations: Publication operations are asynchronous; use getLastPublicationStatus to 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 limit and start parameters

Recommended Servers

playwright-mcp

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured