Skip to main content
Glama
gudlab

ads-mcp

by gudlab

ads-mcp

An MCP server wrapping the Google Ads API and Meta Marketing API directly, with no third-party quota or middleman. Point an MCP-compatible client at it and manage Search campaigns, keywords, ads and Meta ad review status straight against Google's and Meta's own APIs.

Every write tool that could spend money is safe by default: google_ads_create_search_campaign always creates campaigns PAUSED, and the only tools that can turn spend on are google_ads_set_campaign_status / meta_ads_set_campaign_status, called explicitly.

Setup

Credentials require logins and business identity only you have; nothing here can be automated.

Google Ads

  1. Google Cloud project + OAuth client: console.cloud.google.com, new (or existing) project, APIs & Services, Credentials, Create OAuth client ID (type: Desktop app). Copy the Client ID/Secret into .env.

  2. Under that OAuth client, add http://localhost:8787/oauth2callback as an authorized redirect URI.

  3. Developer token: ads.google.com/aw/apicenter, apply for a token. Basic access is free and usually approved within a few days; it allows ~15,000 operations/day. Put it in .env as GOOGLE_ADS_DEVELOPER_TOKEN.

  4. Run pnpm install then pnpm google:auth, this opens a browser, you sign in and consent, and it prints a refresh token to paste into .env as GOOGLE_ADS_REFRESH_TOKEN.

  5. Set GOOGLE_ADS_CUSTOMER_ID to the 10-digit account ID (no dashes) these tools should operate on by default. If that account is managed under an MCC account, also set GOOGLE_ADS_LOGIN_CUSTOMER_ID to the MCC's ID.

Meta Ads

  1. Meta App: developers.facebook.com, My Apps, Create App (type: Business). Copy the App ID/Secret from Settings, Basic into .env.

  2. Request the ads_management permission under App Review. This needs Business Verification (documents proving the business is real) and usually a short screen-recording demo of the exact use case. This step is slow, budget for weeks, not days.

  3. Once approved, generate a long-lived System User access token (Business Settings, System Users) with ads_management scope on the ad account, and put it in .env as META_ACCESS_TOKEN.

  4. Set META_AD_ACCOUNT_ID to the numeric account ID (no act_ prefix needed, the client adds it).

Related MCP server: PaidSync MCP Server

Running

pnpm install
pnpm dev        # runs the MCP server over stdio via tsx, for local testing
pnpm build && pnpm start   # compiled version

Once published to npm, it also runs via npx @gudlab/ads-mcp, no local clone needed. Point an MCP-compatible client at it, e.g. in Claude Code's .mcp.json:

{
  "mcpServers": {
    "ads-mcp": {
      "command": "npx",
      "args": ["-y", "@gudlab/ads-mcp"],
      "env": {
        "GOOGLE_ADS_CLIENT_ID": "...",
        "GOOGLE_ADS_CLIENT_SECRET": "...",
        "GOOGLE_ADS_DEVELOPER_TOKEN": "...",
        "GOOGLE_ADS_REFRESH_TOKEN": "...",
        "GOOGLE_ADS_CUSTOMER_ID": "...",
        "META_APP_ID": "...",
        "META_APP_SECRET": "...",
        "META_ACCESS_TOKEN": "...",
        "META_AD_ACCOUNT_ID": "..."
      }
    }
  }
}

Before that's published, point it at the local build instead: command: "node", args: ["/path/to/ads-mcp/dist/index.js"], or command: "pnpm", args: ["dev"] with cwd set to this directory for local iteration.

First real run: verify before trusting it

The Google Ads and Meta Marketing APIs both shift field/enum names across versions, so before relying on any tool here for real campaign work:

  1. Run google_ads_list_campaigns / meta_ads_list_campaigns first, read-only, the cheapest way to confirm auth and the client libraries are wired correctly.

  2. Test google_ads_create_search_campaign on a throwaway campaign name, then check it directly in the Google Ads UI rather than trusting the tool's own response as proof of correctness.

  3. Only after that, use it for real campaign work.

Tool reference

Google Ads (src/google-ads/tools.ts): list_campaigns, get_campaign_structure, create_search_campaign (always PAUSED), add_keywords, set_keyword_status (pause/enable, reversible), remove_keywords (permanent, requires confirm_delete: true), update_bid_strategy, add_negative_keywords, set_campaign_status (the only enable/spend switch).

