Skip to main content
Glama
FarahTrip

reddit-ads-mcp

by FarahTrip
README.md
# reddit-ads-mcp

A small, self-hosted [MCP](https://modelcontextprotocol.io) server for the **Reddit Ads API v3**, in Python.

- **Read** accounts, campaigns, ad groups, ads, and performance reports.
- **Write** (opt-in) — pause/activate, set budgets, patch fields, create entities.
- **No third party.** Auth is a direct OAuth2 refresh-token exchange with reddit.com using *your* app credentials. Nothing is proxied anywhere.
- **Safe by default.** Mutations are refused unless `REDDIT_ADS_WRITE_ENABLED=true`.

> Inspired by the read-only C# [RedditAdsMcp](https://github.com/mkerchenski/RedditAdsMcp); rewritten in Python with opt-in write support and a self-hosted token helper.

**Status:** reads + the core small-writes (status / budget / field patch) are **verified
against the live Reddit Ads API v3** (2026-06-30). `create_*` are wired but not yet
validated end-to-end — marked experimental below.

## Tools

| Tool | Access | Description |
|------|--------|-------------|
| `list_accounts` | read ✓ | All ad accounts reachable with your credentials |
| `list_campaigns` | read ✓ | Campaigns for an account |
| `list_ad_groups` | read ✓ | Ad groups, optionally by `campaign_id` |
| `list_ads` | read ✓ | Ads, optionally by `ad_group_id` |
| `get_performance_report` | read ✓ | Custom date range / fields / breakdowns |
| `get_daily_performance` | read ✓ | Last N days by DATE + CAMPAIGN_ID |
| `set_campaign_status` / `set_ad_group_status` | write ✓ | Pause / activate / archive |
| `set_campaign_budget` / `set_ad_group_budget` | write ✓ | Daily budget in dollars (auto micro-conversion) |
| `update_campaign` / `update_ad_group` | write ✓ | Patch arbitrary fields |
| `create_campaign` / `create_ad_group` | write ⚠️ | Create entities (experimental — unvalidated) |

Write tools are only registered when `REDDIT_ADS_WRITE_ENABLED=true`.

### API notes (learned from the live v3 API)
- **Collections are nested, single entities are top-level:** list/create use
  `ad_accounts/{id}/campaigns`, but get/update use `campaigns/{id}` (no account prefix).
- **Budgets are micro-currency:** `goal_value: 6000000` = $6.00, `goal_type: "DAILY_SPEND"`.
  The `set_*_budget` tools take plain dollars and convert for you.
- **Reports are a read-only `POST`** to `ad_accounts/{id}/reports` with a `{data:{…}}` body.
- **Write OAuth scope is `adsedit`** (not `adswrite`).

## Setup

### 1. Create a Reddit Ads API app
[ads.reddit.com](https://ads.reddit.com) → **Developer Applications** → **Create a new app**.
Set the **redirect URI** to any HTTPS URL **you control** — it only needs to show the `code`
query param Reddit appends after you click Allow. Copy the **App ID** and **Secret**.

### 2. Get a refresh token (one-time)
Open this URL (URL-encode your redirect; drop `%20adsedit` for read-only):

```
https://www.reddit.com/api/v1/authorize?client_id=YOUR_APP_ID&response_type=code&state=mcp&redirect_uri=YOUR_REDIRECT&duration=permanent&scope=adsread%20adsedit
```

Click **Allow**, copy the `code` from the redirected URL, then:

```bash
python scripts/get_refresh_token.py \
  --client-id YOUR_APP_ID --client-secret YOUR_SECRET \
  --code THE_CODE --redirect-uri YOUR_REDIRECT
```

It prints your permanent `REDDIT_ADS_REFRESH_TOKEN`.

### 3. Find your account ID
[ads.reddit.com](https://ads.reddit.com) → account dropdown — it looks like `a2_xxxxxxxx`.

### 4. Configure your MCP client
With [uv](https://docs.astral.sh/uv/) (no global install needed):

```json
{
  "mcpServers": {
    "reddit-ads": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/reddit-ads-mcp", "python", "-m", "reddit_ads_mcp"],
      "env": {
        "REDDIT_ADS_CLIENT_ID": "your_app_id",
        "REDDIT_ADS_CLIENT_SECRET": "your_secret",
        "REDDIT_ADS_REFRESH_TOKEN": "your_refresh_token",
        "REDDIT_ADS_ACCOUNT_ID": "a2_xxxxxxxx",
        "REDDIT_ADS_WRITE_ENABLED": "false"
      }
    }
  }
}
```

Flip `REDDIT_ADS_WRITE_ENABLED` to `"true"` only when you want mutations (and only if the
refresh token was granted the `adsedit` scope).

## Development

```bash
uv sync
uv run python -m reddit_ads_mcp   # starts the stdio server (needs env vars)
```

## License

MIT © Sorin Mihailescu

TDQS

B3.4/5.0

Scored across 6 tools

Disambiguation4/5

The list tools are clearly distinct by entity, but the two performance tools (get_daily_performance vs get_performance_report) overlap in purpose. Although one is for recent days and the other for custom date ranges, an agent might misselect them without careful description reading.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern using snake_case (get_*, list_*). The naming is predictable and clear.

Tool Count5/5

With 6 tools, the set is well-scoped for an ad management server. It covers essential entities (accounts, campaigns, ad groups, ads) and reporting, without unnecessary redundancy.

Completeness2/5

The tool surface is read-only: only listing and reporting. Missing write operations (create, update, delete) for campaigns, ads, etc., which are critical for actual ad management. Agents cannot perform any operational tasks.

Maintenance

ActivityInactive
ResponsivenessNo issues