> ## Documentation Index
> Fetch the complete documentation index at: https://plainrouter.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Meta Ads MCP: connect your agent with Plainrouter

> Connect Claude Code to Plainrouter's Meta Ads MCP server, read signal health, and submit creative proposals for approval in one Meta ad account.

Connect Claude Code to Plainrouter MCP, verify a synthetic response, then configure a workspace execution token for your own Meta ad account. A successful production setup returns the workspace and account you selected when issuing the token.

For supported tasks, client requirements, and approval boundaries, start with the [MCP introduction](/docs/mcp/overview).

<CardGroup cols={2}>
  <Card title="Try synthetic data" icon="flask-conical" href="#start-in-test-mode">
    Check the client connection without a Plainrouter account or token.
  </Card>

  <Card title="Connect your account" icon="key-round" href="#connect-with-a-workspace-token">
    Configure your workspace token and confirm the first account read.
  </Card>
</CardGroup>

## Choose your MCP endpoint

| Endpoint                              | Use                                                      | Credential                                                |
| ------------------------------------- | -------------------------------------------------------- | --------------------------------------------------------- |
| `https://plainrouter.com/mcp/sandbox` | Prove a client against four synthetic tools.             | None                                                      |
| `https://plainrouter.com/mcp`         | Read one approved account and submit governed proposals. | Workspace execution token for tool calls and account data |

Plainrouter MCP uses owner-issued workspace execution tokens. Each token binds the client to one workspace, one Meta ad account, and an explicit access tier.

If you are choosing between a Signals workspace secret, workspace execution token,
or OAuth management credential, see [Authentication and
clients](/docs/api/authentication).

<Warning>
  OAuth management credentials cannot authenticate to the MCP server. They may read only `GET /api/v1/agent/context` for account discovery. Use a workspace token for every MCP tool call.
</Warning>

## Before you begin

For the sandbox, you only need a client with remote HTTP MCP support. For production, you also need:

* A Plainrouter account with access to the intended workspace.
* An active Meta ad account connection.
* An MCP-compatible client that can send a fixed bearer token to a remote HTTP server.
* A workspace owner who can issue the token.
* A clear choice of the one advertising account the client should use.

For creative-library reads, the Meta connection needs `ads_read` or `ads_management`. Governed creative execution needs `ads_management`.

## Start in test mode

