Skip to main content
Glama
anhnguyen0905

Meta Ads MCP

README.md
# Meta Ads MCP

Meta Ads MCP is a minimal, read-only Python MCP server that pulls Meta
(Facebook) Ads campaign, ad-set, and ad performance so a human can spot
problems and adjust them in Ads Manager earlier. It never changes budgets,
statuses, or creatives.

## Tools

| Tool | Purpose |
| --- | --- |
| `list_ad_accounts` | List the Meta ad accounts available to the configured access token. |
| `get_campaigns` | Get campaigns for an explicit or configured default ad account. |
| `get_adsets` | Get ad sets beneath an account or campaign parent. |
| `get_ads` | Get ads beneath an account, campaign, or ad-set parent. |
| `get_insights` | Get raw Meta performance metrics for an object and reporting period. |
| `performance_summary` | Rank campaigns by spend and flag performance symptoms for review. |

The flags from `performance_summary` are **HEURISTIC** signals, not diagnoses.
Conversions and ROAS are platform-reported (Meta attribution), **not incremental**;
a holdout or lift test is required to claim incrementality.

## Prerequisites

- Python 3.10 or newer

## Meta credentials

1. Create a Meta app at [Meta for Developers](https://developers.facebook.com/).
2. Add the Marketing API to the app.
3. Generate a long-lived User access token with the `ads_read` permission and
   access to the required ad account.
4. Find the ad account ID in Ads Manager. It must use the `act_<digits>` form,
   such as `act_123456789`.

Meta's screens and access-token options can change. Follow the official
[Marketing API Get Started documentation](https://developers.facebook.com/docs/marketing-apis/)
for the current screens. Treat the access token as a secret.

## Quick setup

One command does the whole setup:

```console
python -m meta_ads_mcp setup
```

For the full walkthrough - creating the Meta app, redirect URI rules, giving
teammates access, and a troubleshooting table - see
[docs/SETUP.md](docs/SETUP.md).

It asks for the app id and app secret (the secret is never echoed), writes
`.env` with owner-only permissions, runs the browser login, lists the ad
accounts the token can read so you pick one from a numbered menu, and registers
the server in `claude_desktop_config.json` (backing up any existing file).
Restart Claude Desktop afterwards.

If `META_ACCESS_TOKEN` is already set in `.env`, setup offers to keep it and
skips the login entirely. Useful options:

| Option | Purpose |
| --- | --- |
| `--client-config PATH` | Patch a different MCP client config. |
| `--command-path PATH` | Command the client should launch. |
| `--env-file PATH` | Write to a different dotenv file. |

The manual route below is still supported if you would rather set each piece
yourself.

## Environment setup

Copy the template and fill in your credentials:

```powershell
Copy-Item .env.example .env
```

There are two ways to supply the token. Pick one.

### Route A - paste a token

```dotenv
META_ACCESS_TOKEN=your-long-lived-user-access-token
META_AD_ACCOUNT_ID=act_123456789
META_GRAPH_API_VERSION=v23.0
```

A **system user** token from Business Manager (Business Settings > Users >
System Users) never expires and needs no login flow. A manually generated user
token expires in about 60 days.

### Route B - browser login

Put the app credentials in `.env` and leave `META_ACCESS_TOKEN` blank:

```dotenv
META_APP_ID=your-app-id
META_APP_SECRET=your-app-secret
META_AD_ACCOUNT_ID=act_123456789
```

Then authorize in a browser:

```console
python -m meta_ads_mcp login
```

The command opens the Facebook Login dialog, receives the redirect on
`http://localhost:8721/callback`, exchanges the code for a long-lived token,
and caches it at `~/.local/share/meta-ads-mcp/token.json` (`%LOCALAPPDATA%`
equivalent via `XDG_DATA_HOME`) with owner-only permissions. The server picks
that cached token up automatically, so no token ever needs to sit in a config
file.

`http://localhost:8721/callback` must be listed under **Facebook Login >
Settings > Valid OAuth Redirect URIs** on the app. Change the port or scheme
with `META_OAUTH_REDIRECT_URI` if needed.

If Meta rejects a localhost redirect URI, use the paste flow instead - it uses
Meta's own desktop redirect page and needs no registered URI:

```console
python -m meta_ads_mcp login --paste
```

Log in, then copy the full URL from the browser address bar and paste it back
at the prompt.

Related commands:

| Command | Purpose |
| --- | --- |
| `python -m meta_ads_mcp status` | Show the cached token's expiry. |
| `python -m meta_ads_mcp logout` | Delete the cached token. |

Meta issues no refresh tokens, so a long-lived user token lasts about 60 days;
rerun `login` when `status` shows it near expiry. Only a system user token
(route A) avoids re-authorizing entirely.

`META_ACCESS_TOKEN` always wins over the cached token when both are present.
`META_GRAPH_API_VERSION` is optional and defaults to `v23.0`. The `.env` file
is git-ignored; never commit it.

## Install

Create a virtual environment and install the package with its development
dependencies:

```powershell
python -m venv .venv
.venv\Scripts\python.exe -m pip install -e ".[dev]"
```

If you already use `uv`, the install step can instead be:

```powershell
uv pip install -e ".[dev]"
```

`uv` is optional and is not required.

## Run

From an activated virtual environment, start the stdio server with:

```console
python -m meta_ads_mcp
```

The installed console entry point is also `meta-ads-mcp`. A stdio MCP server
waits on standard input, so it is normally launched by an MCP client rather
than run interactively.

## MCP client configuration

For Claude Desktop, add a server entry under `mcpServers` in
`claude_desktop_config.json`. Replace the example path and credentials:

```json
{
  "mcpServers": {
    "meta-ads": {
      "command": "C:\\Users\\your-name\\path\\to\\meta-ads-mcp\\.venv\\Scripts\\python.exe",
      "args": ["-m", "meta_ads_mcp"],
      "env": {
        "META_ACCESS_TOKEN": "your-long-lived-user-access-token",
        "META_AD_ACCOUNT_ID": "act_123456789"
      }
    }
  }
}
```

For Claude Code, put the equivalent configuration in the project-level
`.mcp.json`; it mirrors the Desktop configuration:

```json
{
  "mcpServers": {
    "meta-ads": {
      "command": "C:\\Users\\your-name\\path\\to\\meta-ads-mcp\\.venv\\Scripts\\python.exe",
      "args": ["-m", "meta_ads_mcp"],
      "env": {
        "META_ACCESS_TOKEN": "your-long-lived-user-access-token",
        "META_AD_ACCOUNT_ID": "act_123456789"
      }
    }
  }
}
```

The token and account ID can live in the client's `env` block instead of
`.env`. Keep client configuration containing a real token private.

If you used browser login (route B), omit `META_ACCESS_TOKEN` from the `env`
block entirely - the server reads the cached token from
`~/.local/share/meta-ads-mcp/token.json`. Leaving a placeholder string there
would override the cached token and every call would fail with an auth error.

## Graph API version

The server pins Graph API `v23.0` by default. If Meta deprecates that version,
set `META_GRAPH_API_VERSION` to a current supported version.

## Smoke test

After filling `.env`, run the manual real-network check:

```console
python scripts/smoke_test.py
```

It lists up to five accessible ad accounts and up to five campaigns from the
configured default account.

## Security

Never commit `.env` or paste the access token into logs, issues, or chat. The
server and all six tools are read-only.

The app secret is used only for the server-side code exchange and is never put
in a URL sent to the browser. The cached token file is written with `0600`
permissions inside a `0700` directory, and the login flow validates the OAuth
`state` value with a constant-time comparison before exchanging any code.

## Development

Run the test suite with:

```console
pytest
```

TDQS

A4.4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a distinct resource (ad accounts, campaigns, ads, ad sets, insights, performance summary) with clear boundaries and explicit 'do not use' guidance, eliminating ambiguity.

Naming Consistency4/5

Most tools follow a 'get_<resource>' pattern (get_campaigns, get_ads, get_insights, get_adsets), with one using 'list_' (list_ad_accounts) and another using a noun phrase (performance_summary), which is a minor inconsistency.

Tool Count5/5

The 6 tools are well-scoped for a read-only Meta Ads analytics server, covering account discovery, hierarchical retrieval, insights, and a summary function without being excessive.

Completeness3/5

The surface is read-only and covers listing and insights, but lacks any create, update, or delete capabilities, and misses tools for single-object retrieval or ad account details beyond listing.

Maintenance

ActivitySlowing
ResponsivenessNo issues