Meta Ads (src/meta-ads/tools.ts): list_campaigns, get_ad_status (pulls ad_review_feedback/issues_info, the actual rejection reason for a disapproved ad), list_ads_by_status (e.g. pull every DISAPPROVED ad in one call), set_campaign_status (the only enable/spend switch).

Privacy and data handling

All credentials stay in your own .env (never committed, see .gitignore) or your MCP client's own env config, and are used only to call Google's and Meta's APIs directly from your machine. This server sends nothing to any third party: no telemetry, no analytics, no relay service. Every request goes straight from your process to googleads.googleapis.com or graph.facebook.com, using your own developer token and access token.

License

MIT, see LICENSE.

Available Tools

13 tools
meta_ads_get_ad_statusGet a Meta ad's review statusB
Read-onlyIdempotent

Get an ad's review status and rejection reason if any

ParametersJSON Schema
NameRequiredDescriptionDefault
ad_idYes

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the operation as readOnly and idempotent, so the safety profile is covered. The description adds the behavioral clue that rejection reason is only returned when applicable, but it does not disclose other useful context such as possible status values, invalid ad_id behavior, or response shape.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single focused sentence with no filler. It front-loads the main purpose and adds one useful detail ('rejection reason if any') that goes beyond the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter read-only getter with strong annotations, the description covers the core return concept: review status plus optional rejection reason. It is slightly thin on output details since there is no output schema, but nothing critical seems missing for basic invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description does not explain the ad_id parameter's format, source, or semantics beyond the resource phrase 'an ad'. The schema only provides the name and type, so the agent gets little help understanding what values are valid.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Get') and the resource ('an ad's review status'), including the additional detail that rejection reason is returned when present. It does not explicitly differentiate from sibling tool meta_ads_list_ads_by_status, which lists ads by status, but the singular focus on a single ad's status is reasonably clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool is used when you need a specific ad's review status or rejection reason, but it gives no explicit 'when to use vs alternatives' guidance. There is no mention of prerequisites, limitations, or when to prefer sibling status-related tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

meta_ads_list_ads_by_statusList Meta ads by review statusA
Read-onlyIdempotent

List ads in an account filtered by effective_status (e.g. DISAPPROVED)

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idNo
effective_statusNo

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint, openWorldHint, and idempotentHint, and the description's 'List' is consistent with them. The description adds account scoping and the effective_status filter, but it does not disclose pagination, return shape, or open-world caveats; with annotations present this is a reasonable but not additive profile.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with the verb and resource front-loaded, followed immediately by the filter dimension. Every part is informative and there is no unnecessary wording.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple and annotations cover its safety profile, but the description leaves account_id's apparent requirement unclear given that no parameters are marked required in the schema, and there is no output schema or mention of what fields are returned. It is adequate for a basic list tool but not fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description identifies effective_status as the filter and provides the DISAPPROVED example, adding some meaning beyond the raw schema. However, account_id is only implied by 'in an account', and the status values are already enumerated in the schema, so the description only partially compensates for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource — 'List ads in an account' — and names the filter dimension effective_status with a concrete example. This is clearly distinct from sibling tools like meta_ads_list_campaigns (different resource) and meta_ads_get_ad_status (single ad lookup), even without naming them explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'filtered by effective_status (e.g. DISAPPROVED)' implies the tool is for retrieving ads by review status, but it never states when to prefer this over meta_ads_get_ad_status or mentions exclusions/alternatives. The usage context is present only implicitly.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

meta_ads_list_campaignsList Meta Ads campaignsB
Read-onlyIdempotent

List Meta campaigns with status and budget

ParametersJSON Schema
NameRequiredDescriptionDefault
account_idNoNumeric ad account ID, no 'act_' prefix. Defaults to META_AD_ACCOUNT_ID

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and openWorldHint=true, so the safety profile is covered. The description adds that the tool returns status and budget, which is useful, but it does not disclose pagination, default account behavior, or whether the result is a summary or full list. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single short sentence that front-loads the action and resource. It is concise and readable, though it could add a brief note about the optional account_id default without much cost.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only list tool with one optional parameter and no output schema, the description is mostly adequate. However, it does not mention pagination, result format, or the fact that account_id defaults to an environment variable, which an agent might need to know for correct invocation. The annotations cover safety, but the description leaves some operational context implicit.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% for the single optional parameter, and the description adds no parameter-specific detail beyond the schema. The baseline of 3 applies because the schema fully documents account_id, including the 'no act_ prefix' and default behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('List') and resource ('Meta campaigns') and mentions the key fields returned ('status and budget'). It is clear enough to distinguish from siblings like meta_ads_get_ad_status, though it does not explicitly name a sibling or contrast with meta_ads_list_ads_by_status.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies a read-only listing use case, and the annotations (readOnlyHint, idempotentHint) reinforce that it is safe to call. However, it does not explicitly state when to use this tool versus alternatives like google_ads_list_campaigns or meta_ads_list_ads_by_status, leaving the agent to infer from the name and platform.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

meta_ads_set_campaign_statusEnable or pause a Meta Ads campaignA
Idempotent

Enable or pause a Meta campaign. The only tool that can turn spend on.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusYes
campaign_idYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false, idempotentHint=true, and destructiveHint=false, and the description does not contradict them. The description adds meaningful behavioral context by warning that this tool controls ad spend — a consequential side effect beyond what annotations express. It does not cover failure modes or timing, but the safety profile is well covered by annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with no filler. The action is front-loaded, and the second sentence earns its place by providing an exclusivity and consequence signal that helps tool selection and invocation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter setter with a clear enum and strong annotations, the description is nearly complete: it states the action, the spend consequence, and why this tool is unique among siblings. It does not describe return values, but with no output schema and a straightforward status-setting operation, that is a minor gap rather than an obstacle.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It partially does: 'Enable or pause' maps directly to the status enum values ACTIVE and PAUSED, and the spend warning clarifies the impact of ACTIVE. However, it does not explain campaign_id format or any edge cases, leaving some semantics to be inferred from parameter names alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb-resource pair ('Enable or pause a Meta campaign') and adds the exclusivity claim 'The only tool that can turn spend on,' which clearly distinguishes it from sibling read/list tools and the Google Ads status setter. An agent can immediately tell what this tool does and how it differs from nearby alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The exclusivity statement 'The only tool that can turn spend on' gives clear routing for the primary use case: activating or pausing a Meta campaign's spend. It does not explicitly name alternatives or state when not to use this tool, but the uniqueness claim is strong enough for an agent to select it over the listed siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 13 tool updatesv0.1.1
    • First observedgoogle_ads_add_keywords
    • First observedgoogle_ads_add_negative_keywords
    • First observedgoogle_ads_create_search_campaign
    • First observedgoogle_ads_get_campaign_structure
    • First observedgoogle_ads_list_campaigns
    • First observedgoogle_ads_remove_keywords
    • First observedgoogle_ads_set_campaign_status
    • First observedgoogle_ads_set_keyword_status
    • First observedgoogle_ads_update_bid_strategy
    • First observedmeta_ads_get_ad_status
    • First observedmeta_ads_list_ads_by_status
    • First observedmeta_ads_list_campaigns
    • First observedmeta_ads_set_campaign_status

TDQS

B3.3/5.0

Scored across 13 tools

Disambiguation5/5

Every tool targets a distinct platform-resource-action combination, with clear google_ads_ vs meta_ads_ prefixes separating the two domains. Even the two set_campaign_status tools are disambiguated by the platform prefix and description.

Naming Consistency5/5

Tool names consistently follow a {platform}_{verb}_{object} pattern in snake_case. Verbs like get, list, create, add, set, remove, update are applied uniformly, making the naming predictable across both platforms.

Tool Count5/5

13 tools is a reasonable scope for an ads management server covering two platforms. The count is neither bloated nor thin, and each tool covers a distinct operation.

Completeness2/5

The Google Ads side is fairly complete with campaign creation, keyword management, bid strategy updates, and status controls. However, the Meta Ads side lacks any create or edit operations beyond status changes, making the surface significantly incomplete for managing Meta campaigns.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables comprehensive management of Google Ads campaigns through natural language, including campaign creation, ad group management, keyword operations, Performance Max campaigns, conversion tracking, and performance insights with support for multiple accounts.
    4
    -
  • A
    license
    Not graded
    quality
    F
    maintenance
    Connects Google Ads, Meta Ads, and LinkedIn Ads to AI assistants, enabling natural language ad campaign management, reporting, and optimization across platforms.
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Enables full read/write control of Google Ads accounts—managing campaigns, ad groups, ads, keywords, Performance Max, budgets, targeting, and performance reporting through natural language. Supports Search, Display, Video, and Demand Gen campaign types with flexible GAQL querying and comprehensive reporting.
    47
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables complete management of Google Ads, including CRUD operations, dashboards, reporting, and all features from the Google Ads panel.
    18 npm
    MIT