In a terminal with [Claude Code](https://code.claude.com/docs/en/mcp) installed, add the test server:

```bash theme={null}
claude mcp add --transport http plainrouter-test https://plainrouter.com/mcp/sandbox
```

Open Claude Code in the same directory and run `/mcp` to check the connection. No Plainrouter account or credential is required. Other MCP clients can use the same URL with HTTP transport.

The endpoint exposes:

* `get_account_state`
* `get_signal_health`
* `get_performance`
* `validate_sandbox_event`

Every response is synthetic and carries `"sandbox": true`. Test mode reads no
tenant data, persists nothing, and contacts no advertising provider. It exposes
no proposal, write, approval, Launcher, or spend-affecting tool.

Ask the agent:

```text theme={null}
Use plainrouter-test to call get_account_state, then get_signal_health.
Summarize the synthetic account and signal health. Do not call other tools.
```

A successful account response includes the following fields. This is an excerpt from the synthetic response, not a real advertising account:

```json theme={null}
{
  "sandbox": true,
  "workspace": { "id": 0, "name": "Sandbox Workspace" },
  "ad_account": {
    "id": 0,
    "external_id": "act_SANDBOX",
    "name": "Sandbox Ad Account"
  }
}
```

Both calls should return without a tool error. This proves the test connection works; it does not verify your production account or event collection.

When the client can discover or initialize the server, list tools, and call a sandbox tool, change its
server URL to `https://plainrouter.com/mcp` and configure a workspace execution
token. The three read-tool names and their argument shapes match production.

<Note>
  The production endpoint exposes its protocol handshake, tool and resource
  catalogs, and static `ui://` app shells without a credential. Tool calls and
  account-data reads remain account-bound and return HTTP `401` without a valid
  workspace execution token.
</Note>

## Which MCP protocol should my client use?

Plainrouter supports `2026-07-28` through `server/discover`. Existing clients using `initialize` can negotiate `2025-11-25` or `2025-06-18`. Let your MCP client handle the protocol; the Claude Code configuration below does not need manual protocol fields.

For a custom HTTP client, start with this credential-free sandbox discovery request:

```bash theme={null}
curl https://plainrouter.com/mcp/sandbox \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/json, text/event-stream' \
  --header 'MCP-Protocol-Version: 2026-07-28' \
  --header 'Mcp-Method: server/discover' \
  --data '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "server/discover",
    "params": {
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'
```

Check that `result.supportedVersions` includes `2026-07-28` and `result._meta["io.modelcontextprotocol/serverInfo"].name` is `Plainrouter Sandbox`. Discovery confirms protocol compatibility; it does not read your account or verify a tool call.

For subsequent `2026-07-28` requests:

| Field or header        | Requirement                                                                            |
| ---------------------- | -------------------------------------------------------------------------------------- |
| `params._meta`         | Include both protocol version and client capabilities on each request.                 |
| `MCP-Protocol-Version` | Match the protocol version in `_meta`.                                                 |
| `Mcp-Method`           | Match the JSON-RPC `method`, such as `tools/list` or `tools/call`.                     |
| `Mcp-Name`             | For `tools/call`, match `params.name`. For `resources/read`, match `params.uri`.       |
| `Authorization`        | Include the workspace bearer token on every production tool call or account-data read. |

Requests are stateless: do not wait for or require an `Mcp-Session-Id` response header. Legacy clients without protocol metadata in `_meta` remain on the compatibility path; do not mix the two request formats. Sending either protocol metadata key selects the modern validation path.

The [published server card](https://plainrouter.com/.well-known/mcp/server-card.json) lists the current protocol, tools, and resources. The [transport upgrade reference](https://github.com/laravel/mcp/blob/cfa4f38f82873eeb6848527883545f98f871e229/UPGRADE.md) explains modern headers and legacy compatibility.

## Connect with a workspace token

<Steps>
  <Step title="Issue the token">
    In Plainrouter, switch to the intended workspace and open **API** in the dashboard. Choose the exact ad account, a Read, Write, or Admin tier, and an expiry.
  </Step>

  <Step title="Copy it once">
    Copy the complete token immediately after issuance. Plainrouter cannot display it again.
  </Step>

  <Step title="Add the Plainrouter MCP server">
    In your MCP client, add `https://plainrouter.com/mcp` as a remote HTTP server and configure the token as its bearer credential.
  </Step>

  <Step title="Confirm the bound account">
    Call `get_account_state` first. Confirm the returned workspace and account before continuing.
  </Step>
</Steps>

See [Workspace tokens](/docs/mcp/workspace-tokens) for tier selection, rotation, and revocation.

### Configure Claude Code for your Meta ad account

Add this server entry to your project's `.mcp.json`, preserving any existing servers. Claude Code [expands environment variables in MCP headers](https://code.claude.com/docs/en/mcp#environment-variable-expansion-in-mcp-json).

```json theme={null}
{
  "mcpServers": {
    "plainrouter": {
      "type": "http",
      "url": "https://plainrouter.com/mcp",
      "headers": {
        "Authorization": "Bearer ${PLAINROUTER_WORKSPACE_TOKEN}"
      }
    }
  }
}
```

Supply `PLAINROUTER_WORKSPACE_TOKEN` in the terminal environment through your local secret manager before starting Claude Code. Keep the variable reference in the file; do not replace it with the token or paste the token into an agent prompt. Open `/mcp` and allow the project connection when prompted.

Start with a **Read** token for account and library inspection. Use **Write** or **Admin** only when you need proposal-producing tools.

### Confirm a read-only production connection

Ask the agent:

```text theme={null}
Use plainrouter to call get_account_state. Show the workspace name and
Meta ad account name and external ID, then stop. Do not propose changes.
```

Check that the returned workspace and Meta ad account match the ones you selected when issuing the token. If they do, ask for `get_signal_health` to inspect conversion delivery and matching diagnostics. Missing Signals setup or measurement history is a separate setup issue; a successful account read does not establish healthy tracking.

For the returned fields and their meaning, see the [MCP tool reference](/docs/mcp/tools).

## Permissions

| Permission        | Allows                                                                                             |
| ----------------- | -------------------------------------------------------------------------------------------------- |
| `ad-account.read` | Read the approved account and its Plainrouter Signals context.                                     |
| `signals.verify`  | Write an identity-free, consent-safe Signal ingestion diagnostic. It does not complete onboarding. |
| `actions.propose` | Submit actions through workspace policy and the approval pipeline.                                 |
| `creative.read`   | Read the approved Meta account's creative library.                                                 |
| `creative.write`  | Stage creative assets and propose creative changes. It does not bypass `actions.propose`.          |

The agent cannot supply another account ID to widen access. Creative tools recheck that provider objects belong to the approved account.

<Warning>
  Creative write permission is not direct mutation authority. `upload-asset` and `duplicate-ad-with-creative` return a governed proposal. A later policy decision or human approval determines whether execution is queued.
</Warning>

## Recommended workflows

Once the account read succeeds, choose a task:

* [Diagnose Signals](/docs/mcp/tools#get_signal_health) with a stored read. Use [ingestion verification](/docs/mcp/tools#verify_signal_ingestion) only when you explicitly want a diagnostic write; it does not verify a real arrival.
* [Inspect stored reconciliation](/docs/mcp/tools#get_performance) and its evidence limits.
* [Create a paused creative variant](/docs/actions/creative-workflow): read the account library, select the source ad and asset, submit a proposal, and review the approval link.

Ask the agent to distinguish suggest-only, pending approval, blocked, awaiting verification, and Landed. The [Actions overview](/docs/actions/overview) lists supported changes.

### Example: request a creative proposal

After you select a source ad and an asset from the approved account's creative library, ask:

```text theme={null}
Use the source ad and asset I selected to propose a paused ad copy.
Summarize the proposed change and show its approval link. Stop for my review.
```

The creative tool submits a proposal to Plainrouter; it does not change Meta during that MCP call. In **Suggest only**, approval records agreement without execution. In an executable mode, supported creative work still requires policy checks and human approval. A new ad copy remains `PAUSED`.

Follow the [creative workflow](/docs/actions/creative-workflow) and [proposal review guide](/docs/actions/review-proposals) for the next steps. Use the [Actions overview](/docs/actions/overview) to check supported changes before requesting a different operation.

## Authorization lifetime and revocation

Workspace tokens expire after 30, 90, or 365 days. The owner can rotate or revoke them immediately from **API** in the dashboard.

Plainrouter also caps a token by the issuing person's current workspace role on every request. If that person's role no longer covers the token tier, the token stops authenticating at that tier. Issue a new token instead of trying to reuse a credential whose authority changed.

## Troubleshoot

### A custom client gets HTTP `400` or a protocol error

For JSON-RPC error `-32020`, compare `MCP-Protocol-Version`, `Mcp-Method`, and, where required, `Mcp-Name` with the request body. A missing required header or mismatched value fails before the tool runs. Ensure your reverse proxy preserves these headers.

For `-32022`, check the protocol version against discovery and use a supported version. Follow the [complete modern request format](#which-mcp-protocol-should-my-client-use); changing only the version header is not enough. An authentication `401` is a separate credential failure.

### The client waits for a session ID

Plainrouter processes requests independently and does not return `Mcp-Session-Id`. Update a client or custom transport that requires that header. Keep sending the workspace bearer credential on each production tool request; a successful discovery response does not authorize later calls.

### Claude Code cannot load the token variable

Confirm `PLAINROUTER_WORKSPACE_TOKEN` is set in the environment that starts Claude Code. Restart the client after supplying it. Keep the variable name consistent with `.mcp.json`; do not print the token to debug the connection.

### The client receives `401 Unauthorized`

Confirm the client sends the complete workspace token as a bearer credential and that it has not expired or been revoked. Also confirm its issuing person still holds a workspace role that covers the token tier.

If the client is sending an OAuth access token, the rejection is expected. Replace it with a workspace execution token.

### A creative tool reports a missing permission

Issue a token with the required tier. Use Read for library access and Write or Admin for proposal-producing creative tools and Launcher draft batches.

### Plainrouter asks you to reconnect Meta

The selected account may lack an active Meta connection or the required `ads_read` or `ads_management` access. Reconnect Meta, confirm the same ad account, and retry the same idempotent request.

### The wrong account appears

Revoke the token and issue another one for the correct advertising account. Tool calls do not accept an account override.

### Signals tools show setup required

The account may be authorized correctly while its workspace has no active Signal or destination. Complete [Signals setup](/docs/signals/overview) and call the tools again.
