# AI access (MCP)



## How it stays safe

- **Drafts first.** Every change an assistant makes is saved as a draft. A draft is invisible to your visitors until a person publishes it, in the panel under *AI access* or, if you allowed it, by the assistant after you said yes.
- **You choose the permissions** every time an app connects: *read*, *prepare drafts* and *publish*. Publishing is off by default.
- **Normal sign-in.** Connecting uses the panel login, including lockout after failed attempts and two-factor authentication. Only staff accounts can connect an app.
- **Nothing is deleted.** Assistants can create and change advertisers, campaigns, ads and zones. They cannot delete anything and cannot upload image or video files.
- **Revocable.** Under *AI access → Connected apps* you can cut an app off at any time.

## Switch it on

1. Make sure `ADSERVER_BASE_URL` in your `.env` is the public **https** address of this server. Cloud assistants call it from the internet, so `localhost` does not work (use a tunnel for tests).
2. Set `MCP_ENABLED=true` and restart. *AI access* in the panel now shows your connection address.
3. Apps from `claude.ai`, `claude.com`, `chatgpt.com` and from your own computer (`localhost`) may register themselves. Add more hosts with `MCP_ALLOWED_REDIRECT_HOSTS` (comma separated).

## Connect an app

- **Claude** (claude.ai, desktop, mobile): Settings → Connectors → Add custom connector → paste the address from *AI access*.
- **ChatGPT:** Settings → Apps → create an app with OAuth and paste the address.
- **Claude Code:** `claude mcp add --transport http adserver https://your-server/mcp`, then run `/mcp` and sign in.

A browser window opens. Sign in, read what the app asks for, untick what you do not want to allow, and press *Allow*.

## What an assistant can do

Tools by permission

| Permission | Tools |
| --- | --- |
| read | `list_advertisers`, `list_campaigns`, `get_campaign`, `list_ads`, `list_zones`, `get_stats` |
| prepare drafts | `save_draft` (create or change an advertiser, campaign, ad or zone; a campaign can carry zone assignments and ads), `list_drafts`, `discard_draft` |
| publish | `publish_draft`: makes a draft live. The assistant is instructed to call it only after you explicitly agreed to that draft. |

Drafts follow the same rules as the forms in the panel, so a draft with an invalid budget or date is rejected with a clear message. When you publish, the draft is checked again against the current data, and fields you changed in the meantime are kept.

## Troubleshooting

- **The app cannot connect.** Open `/.well-known/oauth-protected-resource` on your server from outside. It must answer with JSON, over https, at the address in `ADSERVER_BASE_URL`.
- **"Redirect URIs must be…"** The app's host is not on the list. Add it to `MCP_ALLOWED_REDIRECT_HOSTS`.
- **The sign-in page shows up twice.** Panel cookies are strict (`SameSite=Strict`), so a browser may ask you to sign in once more when the app opens the consent page from another site. That is expected.
- **The app cannot publish.** You did not tick *publish* on the consent screen. Disconnect the app and connect it again, or publish in the panel.
