OpenAI Ads MCP server
Provides tools for interacting with the OpenAI Ads Advertiser API, enabling reads of ad accounts, campaigns, ad groups, ads, and performance insights, plus optional write operations such as uploading ad images and creating, pausing, activating, or updating paused campaigns, ad groups, and ads.
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., "@OpenAI Ads MCP servershow me campaign performance for the last 7 days"
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.
OpenAI Ads MCP server
Unofficial Model Context Protocol server for the OpenAI Ads Advertiser API. It is not affiliated with, endorsed by, or maintained by OpenAI.
The server speaks remote MCP over Streamable HTTP at /mcp. It calls https://api.ads.openai.com/v1 with Authorization: Bearer $OPENAI_ADS_API_KEY. Your MCP client authenticates to this server with a separate bearer token.
By default the process is read-only. Write tools are registered only when MCP_READ_ONLY=false. Campaigns, ad groups, and ads created through this server start paused.
Features
Ad account, campaign, ad group, and ad reads, including status and optional serving issues.
Insights for a date range: impressions, clicks, spend, CTR, and CPC.
Cursor pagination (
has_more,after,before) with a page cap.Client-side limits of 600 requests per minute per endpoint and 1,200 per minute overall, plus bounded retries on HTTP 429.
Optional writes: upload an image, create a paused campaign, ad group, and ad, then pause, activate, or edit them.
Archive is not a separate tool. Setting
statustoarchivedon an update is irreversible and requiresconfirm=true.GET /healthreturns 200 with no credentials, for container health checks./mcprequiresAuthorization: Bearer $MCP_AUTH_TOKEN. Secrets are not written to logs.
Read tools
Tool | Ads API |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Write tools
Registered only when MCP_READ_ONLY=false.
Tool | Ads API |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A chat-card ad needs a file_id from upload_ad_image plus a target_url. Amounts are micros of the account currency (1000000 micros = 1 currency unit). The minimum campaign budget value is 1000000.
Typical create order: upload the image, create a paused campaign, create a paused ad group, create a paused ad, then call the activate tools when you want delivery.
Related MCP server: flin-meta-ads-mcp
Requirements
Python 3.11 or newer for a local install
Docker, if you run the container
An OpenAI Ads API key from Ads Manager
A random string you choose as
MCP_AUTH_TOKEN
The Python package pins the official MCP SDK mcp==1.30.0, which provides FastMCP. MCP Python SDK 2.x renamed that class, so this project stays on the 1.30 line.
Quick start
python -m venv .venv
source .venv/bin/activate
python -m pip install -e .
cp .env.example .envEdit .env and set OPENAI_ADS_API_KEY and MCP_AUTH_TOKEN. Then:
set -a
source .env
set +a
openai-ads-mcpThe process listens on 0.0.0.0:8000. Check it with:
curl -sS http://127.0.0.1:8000/healthCall MCP with the bearer token:
curl -sS http://127.0.0.1:8000/mcp \
-H "Authorization: Bearer $MCP_AUTH_TOKEN" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json"A request without that header receives 401.
MCP client configuration
{
"mcpServers": {
"openai-ads": {
"type": "streamable-http",
"url": "https://<your-domain>/mcp",
"headers": {
"Authorization": "Bearer <MCP_AUTH_TOKEN>"
}
}
}
}For a local process, use http://127.0.0.1:8000/mcp. Put only MCP_AUTH_TOKEN in the client. The Ads API key stays in the server environment.
Configuration
Variable | Default | Purpose |
| required | Ads API bearer token. Never logged. |
| required | Bearer token required on |
|
| Write tools are registered only when this is |
|
| Bind address. |
|
| Listen port. |
|
| Advertiser API origin. Override only for tests. |
|
|
|
|
| Requests per minute for one endpoint, per process. |
|
| Requests per minute across endpoints, per process. |
|
| Window for those limits. |
|
| Extra attempts after HTTP 429. |
|
| Exponential backoff base when |
|
| Cap for a single retry wait. |
|
| HTTP timeout. |
|
| Maximum pages followed when |
Changing MCP_READ_ONLY applies on the next process start. Restart or redeploy after you change it.
The in-process limiter does not coordinate across replicas. The Ads API still enforces the account and IP quotas, and this server retries HTTP 429.
Docker
Build and run from source:
docker build -t openai-ads-mcp:local .
docker run --rm -p 8000:8000 \
-e OPENAI_ADS_API_KEY \
-e MCP_AUTH_TOKEN \
-e MCP_READ_ONLY=true \
openai-ads-mcp:localThe image is multi-stage, based on python:3.12-slim, runs as a non-root user, and defines a HEALTHCHECK for GET /health. MCP_READ_ONLY defaults to true in the image.
Docker Compose
docker-compose.yml builds this repository. It exposes port 8000 on the container network and does not publish a host port, which matches a proxy in front of the container (Coolify, Traefik, or another reverse proxy).
export OPENAI_ADS_API_KEY=...
export MCP_AUTH_TOKEN=...
docker compose up --buildThe Compose file does not publish a host port. For a local check from your machine, use docker run -p 8000:8000 above. Add a host port mapping only in a local override, and do not commit it if a reverse proxy is what reaches the container.
Prebuilt image
GitHub Actions can publish this image to GHCR. The repository workflow does not run until you add this project to a GitHub repository and publish a release. The image name is lowercased:
ghcr.io/<owner>/<repository>:0.1.0
ghcr.io/<owner>/<repository>:latestExample:
docker pull ghcr.io/<owner>/<repository>:0.1.0
docker run --rm -p 8000:8000 \
-e OPENAI_ADS_API_KEY \
-e MCP_AUTH_TOKEN \
ghcr.io/<owner>/<repository>:0.1.0Building from this Dockerfile remains a supported way to run the server. The Compose file in the repo builds from source so a fresh checkout does not depend on GHCR.
The package is private to the repository until you change the GHCR package visibility. This repo does not publish to any other registry.
Release process
Bump the version in
pyproject.toml,src/openai_ads_mcp/__init__.py(__version__), andCHANGELOG.md.Commit and push those changes.
Create a GitHub Release. Set the tag to
vX.Y.Z, for examplev0.1.0, and publish the release..github/workflows/release.ymlruns when that release is published and when av*.*.*tag is pushed. It also supportsworkflow_dispatchon a version tag.The workflow checks out the code, sets up Buildx, logs in to
ghcr.ioas${{ github.actor }}withGITHUB_TOKEN, and pushes two tags: the semver version (0.1.0) andlatest.
No other secret is required. A manual run has to use the tag ref, for example:
gh workflow run "Publish container image" --ref v0.1.0Running the workflow from a branch, without a vX.Y.Z tag, stops before it pushes an image.
.github/workflows/ci.yml runs the unit tests on pull requests and on pushes.
Reverse proxy and TLS
Terminate TLS at the proxy. Forward https://<your-domain>/mcp and https://<your-domain>/health to the container port 8000. Preserve the Authorization header and the request path.
/health is the health check path and must not require the bearer token. /mcp must require it. The server is stateless Streamable HTTP, so the proxy does not need sticky sessions.
The default bind address is 0.0.0.0, which accepts the Host header your proxy sends. Access to /mcp is still limited by the bearer token. If you set HOST to 127.0.0.1, the server only accepts localhost Host headers.
Do not put the Ads API key in proxy access logs. This process does not log that key or MCP_AUTH_TOKEN.
What you must change
Before anyone else can use a deployment, replace the placeholders:
Ads API key. Set
OPENAI_ADS_API_KEYto the key from your Ads Manager settings. Treat it as a secret.MCP bearer token. Set
MCP_AUTH_TOKENto a long random string you generate. This is not the Ads API key. Treat it as a secret.Domain. At your proxy, choose the hostname clients will use and turn on HTTPS so the proxy can obtain a certificate. This repository does not contain a domain.
Read-only or writes. Leave
MCP_READ_ONLY=trueuntil you intend to create or change ads. Set it tofalseand restart the process to register write tools.Do not commit secrets. Keep real values in the host environment or in an untracked
.env..env.exampleis only a template.
Coolify-specific clicks are in docs/coolify.md.
Tests
python -m pip install -e ".[dev]"
python -m pytestTests mock HTTP. They cover pagination, 429 backoff, secret redaction, both MCP_READ_ONLY modes, paused creates, archive confirmation, and bearer auth on /mcp.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted TikTok ads MCP with OAuth, bounded reads, and prepare/confirm writes.
Hosted Meta ads MCP with OAuth, bounded reads, and prepare/confirm writes.
Google Ads MCP server — manage campaigns, keywords, and metrics.
Run ads across 16 ad platforms from any MCP client. Hosted OAuth at mcp.synterai.com.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceMCP server for Meta/Facebook Marketing API allowing you to view and manage ad accounts, campaigns, ad sets, ads, and creatives, as well as fetch insights and upload ad images.MIT
- AlicenseBqualityBmaintenanceRead-only MCP server for Meta Ads that lists and reads ad accounts, campaigns, ad sets, ads, ad images, creatives, and fetches insights at various levels.14MIT
- FlicenseNot gradedqualityBmaintenanceMCP server for the Meta Marketing API enabling ad performance reports and operational adjustments like pause/resume and budget changes on client ad accounts, with strict allowlist and read-only safeguards.-
- FlicenseNot gradedqualityBmaintenanceA read-only MCP server for querying Meta Ads accounts through Meta's Marketing API. It provides tools to retrieve ad accounts, campaigns, ad sets, ads, and performance insights.-