Skip to main content
Glama
AdvisorPPC X Ads MCP Server

AdvisorPPC X Ads MCP Server

AdvisorPPC X Ads

Policy-safe X (Twitter) Ads for MCP clients — Claude Code, Cursor, Grok, VS Code, or any Streamable HTTP host.

This is the operator connector reverse-engineered from Grok’s first-party X Ads tools (27 curated tools) and mapped onto Ads API v12, plus audience/pixel/DNR and chunked video that Grok’s 27 omit. Writes default to PAUSED. Spend, pause, and delete require an explicit confirm flag after a named user ask.

Website · Tasks · Reverse-engineering · MCP v2 notes · Scheduler

What you get

Layer

Detail

Protocol

MCP 2026-07-28 (SDK v2 @modelcontextprotocol/server)

Transports

stdio (local) and Streamable HTTP POST /mcp

UI

MCP Apps dashboard + scheduler (ui://advisorppc/x-ads/dashboard, …/scheduler)

API

https://ads-api.x.com/12 with OAuth2 Bearer (ads.read / ads.write)

Safety

Paused-by-default, confirm_spend / confirm, no creative substitution, budget on the ad group

Scheduler

AdvisorPPC queue (X has none). HTTP auto-starts the worker. Import @advisorppc/x-ads/schedule in the backend.

Related MCP server: mcp-server-reddit-ads

Tools (51)

Read: x_ads_list_accounts · x_ads_list_funding · x_ads_list_campaigns · x_ads_list_line_items · x_ads_list_ads · x_ads_list_targeting · x_ads_list_audiences · x_ads_audience_targeted · x_ads_list_dnr · x_ads_list_pixels · x_ads_get_pixel · x_ads_list_creatives · x_ads_get_tweets · x_ads_get_cards · x_ads_get_media · x_ads_search_targeting · x_ads_estimate_audience · x_ads_active_entities · x_ads_get_analytics · x_ads_reach

Write structure: x_ads_create_campaign · x_ads_create_ad_group · x_ads_update_campaign · x_ads_update_ad_group · x_ads_set_status · x_ads_add_targeting · x_ads_delete

Audiences & DNR: x_ads_create_audience · x_ads_update_audience · x_ads_delete_audience · x_ads_audience_users · x_ads_create_dnr · x_ads_delete_dnr · x_ads_dnr_users

Pixels (web event tags): x_ads_create_pixel · x_ads_update_pixel · x_ads_delete_pixel

Write creative: x_ads_upload_media (images + chunked video) · x_ads_create_card · x_ads_create_tweet · x_ads_create_ad · x_ads_create_image_ad · x_ads_create_video_ad

Scheduler / agents: x_ads_scheduler_setup · x_ads_scheduler_status · x_ads_scheduler_settings · x_ads_schedule_list · x_ads_schedule_create · x_ads_schedule_cancel · x_ads_agents_list · x_ads_agent_set

Install

git clone https://github.com/atmosphere-ai/advisorppc-x-ads
cd advisorppc-x-ads
npm install
cp .env.example .env   # set X_ADS_ACCESS_TOKEN
npm run build

Token: an X developer app with scopes ads.read, ads.write, offline.access (and media.write if you upload). Auth URL https://x.com/i/oauth2/authorize, token URL https://api.x.com/2/oauth2/token. PKCE S256; X rejects client_secret_post.

Claude Code / Cursor (stdio)

{
  "mcpServers": {
    "advisorppc-x-ads": {
      "command": "node",
      "args": ["/absolute/path/to/advisorppc-x-ads/dist/index.js"],
      "env": { "X_ADS_ACCESS_TOKEN": "…", "ADVISORPPC_SCHEDULER": "1" }
    }
  }
}

Or the plugin path: claude plugin marketplace add atmosphere-ai/advisorppc-x-ads then install advisorppc-x-ads@advisorppc.

Streamable HTTP

npm run start:http
# POST http://127.0.0.1:3333/mcp
# GET  http://127.0.0.1:3333/scheduler
# Authorization: Bearer <token>  (overrides env)

Grok web custom connector: server URL of your hosted /mcp, PKCE, scopes ads.read ads.write offline.access.

Grok Build CLI

[mcp_servers.advisorppc-x-ads]
command = "node"
args = ["/absolute/path/to/dist/index.js"]

[mcp_servers.advisorppc-x-ads.env]
X_ADS_ACCESS_TOKEN = "…"
ADVISORPPC_SCHEDULER = "1"

Hierarchy (do not skip)

account → funding instrument → campaign → ad group (line item)
                                      ├─ targeting
                                      └─ ad (promoted tweet → tweet → card → media_key)

Budget is on the ad group. Campaign create does not take a budget. An ad serves only when campaign and ad group are both ACTIVE.

Safety

Action

Default

To override

Create campaign / ad group

PAUSED

entity_status=ACTIVE and confirm_spend=true after an explicit “start/go live”

Pause / resume / delete

refused

confirm=true after the user named the entity

Failed creative

stop

never promote a substitute

Invented name or budget

forbidden

user supplies it

Schedule

AdvisorPPC queue

x_ads_scheduler_setup then schedule_create (confirm=true). HTTP worker auto-starts.

Skills (bundled)

Skill

When to use

getting-connected

Auth, tokens, first list_accounts

x-ads-operator

Playbooks (audit, analytics, create image ad, schedule pause/resume)

mcp-building

How this server is built on MCP v2

mcp-apps

Inline dashboard / ui:// resources

Scheduler (built-in)

X cannot natively schedule. Call x_ads_scheduler_setup once — it starts the worker and returns paste-ready configs for Claude, ChatGPT, Cursor, Grok, and the AdvisorPPC backend (import { createScheduler } from "@advisorppc/x-ads/schedule").

Agents: publish_queue (fires due jobs), analytics_digest / paused_audit (snapshots + optional webhook, never auto-resume spend), health. Allowlisted jobs: set_status, update_campaign, update_ad_group, delete. Details: docs/SCHEDULER.md.

Develop

npm test
npm run typecheck
npm run dev          # stdio
npm run dev:http
npm run worker

What this is not

  • Not a 1:1 clone of X’s official 74-tool MCP at https://ads-api.x.com/mcp (app lists, tweet previews, app event tags, and tracking-partner tags still live there).

  • Not a native X scheduler. The built-in queue is AdvisorPPC's.

  • Not organic posting — that is atmosphere-ai/advisorppc-x-organic.

  • Not a Google Ads connector — that is advisorppc-org/advisorppc-plugin → https://mcp.advisorppc.com/claude.

License

MIT for this repository. The AdvisorPPC name and hosted service remain Advisor Media.

Available Tools

51 tools
x_ads_active_entitiesActive entitiesA
Read-only

Which CAMPAIGN / LINE_ITEM / PROMOTED_TWEET ids had activity in a window. Call before analytics so you don't request empty ids. Times are whole-hour ISO 8601.

Inputs: entity (required), end_time (required), account_id (required), start_time (required).

x_ads_add_targetingAdd targetingA

Add targeting on an ad group. criteria: {targeting_type, targeting_value, operator_type?}. Types: LOCATION, CUSTOM_AUDIENCE, FOLLOWER_LOOK_ALIKE, INTEREST, LANGUAGE, PLATFORM, DEVICE, GENDER, AGE, EVENT, KEYWORD. GENDER=MALE/FEMALE, AGE like AGE_25_TO_34. operator EQ include (default) or NE exclude. replace=true removes existing criteria of the SAME types first. Only set targeting the user asked for.

Inputs: criteria (required), account_id (required), line_item_id (required), replace.

x_ads_agent_setEnable or configure an agentA

Turn an agent on/off and set every_ms / account_id. Live agents (if any) need confirm=true. analytics_digest / paused_audit never change status.

Inputs: agent_id (required), confirm, enabled, every_ms, +1 more.

x_ads_agents_listList scheduler agentsA
Read-only

Built-in agents: publish_queue, analytics_digest, paused_audit, health. Digests never auto-resume spend; they snapshot and optionally POST webhook_url for Claude/ChatGPT/AdvisorPPC.

Inputs: no named parameters.

x_ads_audience_targetedWhere audience is targetedB
Read-only

Campaigns and ad groups currently targeting a custom audience.

Inputs: account_id (required), custom_audience_id (required), with_active.

x_ads_audience_usersAdd/remove audience usersA

Add (Update) or remove (Delete) members of a custom audience. Pass raw emails/phones/handles — this server SHA-256 hashes after X normalization (lowercase email, strip @, digits-only phone). Set hashed=true only if values are already hex SHA-256. Max 2500 users per call. Never log the raw list.

Inputs: users (required), account_id (required), custom_audience_id (required), hashed, +1 more.

x_ads_create_adPromote postA

Promote an EXISTING post under an ad group. Ad serves only if group AND campaign are ACTIVE. For a brand-new image use x_ads_create_image_ad. If the intended creative failed to upload or process, STOP. Do not promote a still, a source image, or a pre-existing library asset.

Inputs: tweet_id (required), account_id (required), line_item_id (required).

x_ads_create_ad_groupCreate ad groupA

Create a line item under a campaign. PAUSED by default. All groups in a campaign must share objective and product_type. Budget is set HERE (daily_budget_amount_local_micro / total_budget_amount_local_micro). Bid omitted = automatic. Amounts are local micro-units (1 unit = 1_000_000). If creating multiple groups from one stated budget, ASK split vs each. confirm_spend required for ACTIVE.

Inputs: objective (required), account_id (required), campaign_id (required), name, +8 more.

x_ads_create_audienceCreate custom audienceA

Create an empty CRM custom audience. Then load members with x_ads_audience_users (SHA-256 hashed). Name must come from the user. targetable stays false until it is large enough.

Inputs: name (required), account_id (required), description.

x_ads_create_campaignCreate campaignA

Create a campaign PAUSED by default (no spend). Pass entity_status=ACTIVE only with confirm_spend=true after an explicit user ask. Needs funding_instrument_id. BUDGETS ARE SET PER AD GROUP — do not pass a campaign budget. Don't invent names.

Inputs: name (required), account_id (required), funding_instrument_id (required), confirm_spend, +1 more.

x_ads_create_cardCreate cardA

Create an ad card from raw components. Returns card_uri. Website: [{"type":"MEDIA","media_key":"…"},{"type":"DETAILS","title":"…","destination":{"type":"WEBSITE","url":"https://…"}}]. App cards use BUTTON not DETAILS. Carousel uses SWIPEABLE_MEDIA. If the intended creative failed to upload or process, STOP. Do not promote a still, a source image, or a pre-existing library asset.

Inputs: account_id (required), components (required), name.

x_ads_create_dnrCreate Do Not Reach listA

Create the account DNR (suppression) list. One per account; API names it 'Do Not Reach List'. Optional description only. Then load emails with x_ads_dnr_users.

Inputs: account_id (required), description.

x_ads_create_image_adCreate image ad (pipeline)A

Composite: upload creative → website card → nullcast post → promote onto an EXISTING ad group. Provide media_url or inline image. Requires text + destination_url. Serves only when campaign and ad group are ACTIVE. If the intended creative failed to upload or process, STOP. Do not promote a still, a source image, or a pre-existing library asset.

Inputs: text (required), account_id (required), line_item_id (required), destination_url (required), +3 more.

x_ads_create_pixelCreate X Pixel (web event tag)A

Create a conversion / site-visit web event tag. type default SITE_VISIT. click_window and view_through_window in days (0,1,7,14,30,60,90). retargeting_enabled default false. Returns embed_code + website_tag_id. Don't invent names.

Inputs: name (required), account_id (required), type, click_window, +2 more.

x_ads_create_tweetCreate ad postA

Create a promoted-only (nullcast) post. media_keys XOR card_uri. Returns tweet id; promote with x_ads_create_ad. nullcast default true. If the intended creative failed to upload or process, STOP. Do not promote a still, a source image, or a pre-existing library asset.

Inputs: text (required), account_id (required), card_uri, nullcast, +2 more.

x_ads_create_video_adCreate video ad (pipeline)A

Composite: chunked video upload (amplify_video) → website card (or media_keys if no destination_url) → nullcast post → promote onto an EXISTING ad group. Provide media_url or inline video (base64). Requires text. If the intended creative failed to upload or process, STOP. Do not promote a still, a source image, or a pre-existing library asset.

Inputs: text (required), account_id (required), line_item_id (required), name, +3 more.

x_ads_deleteDelete entityA
Destructive

SOFT-delete CAMPAIGN, LINE_ITEM, or PROMOTED_TWEET. Children go with the parent. Cannot be undone here. Deleting an ad does not delete the post. confirm=true after the user named the exact entity. Never inferred.

Inputs: entity (required), entity_id (required), account_id (required), confirm.

x_ads_delete_audienceDelete custom audienceA
Destructive

Soft-delete a custom audience. Line items targeting it will stop matching. confirm=true after the user named the audience.

Inputs: account_id (required), custom_audience_id (required), confirm.

x_ads_delete_dnrDelete Do Not Reach listA
Destructive

Delete the account DNR list. confirm=true after the user named it. Suppression stops.

Inputs: account_id (required), do_not_reach_list_id (required), confirm.

x_ads_delete_pixelDelete X PixelA
Destructive

Delete a web event tag. Conversion tracking for that event stops. confirm=true after the user named it.

Inputs: account_id (required), web_event_tag_id (required), confirm.

x_ads_dnr_usersAdd/remove DNR usersA

Add (Update) or remove (Delete) emails on the Do Not Reach list. Emails only. Raw emails are SHA-256 hashed here. hashed=true if already hex SHA-256. expires_at optional ISO 8601 (must be < 13 months).

Inputs: emails (required), account_id (required), do_not_reach_list_id (required), hashed, +2 more.

x_ads_estimate_audienceEstimate audience sizeA
Read-only

Estimate reachable unique users for a targeting set BEFORE spending. criteria same shape as x_ads_add_targeting. Does not create anything.

Inputs: criteria (required), account_id (required).

x_ads_get_analyticsGet analyticsA
Read-only

Sync stats for up to 20 campaigns, ad groups, or ads. Range must be ≤ 7 days. Times whole-hour ISO 8601. granularity TOTAL (default) / DAY / HOUR. metric_groups default ENGAGEMENT,BILLING. Placement ALL_ON_TWITTER. Spend is billed_charge_local_micro / 1_000_000.

Inputs: entity (required), end_time (required), account_id (required), entity_ids (required), +3 more.

x_ads_get_cardsGet cardsA
Read-only

Expand card_uri values (card://…) into type, components, buttons, media_keys. Images inside a card are media_keys, not URLs — pass those to x_ads_get_media.

Inputs: card_uris (required), account_id (required).

x_ads_get_mediaGet media URLsA
Read-only

Resolve media_key values (e.g. 3_2069…) to media_url / poster_media_url from the Media Library.

Inputs: account_id (required), media_keys (required).

x_ads_get_pixelGet X PixelB
Read-only

Fetch one web event tag: type, windows, retargeting, embed_code, website_tag_id.

Inputs: account_id (required), web_event_tag_id (required).

x_ads_get_tweetsGet ad postsA
Read-only

Fetch posts behind ads (tweet_id from x_ads_list_ads) WITH card_uri and media. Use this, not a generic post fetch, to tell whether an ad has a card/image. Up to ~200 ids. tweet_type default PUBLISHED (includes promoted-only).

Inputs: tweet_ids (required), account_id (required), tweet_type.

x_ads_list_accountsList ad accountsA
Read-only

List X ad accounts the connected token can manage. Returns id, name, approval status. Call this first.

Inputs: no named parameters.

x_ads_list_adsList adsA
Read-only

List promoted posts. Each row has status and tweet_id. Filter with line_item_id. Deleted ads are hidden unless with_deleted=true; the underlying post is not deleted.

Inputs: account_id (required), count, cursor, line_item_id, +1 more.

x_ads_list_audiencesList custom audiencesA
Read-only

List tailored audiences with size and targetable status. Use ids as CUSTOM_AUDIENCE targeting values.

Inputs: account_id (required), count, cursor.

x_ads_list_campaignsList campaignsB
Read-only

List campaigns: status (ACTIVE/PAUSED/DRAFT) and any budgets. Requires account_id.

Inputs: account_id (required), count, cursor, with_deleted.

x_ads_list_creativesList creativesA
Read-only

Creative inventory: cards (cap 200) and media library (cap 50) in one call. Page with cards_cursor / media_cursor.

Inputs: account_id (required), count, cards_cursor, media_cursor.

x_ads_list_dnrList Do Not Reach listsA
Read-only

Account-level exclusion list (suppression). An account can have at most one DNR list. Excludes those users from ALL campaigns on the account; it does not strip them from custom audiences.

Inputs: account_id (required), count, cursor.

x_ads_list_fundingList funding instrumentsA
Read-only

How campaigns are paid. Trust able_to_fund=true and empty reasons_not_able_to_fund — NOT description, which for self-serve cards can read '(no payment method has been set up yet)' even when serving. The API exposes one representative instrument for self-serve accounts.

Inputs: account_id (required), count, cursor.

x_ads_list_line_itemsList ad groupsA
Read-only

List line items (ad groups): status, objective, bid. Optionally filter by campaign_id. Budget lives HERE, not on the campaign.

Inputs: account_id (required), count, cursor, campaign_id, +1 more.

x_ads_list_pixelsList X Pixels (web event tags)B
Read-only

List Universal Website Tags / conversion pixels. id is the web_event_tag_id; website_tag_id is the pixel id used in embed code and Conversion API (tw-{website_tag_id}-…). embed_code is the snippet to put on the site.

Inputs: account_id (required), count, cursor.

x_ads_list_targetingList targetingA
Read-only

List targeting criteria for an ad group. Always pass line_item_id to see why groups differ.

Inputs: account_id (required), count, cursor, line_item_id.

x_ads_reachCampaign reachA
Read-only

Unique reach + average frequency for campaigns. Window ≤ 7 days, whole-hour ISO 8601. Impressions cannot tell you this.

Inputs: end_time (required), account_id (required), start_time (required), campaign_ids (required).

x_ads_schedule_cancelCancel, pause, or resume a jobA
Destructive

Cancel (terminal), pause, or resume a named job id. confirm=true after the user named the job.

Inputs: job_id (required), action, confirm.

x_ads_schedule_createSchedule a jobA

Enqueue a future ads action on AdvisorPPC's queue (X cannot natively schedule). tool must be allowlisted: set_status, update_campaign, update_ad_group, delete. arguments must be user-supplied ids/status — never invent entities. confirm=true after they named the time and the entity. Enables publish_queue. Kinds: once (run_at ISO), every (every_ms), cron (5-field).

Inputs: kind (required), tool (required), arguments (required), cron, +6 more.

x_ads_schedule_listList scheduled jobsA
Read-only

List AdvisorPPC queue jobs (not X native). Filter by status.

Inputs: status.

x_ads_scheduler_settingsScheduler settingsC

Get or patch timezone, digest webhook_url (Claude/ChatGPT/AdvisorPPC callback), tick_ms, public_url.

Inputs: tick_ms, timezone, public_url, webhook_url.

x_ads_scheduler_setupAuto-setup schedulerA

Self-configure the AdvisorPPC scheduler for Claude, ChatGPT, Cursor, Grok, raw HTTP, or the AdvisorPPC backend. Starts the in-process worker, writes the job store, returns paste-ready MCP snippets. Does not invent campaigns or spend.

Inputs: vendor, timezone, account_id, public_url, +1 more.

x_ads_scheduler_statusScheduler statusA
Read-only

AdvisorPPC's own job queue (Ads API has no native schedule). Shows worker, agents, upcoming jobs, last runs. HTTP mode starts the worker by itself.

Inputs: no named parameters.

x_ads_search_targetingSearch targeting valuesA
Read-only

Resolve human terms into targeting_value ids for x_ads_add_targeting. kind ∈ locations, interests, languages, platforms, devices, events, app_store_categories, conversations, network_operators, tv_markets, tv_shows. For locations pass query and optional location_type. Custom audiences: use x_ads_list_audiences.

Inputs: kind (required), count, query, location_type.

x_ads_set_statusPause or resumeA

Set a campaign or ad group to ACTIVE or PAUSED. Pausing a campaign stops its groups and ads. Ads have no on/off switch — pause the group or delete the ad. confirm=true after an explicit named ask. Never inferred.

Inputs: entity (required), status (required), entity_id (required), account_id (required), +1 more.

x_ads_update_ad_groupUpdate ad groupA

Partial update of name, bid, and/or budget micros. Only change what the user named. confirm=true for money fields.

Inputs: account_id (required), line_item_id (required), name, confirm, +3 more.

x_ads_update_audienceUpdate custom audienceA

Rename or change description of a custom audience. Only fields the user named.

Inputs: account_id (required), custom_audience_id (required), name, description.

x_ads_update_campaignUpdate campaignA

Partial update of name and/or budget micros. Only change fields the user named. Daily must be ≤ total. Money-affecting: pass confirm=true.

Inputs: account_id (required), campaign_id (required), name, confirm, +2 more.

x_ads_update_pixelUpdate X PixelA

Partial update of name, windows, or retargeting on a web event tag. Only fields the user named.

Inputs: account_id (required), web_event_tag_id (required), name, click_window, +2 more.

x_ads_upload_mediaUpload mediaA

Upload an image or video via media_url or inline base64 (media.data). Images use simple upload; video and files >5MB use v2 chunked INIT/APPEND/FINALIZE and poll until processed. Ads videos use media_category amplify_video. Returns media_key for cards/tweets. Provide exactly one source. If the intended creative failed to upload or process, STOP. Do not promote a still, a source image, or a pre-existing library asset.

Inputs: account_id (required), name, media, for_ads, +1 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.4/5.0

    Scored across 51 tools

    Disambiguation3/5

    Most tools target distinct resources, but there are overlapping clusters: scheduler_status vs schedule_list both expose job/queue information, create_ad vs create_image_ad vs create_video_ad are all ad-creation entry points, and get_cards/list_creatives/get_tweets/get_media have related creative-resolution duties. Descriptions help, but an agent would need to read carefully to avoid selecting the wrong tool.

    Naming Consistency4/5

    The x_ads_ prefix is consistent, and the dominant verb_noun pattern (list_campaigns, create_pixel, update_ad_group) is clear. There are notable exceptions like schedule_list, agents_list, audience_users, audience_targeted, and active_entities that invert or break the pattern, but they are relatively few and do not make the naming chaotic.

    Tool Count1/5

    51 tools is far beyond the typical well-scoped MCP surface and crosses the 50+ extreme-mismatch threshold. Even though many tools map to real X Ads operations, the scheduler/agents sub-domain and composite ad-creation helpers add significant bulk that could be consolidated or split into a separate server.

    Completeness4/5

    The set provides solid lifecycle coverage: campaigns, ad groups, ads, audiences, DNR lists, pixels, targeting, creatives, media, analytics, and reach all have create/read/update/delete or equivalent operations. Minor gaps exist, such as no single-entity get for campaigns/ad groups, no direct remove-targeting tool, and no way to edit a scheduled job after creation, but these are workable.

    Maintenance

    ActivityNo data
    ResponsivenessSyncing

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables management of OpenAI Ads campaigns, ad groups, ads, and insights through MCP, with approval-gated live mutations for safe write operations.
      127 npm
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables reading and writing Reddit Ads campaigns, ad groups, ads, and performance reports with tiered safety controls, using the Reddit Ads API v3.
      30
      84 npm
      1
      MIT
    • F
      license
      Not graded
      quality
      C
      maintenance
      Enables management of Google Ads accounts via MCP, providing read and write tools for campaigns, ad groups, keywords, assets, and more, with support for reporting and mutations.
      -
    • F
      license
      Not graded
      quality
      C
      maintenance
      Enables MCP-based interaction with the X (Twitter) API v2, supporting reads like timeline, mentions, and search, and writes through a two-step draft-and-confirm process that ensures human-visible previews before any public post.
      -