MCP server for the OpenAI Ads Advertiser API, with retries, response budgeting and postcode-level location lookup
Server has 11 well-named, read-only tools with consistent verb-noun naming (get_, list_, search_). All tools have descriptions (avg 180 chars, within baseline 194). Input schemas are present and properly typed with constraints (e.g., search_geo limit 1-100, list_campaigns order enum). However, output schemas are NOT documented, responses are returned as JSON strings without field-level documentation, forcing LLMs to parse unstructured text. Error handling is present but generic (soft errors as JSON, hard errors raised). No tool annotations (readOnlyHint, etc.). Parameter descriptions are adequate but could be more prescriptive about expected formats and dependencies.
Report which features are enabled for this account. Useful because this API answers 403, and sometimes 404, when a feature is not enabled — not only when a resource is missing.
Fetch the ad account: currency, timezone, status, negative keywords. The first call to make after connecting; it also verifies the API key. A 409 "Ad Account is missing details" means the account profile is incomplete in the Ads Manager.
Fetch a single ad. When review_status is 'rejected', the cause is in review.reason_code — for example crawl_failed, crawler_403, robots_txt or missing_favicon.
Fetch a single ad group, including its context hints and bid.
Fetch a single campaign.
Fetch performance metrics for an account, campaign, ad group or ad.
Output schemas not documented. Tools return JSON strings without field-level documentation. LLMs must parse unstructured responses, increasing token waste and hallucination risk.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). All tools are read-only but this is not declared in the schema, forcing LLMs to infer safety from descriptions.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 62 | 2026-07-28+ | v2 |
List every account this API key can reach.
List the ad groups of a campaign.
List the ads of an ad group, including review status.
List campaigns. With fetch_all the cursor is followed automatically.
Look up location IDs for targeting. Handles three quirks of the German dataset automatically: - Place names without umlauts return nothing. "Muenchen" is rewritten to "München", "Koeln" to "Köln", and both spellings are tried. - English exonyms return no German results. "Munich" becomes "München". - Districts and sub-municipal localities do not exist as a location type; the response says so instead of returning an empty list silently. Types available for Germany: country, region (16 federal states) and postal_code. No dma, no radius.
Parameter descriptions lack prescriptive format guidance. E.g., 'campaign_id' and 'ad_group_id' have no description of expected format (opaque string? numeric? length constraints?). Baseline: 100% of A+ tools have param descriptions.
Error responses are generic JSON with 'error' and 'message' fields. No recovery guidance (e.g., 'Try search_geo() first to find valid location IDs'). Baseline: A+ tools include actionable recovery hints.
Pagination in list_* tools uses 'fetch_all' boolean and 'max_items' limit, but no 'next_cursor' or 'total_count' returned. LLMs cannot determine if more results exist or plan pagination strategy.