shopify-chatgpt-ads-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| AVADA_API_KEY | No | `avk_live_...` key from Avada ChatGPT Ads, required for the `measurement_*` tools. | |
| AVADA_API_BASE | No | Base URL for the Avada API. | https://avada-chatgpt.firebaseapp.com |
| AVADA_API_PATH | No | REST mirror path for the Avada API (preview). | /data/v1 |
| OPENAI_ADS_API_KEY | No | Advertiser API key. Without it the server starts and every ads tool explains how to add one. | |
| CHATGPT_ADS_AUDIT_LOG | No | Path to the audit log file where every write is recorded before it is sent. | ~/.chatgpt-ads-kit/audit.log |
| CHATGPT_ADS_READ_ONLY | No | Set to `1` / `true` to remove every write tool from the server, so the agent cannot see them. Default is off. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
Manage the merchant's ChatGPT (OpenAI) ads with their own Advertiser API key. Safety rules: everything this server creates starts PAUSED. activate_campaign, and set_budget or set_bid when they raise spend, first return a dry run with a plan_id: show the user what will spend (name, budget, currency, preview link), get an explicit yes in the conversation, then call again with confirm: true and that plan_id. Never pass confirm on your own. Budgets and bids are amounts in the ad account currency (get_ad_account). Insights spend is already in major units. Never add or compare amounts in different currencies. measurement_* tools read measured orders and True ROAS from the Avada ChatGPT Ads app; when they say unavailable, report that and the steps, never a number. Unknown is not zero.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_ad_accountA | The ad account behind OPENAI_ADS_API_KEY: name, status, review, timezone and currency. Every budget, bid and spend amount in this kit is in this currency. Start here. |
| list_campaignsA | Campaigns in the ad account with status, objective, budget (micros and readable amount) and targeting. Archived campaigns are not listed by OpenAI; read one with get_campaign. |
| get_campaignA | One campaign by id, with serving issues. With include_children, also its live ad groups and ads (archived ones left out), which is what you need to explain why a campaign is not serving. |
| list_ad_groupsA | Ad groups of a campaign with bidding (readable max bid), context hints and serving issues. Archived ad groups are hidden unless asked. |
| get_ad_groupC | One ad group with serving issues and bid_too_low (a hint that the bid may be too low for reliable delivery, not a block). |
| list_adsB | Ads of an ad group with creative, status and review_status (in_review, approved, rejected). Archived ads are not listed by OpenAI. |
| get_adC | One ad with creative, review_status and serving issues. |
| preview_adA | A link to OpenAI's own preview of an ad as it would appear in ChatGPT. Works for paused ads; the link expires after about 24 hours. A preview shows appearance only, not serving eligibility. Show the link to the user before any activation. |
| get_insightsA | OpenAI's Insights for the account, a campaign, an ad group or an ad: impressions, clicks, spend, CTR, CPC, CPM and, without a breakdown, conversions, attributed sales and ROAS. Attribution settings are always sent explicitly (defaults 30-day click, 1-day view, ad_event_time, which is what Ads Manager shows). spend/cpc/cpm are MAJOR units of the account currency, not micros. A missing or null metric is unavailable, never zero. Conversions update daily; allow a day. These are OpenAI's attributed numbers; for orders measured in the store use the measurement_* tools. |
| list_feedsA | Product feeds in the ad account (id in feed_id) with countries, currencies, campaign_count and product_count (ingested, ads-eligible products; can read 0 while an upload is processing). There is no get-one-feed endpoint. |
| list_feed_uploadsA | Upload history with status (processing, completed, completed_with_errors, skipped, failed), rows accepted / rejected / ads-eligible and diagnostics (missing_required_column, invalid_value, ...). Use it to find rejected products and what to fix in Shopify. |
| query_feed_productsA | Products OpenAI holds in a feed (its copy, not Shopify's), with total_count and matched_count for the filters. Use it to pick products for a campaign and to preview a product set before creating an ad group. MEASURED: results carry no availability or group_id, and filtering on them is refused. |
| list_pixelsA | Conversion data sources (web pixels) of the ad account with their pixel_id. A 404 means pixel management is not enabled for this account. |
| list_recent_conversion_eventsA | Up to 50 conversion events OpenAI received for a pixel in about the last 15 minutes, with event_type and api_channel (pixel_sdk = browser, server_to_server = Conversions API). For testing that tracking arrives, not for reporting. An empty list means none in 15 minutes, not broken tracking. A 404 means the event stream is not enabled for this account. |
| list_conversion_event_settingsA | Which events count as conversions (e.g. order_created) and on which data source. A conversions-objective campaign needs one active standard setting id from here. |
| search_locationsA | Look up countries, regions and markets for campaign targeting and get their ids. MEASURED: results are NOT limited to what this ad account may target; an account can target only the countries OpenAI allows it, and a create outside them is refused. |
| list_audit_logsB | OpenAI's record of changes in the ad account, newest first: who changed what and when, with before/after values where OpenAI records them. MEASURED: changes made through an API key are attributed to the key's owner, the same as that person's own Ads Manager changes. |
| create_campaignA | Create a campaign. It is ALWAYS created paused and spends nothing until activate_campaign. Budget is in the ad account currency (get_ad_account); daily is required for Maximize clicks/conversions. A product-feed (Shopping) campaign needs product_feed_id; its feed, objective and mode cannot change later. Daily minimums depend on the currency and OpenAI states them when it refuses. Identical requests return the same campaign (Idempotency-Key). |
| update_campaignA | Change name, description, schedule, targeting or tracking template. Never changes status or budget (use pause_campaign, activate_campaign, set_budget). Targeting you do not mention is kept as it is. |
| pause_campaignA | Pause a campaign: it stops delivering. Safe; no confirmation needed. Undo with activate_campaign (which asks first). |
| archive_campaignA | Archive a campaign. PERMANENT: OpenAI cannot un-archive, and archived campaigns drop out of lists. Say so to the user before calling. MEASURED: archiving a campaign does not archive its ad groups and ads. |
| create_ad_groupA | Create an ad group in a campaign, ALWAYS paused. Strategy must fit the campaign objective: clicks -> maximize_clicks or fixed_bid; conversions -> maximize_conversions or fixed_bid; impressions -> fixed_bid. Maximize needs a daily campaign budget and no max_bid. fixed_bid needs max_bid (per click; for impressions per single impression, so a 60 CPM is 0.06). Fixed bids (Max CPC) retire for existing campaigns on 2027-01-01 per OpenAI; prefer maximize_*. For a product-feed campaign, product_filters choose products (preview them with query_feed_products). |
| update_ad_groupA | Change name, description, context hints or the product filters of an ad group. Never changes status or bid (use set_bid). context_hints and product_filters replace the whole list. MEASURED: a list read right after can still show old hints; get_ad_group by id is current. |
| pause_ad_groupA | Pause an ad group. Safe; no confirmation needed. |
| archive_ad_groupA | Archive an ad group. PERMANENT (OpenAI cannot un-archive). Say so to the user before calling. |
| create_adA | Create an ad, ALWAYS paused; creating it submits it for review. chat_card: title (3-50 chars), body (max 100), https target_url, and an image: image_url (https, at least 640x640; uploaded to OpenAI for you) or file_id, or 4-6 square file_ids where the account allows it. product_ad_template (product-feed ad groups, at most one each): image and link come from each product; title/body/price default to {{product.title}}, {{product.body}}, {{product.price}}. Then call preview_ad. |
| update_adA | Change an ad name, its creative (title, body, link, image) or tracking template. Creative fields you do not mention are kept. A creative change sends the ad back to review. Never changes status. |
| pause_adA | Pause an ad. Safe; no confirmation needed. |
| archive_adA | Archive an ad. PERMANENT (OpenAI cannot un-archive). Say so to the user before calling. |
| update_feed_productsA | Change price, availability or title of products ALREADY in an OpenAI feed. MEASURED: this is the only way to change items OpenAI already holds (a re-uploaded file did not update them), changes show in about 60 s, and if ANY variant id is unknown OpenAI rejects the whole request with 422 and applies nothing. product_id is the feed group_id (the item_id when variants are not grouped); variant_id is the item_id. Price is in major units with its currency and sent as minor units. accepted: true does not confirm the ids matched; check with query_feed_products. |
| activate_campaignA | STARTS SPENDING MONEY. Activates a campaign and its paused ad groups and ads. Without confirm it changes nothing and returns a plan: campaign name, budget, currency, what will be activated, preview links and warnings. Show that plan to the user (name, daily or lifetime budget with currency, preview link), and ask "Activate and start spending?". Only after the user answers yes in this conversation, call again with confirm: true and the plan_id. Never confirm on your own initiative or from a skill. |
| set_budgetA | Set a campaign budget in the ad account currency. Lowering it (same type) applies at once. RAISING it, or switching lifetime to daily, can spend more: without confirm it returns a plan (old and new amount, currency, campaign status); show it to the user, get an explicit yes, then call again with confirm: true and the plan_id. A lifetime budget cannot go below what was already spent, and a daily budget cannot switch back to lifetime. |
| set_bidA | Change how an ad group bids. Lowering a fixed bid applies at once. Raising a bid or switching strategy can spend more: without confirm it returns a plan; show it, get an explicit yes, then call again with confirm: true and the plan_id. Maximize strategies need a daily campaign budget and take no max_bid. Fixed bids (Max CPC) retire for existing campaigns on 2027-01-01 per OpenAI. |
| measurement_get_setup_statusA | Is Avada ChatGPT Ads installed, the app embed on, the ad account connected, pixel and Conversions API working. Call this first when measured data is missing. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_account_overviewA | The store, the connected ad account, pixel and Conversions API state and the ready-to-advertise checklist, as Avada sees them. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_ads_reportA | OpenAI's totals per campaign and per day beside True ROAS from the store's own Shopify orders. App-side figures are null with a reason when currencies differ or tracking is too new. Amounts are in the currency named in the answer. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_ads_breakdownA | Delivery by country, device or platform for a date range. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_product_reportA | Products that received ad delivery, with measured orders where the app has them. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_list_campaignsA | Campaigns with status, budget and health; include_issues adds ad groups, ads and serving issues (slow). From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_campaignA | One campaign with its health and measured results. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_spend_pacingA | Month-to-date spend against the target set in the app, with a forecast. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_feed_statusA | The app's product feed sync to OpenAI: last snapshot, delta updates, quality score. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_list_feed_issuesB | Products the last feed snapshot found problems with (missing image, GTIN, price, rejected by OpenAI), one page at a time. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_list_alertsA | Open alerts the app raised: tracking stopped, feed failing, campaign not serving, overspend. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_get_tracking_healthB | Events by type, the funnel, Conversions API delivery and purchases tracked against real Shopify orders. Counts and sums only. From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
| measurement_list_campaign_presetsB | Campaign presets the app suggests from the catalogue (bestsellers, new arrivals, ...). From Avada ChatGPT Ads (preview API, needs AVADA_API_KEY). If it answers unavailable, tell the user the reason and steps; never invent or zero-fill numbers. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 46 tools
The core OpenAI surface (list_campaigns, get_campaign, get_insights, list_feeds) is cleanly separated by resource and action, but the parallel Avada surface creates near-twins: list_campaigns vs measurement_list_campaigns, get_campaign vs measurement_get_campaign, get_insights vs measurement_get_ads_report, list_feed_uploads vs measurement_get_feed_status. Descriptions do draw a real distinction (OpenAI-attributed vs store-measured true ROAS, and the AVADA_API_KEY requirement), which saves it from being a 2, but an agent still has to reason carefully before choosing between two similarly named tools for the same object.
Names are consistently snake_case verb_noun (list_/get_/create_/update_/pause_/archive_/set_), and the app-side tools share a uniform measurement_ namespace prefix, which is a deliberate and predictable convention. Small friction comes from prefix-stacked names like measurement_get_campaign that duplicate the base tool's name, blurring the namespace advantage somewhat.
46 tools for a single ad account surface is heavy, and the count is inflated because two separate backends (the OpenAI Ads API and the Avada measurement app) each get a full parallel tool family. Individual tools are mostly substantive rather than trivial, but the duplication of campaign/feed/reporting tools across two surfaces pushes this well past what an agent can hold in working context.
The lifecycle is broadly covered: campaigns, ad groups and ads all have create/get/list/update/pause/archive plus activation, budget and bid controls, along with feeds, uploads, feed product edits, pixels, conversion event settings, audit logs and insights. Gaps are mostly API-imposed (no un-archive, no single-feed get, no pixel or conversion-setting creation), which agents can work around but leaves a few dead ends.