Get a working connection before touching a campaign
How to connect your AI agent to the official X Ads MCP
A connected server is not enough. Your agent also needs the right X app, callback, scopes, and ad-account access. This guide gets you to a verified read-only result first.
Verify the connection before you give an agent permission to spend
The frustrating version of X Ads automation is easy to picture: you finish the OAuth flow, your agent says the server is connected, and then it cannot find your ad account. Or it asks for write access before it has proved that it can read the right account.
The safer setup is read first, write later. Register the correct callback in your X developer app, connect https://ads-api.x.com/mcp with ads.read and offline.access, then ask the agent to list the ad accounts available to the signed-in X user. Only add write access after that check succeeds.
This guide uses the official remote X Ads MCP server. It works with MCP-capable agents, although the exact configuration screen or file depends on the client. The X documentation linked below was checked on September 17, 2026.
What you need before opening your AI agent
You need four things:
- an X user that can open the intended advertising account on
ads.x.com; - an app in the X Developer Console;
- that app assigned to the Ads Project under Project Access;
- an MCP client that supports remote servers and OAuth 2.0.
In the app settings, X currently instructs you to use Read and Write app permissions. For Claude Code, use a Native App (public client). Under Keys and tokens, copy the OAuth 2.0 Client ID. It is the long string, not the numeric App ID shown elsewhere in the console.
Do not put a client secret, access token, or refresh token in a prompt, screenshot, or tracked file. A Native App uses PKCE and does not need a client secret.
The official X Ads MCP setup page is the source of truth for the current callbacks, scopes, and client recipes. X documents 74 tools at the time of this review, but the tool count is less important than confirming that your agent can see the correct account.
1. Register the exact callback for your client
OAuth redirects only work when the callback sent by the client matches one registered in the X app. localhost and 127.0.0.1 are not interchangeable here.
| Client | Callback URI to register in X |
|---|---|
| Claude Code | http://localhost:8080/callback |
| Grok Build CLI | http://127.0.0.1:8080/callback |
| Grok on the web | https://grok.com/connectors-oauth-exchange-code/ |
| Custom client using the manual recipe | http://localhost:8080/callback |
X allows several callback URIs on one app, but its documentation warns that only one OAuth grant remains live for a given app and X user. Signing in through a second client can revoke the first client's tokens. If you want Claude Code and another agent connected at the same time, use a separate X app for each client.
2. Start with the smallest useful scopes
Use these two scopes for the first connection:
ads.read offline.access
ads.read lets the agent inspect accounts, campaigns, targeting, and available advertising results. offline.access lets the client refresh the token. Without it, the access token expires after roughly two hours and cannot be refreshed through the normal client flow.
Add scopes only when the workflow needs them:
| Scope | Add it when the agent needs to... |
|---|---|
ads.read | Read advertising accounts, settings, and results |
offline.access | Refresh the OAuth token after the initial sign-in |
ads.write | Create or change campaigns, line items, targeting, or promoted posts |
media.write | Upload media or change items in the X Ads media library |
Browsing media does not require media.write. Campaign management does not require it either. Keeping the first connection read-only removes an entire category of mistakes while you verify the account and reporting window.
3. Connect the official server
The endpoint is the same for every supported client:
https://ads-api.x.com/mcp
It is a remote MCP server using Streamable HTTP. Do not substitute the general X API MCP or the documentation MCP. They solve different jobs.
Claude Code
Register http://localhost:8080/callback in the X app, then add the server:
claude mcp add x-ads https://ads-api.x.com/mcp \
--transport http \
--client-id YOUR_OAUTH2_CLIENT_ID \
--callback-port 8080
Open /mcp inside Claude Code and complete the X consent flow. Claude Code does not expose a scope flag in this command. To keep the connection read-only, set the server entry in ~/.claude.json to use this OAuth scope string:
{
"oauth": {
"scopes": "ads.read offline.access"
}
}
Grok Build
Register http://127.0.0.1:8080/callback, then add this server to ~/.grok/config.toml:
[mcp_servers.ads-mcp]
url = "https://ads-api.x.com/mcp"
[mcp_servers.ads-mcp.oauth]
clientId = "YOUR_OAUTH2_CLIENT_ID"
callbackPort = 8080
scopes = ["ads.read", "offline.access"]
The OAuth keys are camelCase. In Grok, open /mcps, select ads-mcp, press i, and finish the browser consent.
Another MCP-capable agent
Point the client at the same endpoint and configure OAuth 2.0 with your Client ID, the callback URI actually used by that client, and the two read-only scopes. The client must support Streamable HTTP and the OAuth flow required by the server.
Do not assume a generic "add MCP" screen handles OAuth correctly. If the client only accepts a static authorization header, follow the manual token instructions in the official guide and use its secret storage. Static access tokens expire, so this path needs an explicit refresh process.
4. Prove that the agent can read the intended account
Do not start by asking for a campaign. Start with a result you can verify in Ads Manager.
Use the official X Ads MCP in read-only mode.
List every X Ads account available to the signed-in user. Return each account ID, name, timezone, currency, and approval status exactly as provided. Do not create or change anything.
After I choose an account ID, list its campaigns and show the reporting window and timezone used. Keep missing values separate from zero. Do not activate, pause, or edit any campaign.
The first request should call the account-reading tools, including list_ads_accounts. Once you select an account, the agent can use tools such as list_campaigns and get_account_stats. The official X Ads MCP reference maps every tool to its Ads API endpoint.
Compare the returned account ID and timezone with Ads Manager. A successful MCP handshake only proves that the client reached the server. It does not prove that the signed-in user has access to the advertising account you intended to use.
5. Fix the failure you actually have
Changing several settings at once makes OAuth problems harder to isolate. Use the symptom to choose the next check.
| Symptom | Check first |
|---|---|
| The browser reports a redirect or callback mismatch | Compare the registered callback character for character, including localhost versus 127.0.0.1, port, protocol, and trailing slash |
| The server returns 401 | The access token may be invalid or expired; reconnect or refresh it and confirm that offline.access was granted |
| The server returns 403 | Confirm that the app is assigned to the Ads Project and has X Ads access |
| Tools are visible but no ad account is returned | Sign in to ads.x.com with the same X user and confirm that it can open the intended account |
| Read tools work but write tools fail | The connection probably lacks ads.write; keep it read-only unless you are ready to authorize changes |
| One client stopped working after connecting another | X may have revoked the earlier grant; use one X app per client when both must remain connected |
| Claude Code fails with a confidential app | Use a Native App public client; the official guide says Claude Code's client authentication is rejected for confidential apps |
Do not regenerate every credential as a first response. Regenerating a key or token invalidates the old one and can break another working integration. Identify whether the failure is at the callback, OAuth grant, Ads Project enrollment, or account-access layer first.
6. Add write access only after the read test passes
When you are ready to let the agent prepare campaigns, reconnect with:
ads.read ads.write offline.access
Add media.write only if the agent must upload or modify media. Then give it an exact account ID, objective, currency, timezone, approved budget, dates, and allowed actions.
The official server creates campaigns and line items in a paused state. That is a useful default, but it is not a complete spending control. Activation is a separate action, and your approval process should name the exact campaign and line-item IDs before either is activated.
Use a read-back check after any write:
Create only the paused objects described in the approved brief. Do not activate them.
After creation, read back the saved campaign, line item, targeting, placements, budget, dates, timezone, and associated post. Compare each saved value with the brief. Return the object IDs and every mismatch. Stop if a required setting is unavailable instead of substituting another value.
For the complete workflow around objectives, organic post selection, measurement, and review, read How to run X Ads with AI.
Where PilotMyX fits
The official X Ads MCP handles advertising accounts and campaigns. PilotMyX gives your agent the organic account context needed before and after that work: which posts addressed the right problem, how your account usually sounds, and which approved posts are ready to publish or schedule.
Those sources should stay distinct. Use X Ads for spend, delivery, targeting, and paid results. Use PilotMyX to examine your own posts and replies, prepare organic copy, and schedule approved posts. Use website or product analytics for signups, purchases, or activation.
If PilotMyX is not connected yet, follow the PilotMyX setup guide for any MCP-capable agent. Then ask the agent to compare relevant organic candidates before you decide which published post deserves a paid test.
Connection checklist
Before you add write access, confirm all seven items:
- The signed-in X user can open the intended ad account.
- The developer app is assigned to the Ads Project.
- You copied the OAuth 2.0 Client ID, not the numeric App ID.
- The registered callback exactly matches the client.
- The first grant contains
ads.readandoffline.access, but notads.write. - The agent returned the expected account ID and timezone.
- A read-only campaign request completed without changing anything.
Once those checks pass, you have a connection worth building on. The next decision is not whether the agent can generate ad copy. It is which post fits the audience and outcome well enough to deserve a controlled paid test.
Use your own account