microsoft-ads-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| READ_ONLY | No | true registers no write tools at all (default false) | false |
| MICROSOFT_ADS_CLIENT_ID | Yes | Azure AD app (client) id | |
| MICROSOFT_ADS_ACCOUNT_ID | No | Discovered via search_accounts if unset | |
| MICROSOFT_ADS_CUSTOMER_ID | No | Discovered via search_accounts if unset | |
| MICROSOFT_ADS_ENVIRONMENT | No | production (default) or sandbox | production |
| MICROSOFT_ADS_CLIENT_SECRET | No | Only for web/confidential app registrations | |
| MICROSOFT_ADS_REFRESH_TOKEN | No | Run non-interactively; else mint one via the auth tools | |
| MICROSOFT_ADS_DEVELOPER_TOKEN | Yes | From the developer portal |
Capabilities
Features and capabilities supported by this server
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| extensions | {
"io.modelcontextprotocol/ui": {}
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| account_healthA | Validate credentials and report the environment and write mode. Call this first. |
| get_auth_urlA | Begin one-time sign-in: returns an OAuth sign-in URL to give the user. The URL targets the account's identity provider (Microsoft by default, or Google for
Google-federated accounts). Present it to the user as a clickable sign-in link, and ask
them to sign in with the account that manages the ad account. After they sign in,
the browser lands on a near-blank page whose address-bar URL contains a Only needed once, when no refresh token is configured. The minted token is persisted and auto-refreshed thereafter, so the user never has to repeat this. |
| complete_authA | Finish sign-in: exchange the browser's redirect URL for a saved refresh token. Call this with the URL the user pasted back after completing |
| search_accountsA | List every Microsoft Advertising account reachable by the authenticated user. |
| get_campaignsA | List Search campaigns in the configured account. |
| get_ad_groupsB | List ad groups in a campaign. |
| get_keywordsA | List keywords in an ad group. |
| get_adsA | List text/responsive-search ads in an ad group. |
| get_budgetsA | Per-campaign budget view (daily budget and any shared-budget id). |
| get_account_url_optionsA | Read the account-level URL tracking / tracking template that every campaign inherits. This is the account-level URL options: the tracking template (tracking URL template),
Final URL suffix, msclkid auto-tagging, and parallel tracking that per-campaign, ad-group,
ad, and keyword values inherit. Those per-entity values are typically blank and inherit
from here, so this answers "where is the tracking template set?" and how UTM / msclkid
click tracking is configured -- check this (not just the entity hierarchy) to confirm how
clicks are tracked. |
| get_effective_url_settingsA | Resolve the effective URL tracking (tracking template, Final URL suffix, URL custom parameters) for a campaign or ad group, and which level set each. URL tracking follows Microsoft's inherited / override order (keyword > ad > ad group >
campaign > account): a value set at a deeper level wins, otherwise it inherits from its
parent, ultimately the account-level URL options. So a campaign or ad group can show a null
tracking template while the account template is what actually applies. This walks that
inheritance chain and returns the effective tracking_url_template / final_url_suffix /
url_custom_parameters plus a |
| get_negative_keywordsA | List negative keywords attached to campaigns or ad groups. |
| get_website_exclusionsA | List the website / mobile-app-id exclusions (negative sites) blocked on a campaign. These prevent ads from serving on the listed sites/apps. Add or remove them with add_website_exclusions / remove_website_exclusions. |
| get_ad_extensionsA | List ad extensions in the account (call, callout, sitelink, etc.). By default this enumerates all association scopes (Account, Campaign, AdGroup) and merges them, so extensions attached at the campaign or ad-group level are included — most extensions are associated to a campaign, not the account, so scoping to Account alone often looks empty. Pass an explicit association_type to narrow to one scope. |
| get_conversion_goalsA | List conversion goals (all types). Pass goal_ids to fetch specific goals. Each goal reports |
| get_uet_tagsA | List UET tags. Pass tag_ids to fetch specific tags. |
| set_active_accountA | Switch which account subsequent tool calls read from and write to (this session only). Use search_accounts to find ids. The OAuth credential is unchanged; this only rescopes calls. Confirm the target with account_health afterwards before any write. |
| get_location_targetsB | List the location targets/exclusions on a campaign. |
| get_location_intentA | Read a campaign's location-intent setting (presence vs. broader reach). Returns the single LocationIntent criterion's |
| get_ad_schedulesA | Read a campaign's ad-schedule (dayparting) windows and their time-zone context. Returns each window ( |
| get_device_bid_adjustmentsA | Read a campaign's device bid adjustments (Computers / Smartphones / Tablets). Each row's |
| resolve_postal_codesA | Resolve ZIP / postal codes to Microsoft LocationIds (for location targeting). Downloads and caches Microsoft's geo-locations file on first use. |
| bulk_downloadA | Export the account to a Bulk file; returns the result file URL when ready. Submits a Bulk download and polls to completion (the file can be large, so the URL is returned rather than the contents). |
| run_performance_reportA | Run a performance report end-to-end and return the parsed rows. Unlike a raw submit, this submits the report, polls until it is ready, downloads the CSV, and parses it — so the rows come back inline. |
| estimate_keyword_bidsA | Estimate the bid to reach the first page (or mainline) for keywords -- the "estimated first page bid" from Keyword Planner. For each keyword, returns one estimate per match type: |
| get_keyword_ideasA | Discover keyword ideas from seed phrases and/or a landing-page URL (Keyword Planner). Each idea reports |
| get_keyword_traffic_estimatesA | Estimate weekly traffic (clicks/impressions/cost/position) for keywords at a given bid. Each keyword's estimate is a min..max bracket at the supplied |
| check_first_page_bidsA | Flag keywords whose bid is below the estimated first-page bid ("Below first page bid"). This is the API-driven version of the delivery state the UI shows as "Below first page
bid". For each keyword in the ad group it looks up the Keyword Planner first-page bid
estimate (at the keyword's own match type) and compares it against the keyword's effective
bid -- the keyword's own bid, or the ad group's default bid when the keyword has none. The
result lists the under-bid keywords first (largest |
| create_campaignA | Create a Search campaign (PAUSED by default for safety). |
| update_campaign_statusB | Set a campaign Active or Paused. |
| update_campaignA | Update an existing campaign in place. Only the fields you pass change. |
| create_ad_groupA | Create an ad group (PAUSED) in a campaign. |
| update_ad_groupA | Update an existing ad group in place. Only the fields you pass change. |
| add_keywordsB | Add keywords (Active) to an ad group. |
| update_keywordA | Update an existing keyword in place. Only the fields you pass change. |
| create_responsive_search_adB | Create a Responsive Search Ad (PAUSED). |
| update_responsive_search_adA | Update an existing Responsive Search Ad in place. Only the fields you pass change. Use this to repoint a Final URL or refresh copy without recreating the ad. |
| set_account_url_optionsA | Set account-level URL options / tracking template (applies account-wide; every campaign inherits them). The cleanest way to apply a tracking template (tracking URL template), Final URL suffix, msclkid auto-tagging, or parallel tracking across the whole account at once -- the inherited URL settings -- instead of editing each campaign/ad/keyword. Only the fields you pass change. Read the current values first with get_account_url_options, and use get_effective_url_settings to confirm what a given campaign/ad group resolves to afterward. |
| delete_campaignB | Delete one or more campaigns by id. |
| delete_ad_groupB | Delete one or more ad groups by id. |
| delete_adB | Delete one or more ads by id. |
| delete_keywordB | Delete one or more keywords by id. |
| add_negative_keywordsB | Attach negative keywords to a campaign or ad group. |
| remove_negative_keywordsA | Remove negative keywords from a campaign or ad group, identified by id. Resolve ids first with get_negative_keywords (deletion is by id, not text). |
| add_website_exclusionsA | Block websites / mobile-app ids on a campaign so ads won't serve there. Additive: new sites are merged with the campaign's existing exclusions (read-modify-write), so this never clobbers what's already blocked. Pass bare domains or paths (e.g. "example.com", "example.com/section") or mobile-app ids; a leading http(s):// is stripped for you. Microsoft sites (e.g. MSN.com) can't be excluded and there's a ~2500-site cap per campaign -- such rejections come back in partial_errors. View with get_website_exclusions. |
| remove_website_exclusionsA | Unblock previously excluded websites / app ids on a campaign (matched by URL). Read-modify-write: the named sites are filtered out and the rest are re-set, so other exclusions are retained. Removal is by URL (case-insensitive), not by id. List the current exclusions first with get_website_exclusions. |
| update_call_extensionA | Update an existing call extension in place (e.g. the brand's phone number or tracking). Microsoft replaces the whole call extension on update, so phone number and country code are always required; when you omit them (e.g. to flip only is_call_tracking_enabled) this tool fetches the current extension and re-sends them, so a single-field toggle is safe. |
| add_callout_extensionA | Create a callout extension and optionally attach it to a campaign or ad group. |
| add_sitelink_extensionA | Create a sitelink extension and optionally attach it to a campaign or ad group. |
| update_sitelink_extensionA | Update an existing sitelink extension in place (e.g. add or edit its descriptions). Microsoft replaces the whole sitelink on update, so display text and the final URL are always required; when you omit any field (e.g. to add only descriptions to an existing sitelink) this tool fetches the current extension and re-sends it, so a partial update is safe. Microsoft requires the two description lines together — a sitelink with description1 must also have description2. |
| add_structured_snippet_extensionA | Create a structured snippet extension and optionally attach it to a campaign/ad group. A structured snippet is a header followed by a short list of values (e.g. header "Brands" with values ["Acme", "Globex", "Initech"]). |
| update_structured_snippet_extensionA | Update an existing structured snippet extension in place (edit its header or values). Microsoft replaces the whole snippet on update, so the header and values are always required; when you omit either (e.g. to change only the values) this tool fetches the current extension and re-sends the other, so a partial update is safe. |
| add_call_extensionA | Create a call extension and optionally attach it to a campaign or ad group. |
| delete_ad_extensionA | Delete account-level ad extensions by id. Removes the extension objects entirely (not just their campaign/ad-group associations). |
| create_conversion_goalA | Create a conversion goal. Goals are created Active (a goal does not spend; a paused goal silently fails to record conversions) — pass status="Paused" to override. Conversion goals CANNOT be deleted (Microsoft has no delete API) — they can only be paused or renamed, so name them deliberately. Microsoft has no native "calls from ads" goal. The bid-eligible path for phone calls is an "OfflineConversion" goal fed by apply_offline_conversions (keyed by MSCLKID) — create that goal here, then upload qualifying calls with apply_offline_conversions. |
| update_conversion_goalA | Update a conversion goal in place. Only the fields you pass change. The key bidding lever is |
| apply_offline_conversionsA | Import offline conversions against an OfflineConversion goal, keyed by MSCLKID. This is the bid-eligible path for phone calls: create an OfflineConversion goal with create_conversion_goal, apply your own qualifying filter to the call-center log (e.g. keep calls >=60s), then upload one record per qualifying call. Each record is attributed to the click that drove it and counted under the goal whose name matches conversion_name. Returns per-record errors in partial_errors (the API returns no ids). Note: wait ~2 hours after creating an OfflineConversion goal before uploading — earlier uploads are rejected with "OfflineConversionNotAcceptedForGoal" (expected, not a bug). |
| update_uet_tagB | Update a UET tag's name and/or description in place. |
| add_location_targetsA | Target (or exclude) Microsoft LocationIds on a campaign. Resolve ZIPs to LocationIds first with resolve_postal_codes. |
| set_location_intentA | Set who sees a campaign's ads relative to its targeted locations (location intent). Updates the campaign's single LocationIntent criterion in place (created by Microsoft with a default of "PeopleInOrSearchingForOrViewingPages"). Read the current value first with get_location_intent. |
| set_device_bid_adjustmentA | Set a campaign's bid adjustment for one device (e.g. a mobile modifier). Microsoft calls mobile "Smartphones" (there is no "Mobile"); "Computers" is desktop/laptop. Device criterions are created as a set, so the first time you set any device this also creates the other two at a neutral 0. Read the current values first with get_device_bid_adjustments. |
| remove_location_targetsA | Remove location targets/exclusions from a campaign by criterion id. |
| add_ad_schedulesA | Add ad-schedule (dayparting) windows to a campaign. Each window restricts serving to one day and time range and is additive (a campaign with no schedule serves all hours). Hours are 0-24; minutes are 15-minute granularity, so only 0/15/30/45 are valid (e.g. 09:15-16:45 -> from_hour 9, from_minute 15, to_hour 16, to_minute 45). Times run in the campaign time zone (see get_campaigns / get_ad_schedules) unless you pass use_searcher_time_zone=true. Read existing windows with get_ad_schedules first to avoid duplicates. A new window for a day may NOT overlap an existing window on that same day -- the API rejects the add (returned as ok=false with a partial error, not a crash). To change or extend a window, remove_ad_schedules the old criterion first and then add the new one, in that order (adding the overlapping window first fails); or use replace_ad_schedule, which does the remove-then-add for a single window in one call. |
| remove_ad_schedulesA | Remove ad-schedule (dayparting) windows from a campaign by criterion id. |
| replace_ad_scheduleA | Replace one ad-schedule (dayparting) window: remove the old criterion, add a new one. The API rejects adding a window that overlaps an existing same-day window, so an in-place edit is impossible; this does the only safe sequence -- remove the old criterion, then add new_window -- in one call. Get the criterion_id from get_ad_schedules. If the remove fails, nothing changes; if the add fails after the remove, the result says so (the old window is gone, so that slot is briefly uncovered until you re-add it). |
| bulk_uploadB | Apply ready-made Bulk CSV rows to the account; polls to completion. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
| microsoft-ads-optimizer/SKILL.md | Playbook for managing and reporting on a Microsoft Advertising account via the microsoft-ads MCP server. |
| microsoft-ads-optimizer/_manifest | File listing for microsoft-ads-optimizer |
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/shinypebble/microsoft-ads-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server