Skip to main content
Glama
shinypebble

openai-ads-mcp

by shinypebble

openai-ads-mcp

An MCP server for the OpenAI Ads (ChatGPT Ads) Advertiser API, built for agent-led performance marketing. It exposes a small set of high-leverage, useful-work tools — find dead ads, audit near-duplicate copy, audit ad-group context hints, and (optionally) act on the findings — rather than a 1:1 mirror of the REST API.

Built with FastMCP, httpx, and Pydantic. Managed with uv, linted/formatted with ruff, type-checked with ty.

Quickstart

uv sync                       # create .venv and install
cp .env.example .env          # then set OPENAI_ADS_API_KEY
uv run python -m openai_ads_mcp     # run over stdio (default)

Or point an MCP client at it with fastmcp run src/openai_ads_mcp/server.py:mcp.

Related MCP server: synter-mcp-server

MCP client configuration

Real agents launch the server over stdio and pass configuration in the env block of the client's config — no .env file or shared working directory required. The examples below go in .mcp.json (Claude Code), claude_desktop_config.json (Claude Desktop), or the equivalent mcpServers block for any other client.

Project-scoped (agent runs inside the checkout)

This is the form in this repo's .mcp.json. ${CLAUDE_PROJECT_DIR:-.} resolves to the project root, so uv finds the right environment. The key comes from the ambient shell or a .env here — add it to env to be explicit.

{
  "mcpServers": {
    "openai-ads": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "--directory", "${CLAUDE_PROJECT_DIR:-.}", "python", "-m", "openai_ads_mcp"],
      "env": {
        "OPENAI_ADS_API_KEY": "sk-svcacct-...",
        "READ_ONLY": "false"
      }
    }
  }
}

From any directory (absolute path)

Most users run the agent somewhere other than this repo. Point --directory at an absolute path to the checkout and pass the key in env — this works regardless of the agent's cwd:

{
  "mcpServers": {
    "openai-ads": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "--directory", "/abs/path/to/openai-ads-mcp", "python", "-m", "openai_ads_mcp"],
      "env": {
        "OPENAI_ADS_API_KEY": "sk-svcacct-..."
      }
    }
  }
}

Installed console script

Install once with uv tool install /abs/path/to/openai-ads-mcp (or uv tool install git+<repo-url>), then reference the openai-ads-mcp script directly — no --directory needed:

{
  "mcpServers": {
    "openai-ads": {
      "type": "stdio",
      "command": "openai-ads-mcp",
      "env": {
        "OPENAI_ADS_API_KEY": "sk-svcacct-...",
        "READ_ONLY": "true"
      }
    }
  }
}

Passing the API key. Put it in the env block above — that's the standard MCP mechanism and pydantic-settings reads it straight from the process environment. There is intentionally no --api-key CLI flag: secrets in argv are visible to other users via ps and tend to leak into shell history and logs. Use env, or a .env file when the agent shares the checkout.

HTTP transport

To run the server once and connect over HTTP instead of spawning it per-client, start it with MCP_TRANSPORT=http (see the table below) and point the client at the URL:

{
  "mcpServers": {
    "openai-ads": { "type": "http", "url": "http://127.0.0.1:8000/mcp" }
  }
}

Configuration

All configuration is via environment variables — set them in the client's env block (above), the ambient shell, or a local .env:

Variable

Default

Purpose

OPENAI_ADS_API_KEY

— (required)

Advertiser API key; identifies the single ad account.

OPENAI_ADS_BASE_URL

https://api.ads.openai.com/v1

API base URL.

READ_ONLY

false

When true, write tools are not registered and cannot be called.

MCP_TRANSPORT

stdio

stdio or http.

MCP_HOST / MCP_PORT

127.0.0.1 / 8000

Bind address for http transport.

Read-only mode

Set READ_ONLY=true to run a safe, analysis-only server. The write tools are never registered, so they don't appear in the tool list and can't be invoked — there is no way for a client to bypass it. The default is false (writes enabled).

Tools

Read (always available)

Tool

What it does

account_health

Validate the key; report account currency, timezone, and read_only. Call first.

account_overview

Walk campaign → ad group → ad once; compact tree with statuses and counts.

ad_performance

Per-ad insights + conversions over a window, with CTR/CPC/CPM and a dead/underperforming/ok flag. The core "find dead ads" tool.

copy_audit

Find near-duplicate titles/bodies and length/guidance issues in a scope.

context_hints_audit

Flag ad groups with missing or thin context_hints.

get_insights

Escape hatch for raw insight rows at any aggregation level.

get_campaign / get_ad_group / get_ad

By-id reads, including serving_issues (which list calls omit).

Write (only when READ_ONLY=false)

Tool

What it does

pause_ads / archive_ads

Bulk pause (reversible) / archive (irreversible, needs confirm=true), error-isolated per id.

set_status

Single activate/pause/archive transition on a campaign, ad group, or ad.

update_ad_copy

Update an ad's creative copy (re-sends the full creative for you).

create_ad_variant

Upload a creative image and create a new ad in one step.

create_campaign / create_ad_group

Create entities with flattened, agent-friendly params.

update_campaign / update_ad_group

Update entities (budget/targeting/bidding/context hints).

A sibling agent skill at skill/openai-ads-optimizer/SKILL.md teaches an agent how and when to use these tools, plus the OpenAI Ads creative guidance. The server also serves this skill as an MCP resource (skill://openai-ads-optimizer/SKILL.md), so any connected client can discover the playbook without a local copy.

Architecture

tools/      → MCP layer (@mcp.tool wrappers, READ_ONLY gate)
services/   → orchestration (hierarchy walk, performance, audits, mutations)
domain/     → pure data shapes + business rules (entities, insights, thresholds, copy audit)
api/        → async httpx client (auth, retry, pagination, errors)

The client mirrors the API's real semantics: reads (and the conversions POST, which has read semantics) are retried on transient failures; other writes are never retried. It also handles the API's quirks — no global ad list, until = today rejection, conversions on a separate endpoint, decimal spend vs. micros bids.

Development

bash scripts/ci.sh     # ruff check + format check + ty + pytest

Requires Python 3.14 and uv.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/shinypebble/openai-ads-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server