Skip to main content
Glama
adeshsarwan

google-ads-function-gateway

by adeshsarwan

Google Ads Function Gateway

Standalone, deterministic Google Ads function gateway for Google Ads Function Catalogue v1 Phase A and Phase B functions 01-06.

The gateway includes a stdio MCP server adapter, but it is intentionally not an MCP conversational client. MCP clients should call approved function names with validated parameters; they must not supply raw GAQL or construct Google Ads API logic at runtime.

Architecture Rules

  • No arbitrary GAQL endpoint.

  • No GAQL supplied by users, CLI callers, or MCP clients.

  • No runtime AI-generated Google Ads API code.

  • Google Ads queries remain predefined and version-controlled.

  • All reporting customer IDs require explicit authorization.

  • No mutation/write operations.

  • The CLI and MCP server use the same catalogue and function classes as future HTTP, cron, dashboard, and automation consumers.

Related MCP server: Google Ads MCP

Local Setup

A. Clone Repository

git clone https://github.com/adeshsarwan/google-ads-mcp-client.git
cd google-ads-mcp-client

B. Create Virtual Environment

python3 -m venv .venv
source .venv/bin/activate

C. Install Dependencies

python -m pip install --upgrade pip
python -m pip install -e ".[dev]"

This installs the official google-ads Python client, python-dotenv, and dev tooling including ruff.

D. Create Local Environment File

cp .env.example .env

Never commit .env. It is ignored by Git.

E. Configure Google Ads Credentials

Edit .env and set:

GOOGLE_ADS_DEVELOPER_TOKEN=replace-me
GOOGLE_ADS_CLIENT_ID=replace-me
GOOGLE_ADS_CLIENT_SECRET=replace-me
GOOGLE_ADS_REFRESH_TOKEN=replace-me

If you do not already have a refresh token, generate one locally:

python -m google_ads_function_gateway oauth-generate-refresh-token

The command reads GOOGLE_ADS_CLIENT_ID and GOOGLE_ADS_CLIENT_SECRET from .env, opens a local Google OAuth consent flow, and prints GOOGLE_ADS_REFRESH_TOKEN=... once. It does not write the token to disk.

This helper intentionally requests only the Google Ads scope and disables incremental authorization. That prevents unrelated scopes previously granted to the same Google Cloud project, such as Ad Manager, from being merged into this local Google Ads setup.

For Web Application OAuth clients, add the local loopback redirect URI in Google Cloud Console under Authorized redirect URIs. The default command uses:

http://127.0.0.1:8080/

If you run the helper with --port, add the matching http://127.0.0.1:<port>/ URI. At minimum, the redirect must use http://127.0.0.1; localhost is not the host used by this helper.

F. Configure Login MCC

GOOGLE_ADS_LOGIN_CUSTOMER_ID is the manager/MCC account used as the discovery root:

GOOGLE_ADS_LOGIN_CUSTOMER_ID=1234567890

The login customer ID is not the same as a reporting customer ID. It identifies the manager account context for Google Ads API access and account discovery.

G. Run Doctor

python -m google_ads_function_gateway doctor

Doctor prints readiness statuses and package/API version details without printing secrets.

H. Discover Accounts

python -m google_ads_function_gateway list-accounts

Discovery can list child accounts under the configured MCC even before those child accounts are in GOOGLE_ADS_ALLOWED_CUSTOMER_IDS. Discovery does not grant reporting authorization.

I. Authorize Selected Customer IDs

Choose customer IDs from discovery output and add them to the allow-list:

GOOGLE_ADS_ALLOWED_CUSTOMER_IDS=1112223333,4445556666

Reporting functions fail closed until the target customer ID is explicitly listed here.

J. Run Standardized Functions

python -m google_ads_function_gateway get-account-details \
  --customer-id 1112223333

python -m google_ads_function_gateway list-campaigns \
  --customer-id 1112223333

python -m google_ads_function_gateway get-campaign-details \
  --customer-id 1112223333 \
  --campaign-id 987654321

python -m google_ads_function_gateway get-campaign-cost \
  --customer-id 1112223333 \
  --start-date 2026-08-30 \
  --end-date 2026-08-30

python -m google_ads_function_gateway get-campaign-performance \
  --customer-id 1112223333 \
  --start-date 2026-08-30 \
  --end-date 2026-08-30

Each command prints the normalized JSON envelope returned by the catalogue.

MCP Transports

The MCP server exposes only the six existing read-only catalogue functions through both stdio and Streamable HTTP. Both transports use the same MCP server object, tool handlers, and Google Ads Function Catalogue. They do not add conversational routing, caller-supplied GAQL, direct HTTP Google Ads calls, or write operations.

Install the project into the local virtual environment first:

source .venv/bin/activate
python -m pip install -e ".[dev]"

Local Stdio Mode

Configure a local MCP client to launch the stdio server from the project root. The client configuration should contain only the executable, arguments, working directory if supported, and non-secret runtime options:

{
  "mcpServers": {
    "google-ads-function-gateway": {
      "command": "/absolute/path/to/google-ads-mcp-client/.venv/bin/python",
      "args": ["-m", "google_ads_function_gateway.mcp_server"],
      "cwd": "/absolute/path/to/google-ads-mcp-client"
    }
  }
}

Local Streamable HTTP Mode

Streamable HTTP uses OAuth by default because it is the remote-capable mode intended for ChatGPT Custom Apps. Set the OAuth environment in the server-side .env first:

GOOGLE_ADS_MCP_AUTH_MODE=oauth
GOOGLE_ADS_MCP_PUBLIC_HOST=googleads-mcp.thebesads.com
GOOGLE_ADS_MCP_PUBLIC_ORIGIN=https://googleads-mcp.thebesads.com
GOOGLE_ADS_MCP_OAUTH_DB=/var/lib/google-ads-mcp/oauth.db
GOOGLE_ADS_MCP_OWNER_USERNAME=replace-me
GOOGLE_ADS_MCP_OWNER_PASSWORD_HASH=replace-with-argon2id-hash
GOOGLE_ADS_MCP_OAUTH_SECRET=replace-with-at-least-32-random-chars
GOOGLE_ADS_MCP_ACCESS_TOKEN_TTL_SECONDS=3600
GOOGLE_ADS_MCP_AUTH_CODE_TTL_SECONDS=300
GOOGLE_ADS_MCP_REFRESH_TOKEN_TTL_SECONDS=2592000
GOOGLE_ADS_MCP_HTTP_DIAGNOSTICS=0

Generate the owner password hash locally without printing the plaintext password:

python - <<'PY'
from argon2 import PasswordHasher
from getpass import getpass

print(PasswordHasher().hash(getpass("Owner password: ")))
PY

Start the Streamable HTTP MCP endpoint:

GOOGLE_ADS_MCP_HOST=127.0.0.1 GOOGLE_ADS_MCP_PORT=8000 \
  python -m google_ads_function_gateway.mcp_server --transport streamable-http

Default endpoint:

http://127.0.0.1:8000/mcp

The server defaults to 127.0.0.1 for safety. OAuth discovery endpoints are exposed by the same process:

https://googleads-mcp.thebesads.com/.well-known/oauth-protected-resource/mcp
https://googleads-mcp.thebesads.com/.well-known/oauth-authorization-server
https://googleads-mcp.thebesads.com/oauth/authorize
https://googleads-mcp.thebesads.com/oauth/token
https://googleads-mcp.thebesads.com/oauth/register
https://googleads-mcp.thebesads.com/oauth/revoke

The OAuth server supports authorization code with PKCE S256 and refresh-token grant. It does not support the implicit grant. The MCP resource scope is:

google_ads.read

offline_access may be requested when the client needs refresh tokens.

OAuth mode is intentionally ChatGPT-compatible mixed authentication. The MCP initialize, notifications/initialized, and tools/list protocol messages can run without an access token so ChatGPT can scan and publish the six tool descriptors before the owner completes OAuth. Every tools/call for the Google Ads catalogue still requires a valid MCP OAuth access token with google_ads.read; missing, invalid, or under-scoped tokens return an MCP auth challenge via _meta["mcp/www_authenticate"] and do not execute Google Ads code.

ChatGPT Business Custom App discovery may first send an unauthenticated empty POST /mcp reachability probe with Content-Length: 0 before it fetches OAuth metadata or sends MCP JSON-RPC. In OAuth mode, the server answers only that exact empty probe with 401 Unauthorized and a WWW-Authenticate challenge pointing to the protected-resource metadata. It does not create an MCP session, authenticate the request, issue OAuth tokens, or execute tools. Any POST containing MCP headers, authorization, a session ID, malformed JSON, a JSON-RPC batch, or a non-empty body continues through the SDK's strict MCP validation path.

OAuth owner approval happens on server-hosted login and approval pages. The password stored in .env must be an Argon2id hash in GOOGLE_ADS_MCP_OWNER_PASSWORD_HASH; do not put a plaintext owner password in .env.

The MCP transport endpoint keeps the SDK Host/Origin/DNS-rebinding checks. OAuth metadata and browser authorization routes keep exact Host allowlisting but do not inherit the MCP transport Origin rule; they are protected by OAuth request state, exact redirect URI matching, PKCE, CSRF tokens, and owner-session cookies.

For a local protocol-only smoke test without OAuth, explicitly choose the fallback mode:

GOOGLE_ADS_MCP_AUTH_MODE=static_bearer \
  python -m google_ads_function_gateway.mcp_server --transport streamable-http

Example local MCP protocol smoke test in fallback mode:

curl -i http://127.0.0.1:8000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl-smoke","version":"0"}}}'

To use the legacy static bearer fallback, set both:

GOOGLE_ADS_MCP_AUTH_MODE=static_bearer
GOOGLE_ADS_MCP_AUTH_TOKEN=replace-with-a-long-random-token

Then add this header to MCP requests:

-H "Authorization: Bearer $GOOGLE_ADS_MCP_AUTH_TOKEN"

OAuth mode ignores GOOGLE_ADS_MCP_AUTH_TOKEN.

Secret Handling

Do not place GOOGLE_ADS_DEVELOPER_TOKEN, OAuth client secrets, refresh tokens, or any other Google Ads credentials in MCP client JSON or remote connector settings. The MCP server uses the same load_local_env() mechanism as the CLI and loads the ignored .env file from the project root.

Google Ads credentials stay server-side and are never provided to ChatGPT. If you expose the Streamable HTTP endpoint through a secure tunnel or HTTPS reverse proxy, ChatGPT connects only to the MCP protocol endpoint and completes OAuth with this MCP service.

MCP stdio reserves stdout for JSON-RPC protocol messages. The MCP entrypoint does not print banners or debug output, and operational logging is configured for stderr.

Set GOOGLE_ADS_MCP_HTTP_DIAGNOSTICS=1 only when diagnosing remote MCP connectivity. It logs secret-free request facts such as path, HTTP status, Content-Type, Accept, User-Agent, Content-Length, MCP session/protocol header presence, MCP method header, JSON top-level type, JSON-RPC version, JSON-RPC id type, parse-failure category, and duration to stderr. It also logs safe OAuth metadata for /oauth/register and /oauth/authorize, including redirect URIs, scope names, grant/response types, token endpoint auth method, client name, software ID, status, and booleans for whether a client ID or client secret was issued. It never logs Authorization header values, cookies, OAuth client secrets, authorization codes, OAuth tokens, owner passwords, Google Ads credentials, raw request parameters, or response data.

Tunnel Compatibility

The Streamable HTTP server is designed to sit behind a standard HTTPS reverse proxy or secure tunnel. Keep the local server bound to 127.0.0.1, expose the MCP and OAuth paths over HTTPS, and ensure the proxy forwards request bodies and MCP headers unchanged. The application does not depend on a specific tunnel vendor.

The MCP Python SDK keeps DNS-rebinding protection enabled for Streamable HTTP. When deploying behind Cloudflare Tunnel or another HTTPS reverse proxy, set the public hostname so the forwarded Host header is explicitly allowed:

GOOGLE_ADS_MCP_PUBLIC_HOST=googleads-mcp.thebesads.com

Current production MCP endpoint:

https://googleads-mcp.thebesads.com/mcp

The production origin allowlist is limited to https://googleads-mcp.thebesads.com when an Origin header is present.

For ChatGPT Business Custom Apps, configure:

  • MCP server URL: https://googleads-mcp.thebesads.com/mcp

  • Authentication: OAuth

  • Scopes: google_ads.read offline_access

ChatGPT receives OAuth access and refresh tokens for this MCP server only. It does not receive the Google Ads developer token, Google OAuth client secret, Google Ads refresh token, or any other server-side Google Ads credential.

Dynamic client registration is enabled so ChatGPT can register its exact redirect URI and use PKCE S256 during account linking.

When the ChatGPT Business app scans actions, it should be able to import the same six tools before the owner OAuth login happens. OAuth is enforced when ChatGPT or another MCP client calls a tool, not when it lists tool descriptors.

Configuration

Supported environment variables:

  • GOOGLE_ADS_DEVELOPER_TOKEN

  • GOOGLE_ADS_CLIENT_ID

  • GOOGLE_ADS_CLIENT_SECRET

  • GOOGLE_ADS_REFRESH_TOKEN

  • GOOGLE_ADS_LOGIN_CUSTOMER_ID

  • GOOGLE_ADS_ALLOWED_CUSTOMER_IDS

  • GOOGLE_ADS_API_VERSION optional; when omitted, the gateway uses the highest API version packaged by google-ads-python

  • GOOGLE_ADS_RETRY_ATTEMPTS optional, default 3

  • GOOGLE_ADS_RUN_LIVE_TESTS optional; set to 1 only when intentionally running live read-only tests

  • GOOGLE_ADS_MCP_HOST optional Streamable HTTP bind host, default 127.0.0.1

  • GOOGLE_ADS_MCP_PORT optional Streamable HTTP bind port, default 8000

  • GOOGLE_ADS_MCP_PUBLIC_HOST optional public HTTPS tunnel or reverse-proxy hostname allowed by MCP DNS-rebinding protection

  • GOOGLE_ADS_MCP_PUBLIC_ORIGIN optional explicit OAuth issuer/origin; defaults to https://GOOGLE_ADS_MCP_PUBLIC_HOST when a public host is configured

  • GOOGLE_ADS_MCP_AUTH_MODE optional Streamable HTTP auth mode, default oauth; set to static_bearer only for the legacy bearer-token fallback

  • GOOGLE_ADS_MCP_OAUTH_DB SQLite OAuth persistence path, default /var/lib/google-ads-mcp/oauth.db

  • GOOGLE_ADS_MCP_OWNER_USERNAME OAuth owner approval username

  • GOOGLE_ADS_MCP_OWNER_PASSWORD_HASH Argon2id hash for the OAuth owner approval password

  • GOOGLE_ADS_MCP_ACCESS_TOKEN_TTL_SECONDS OAuth access-token lifetime, default 3600

  • GOOGLE_ADS_MCP_AUTH_CODE_TTL_SECONDS OAuth authorization-code lifetime, default 300

  • GOOGLE_ADS_MCP_REFRESH_TOKEN_TTL_SECONDS OAuth refresh-token lifetime, default 2592000

  • GOOGLE_ADS_MCP_OAUTH_SECRET server-side HMAC secret for hashing OAuth tokens, authorization codes, client secrets, and owner sessions at rest

  • GOOGLE_ADS_MCP_HTTP_DIAGNOSTICS optional secret-free Streamable HTTP diagnostic logging, default 0

  • GOOGLE_ADS_MCP_AUTH_TOKEN optional bearer token used only when GOOGLE_ADS_MCP_AUTH_MODE=static_bearer

Development Checks

python -m compileall src tests
python -m unittest discover -s tests
ruff check .

Live Smoke Tests

Live tests are opt-in and read-only. They never run unless this flag is set:

GOOGLE_ADS_RUN_LIVE_TESTS=1 python -m unittest tests.integration.test_live_google_ads_smoke

The tests require valid credentials, a configured login MCC when using MCC discovery, and at least one explicitly allowed reporting customer ID.

Available Tools

6 tools
get_account_detailsA
Read-onlyIdempotent

Return details for one explicitly allow-listed Google Ads customer.

ParametersJSON Schema
NameRequiredDescriptionDefault
customer_idYes

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, and non-destructive behavior, so the safety profile is covered. The description adds the key behavioral constraint that only explicitly allow-listed customers are valid, and the verb 'Return' reinforces that this is a read operation.

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 one sentence with no filler, and the core constraint ('explicitly allow-listed') is front-loaded. Every word earns its place.

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?

Given a single required parameter and annotations that establish read-only/idempotent/non-destructive behavior, the description covers the essential precondition and target resource. The return payload is not described, but for a simple single-account lookup with no output schema, this is a minor gap.

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?

The single parameter's purpose is mostly inferable from its name and the description's 'Google Ads customer' phrasing, and the allow-list constraint adds meaning about valid values. However, with 0% schema coverage, the description still does not specify customer_id format (e.g., hyphens vs. plain numeric ID) or how to obtain an allow-listed ID.

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 ('Return') and names the exact resource ('details for one explicitly allow-listed Google Ads customer'). It clearly distinguishes this tool from list_accounts by scoping it to a single customer and adds the allow-listed constraint.

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 phrase 'explicitly allow-listed' serves as a clear precondition: the tool should only be used for customers that have been pre-approved. It does not explicitly name alternatives like list_accounts, but the single-customer scope provides enough context for selection.

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

get_campaign_costA
Read-onlyIdempotent

Return daily campaign cost rows for an explicitly allow-listed customer.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNo
end_dateYes
start_dateYes
customer_idYes
campaign_idsNo

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already carry the read-only, idempotent, and non-destructive profile, so the description's burden is lower. It adds meaningful context by disclosing the allow-list requirement and the daily-row granularity, which are behavioral constraints not present in the structured metadata.

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, tightly worded sentence with no filler. The core action and resource are front-loaded, and the access constraint is appended without bloating the text.

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

Completeness2/5

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

There is no output schema and no parameter documentation in the schema, so the description is the only source of operational detail. It fails to explain date range behavior, optional filters, return shape, or failure behavior for non-allow-listed customers, leaving important invocation details unspecified.

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%, so the description needed to compensate for missing parameter meaning. It only contextualizes customer_id through the allow-listed customer phrase; start_date, end_date, campaign_ids, and status semantics remain unexplained.

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 states a specific verb and resource: 'Return daily campaign cost rows'. The qualifier 'for an explicitly allow-listed customer' adds useful scope and distinguishes this from sibling tools like get_campaign_details or get_campaign_performance, which target different data.

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 when to use this tool: when daily campaign cost rows are needed. However, it does not explicitly mention alternatives, exclusions, or when another sibling tool would be more appropriate, so routing guidance is left mostly to inference.

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

get_campaign_detailsB
Read-onlyIdempotent

Return details for one campaign in an explicitly allow-listed customer.

ParametersJSON Schema
NameRequiredDescriptionDefault
campaign_idYes
customer_idYes

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and non-destructive behavior, so the safety profile is covered. The description adds the 'explicitly allow-listed customer' eligibility/auth context, but it does not disclose return shape, error behavior, or what happens for non-allow-listed customers.

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 sentence with no filler; it front-loads the verb and resource and adds one relevant constraint. The phrase 'explicitly allow-listed' is slightly awkward but still compact and purposeful.

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 two-parameter read tool with strong annotations, the description is minimally adequate but leaves gaps: return content is undefined, parameter semantics are undocumented, and there is no positioning against sibling tools. An agent can infer much from the schema and tool name, but the description alone does not fully support correct 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 mentions 'customer' and 'campaign' only generically without explaining what customer_id or campaign_id mean, how they relate, or what values are valid. The description does not compensate for the missing parameter documentation.

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 uses a specific verb ('Return') and resource ('details for one campaign'), and adds a scope constraint ('explicitly allow-listed customer'). It distinguishes from list_campaigns by saying 'one campaign' and from cost/performance siblings by saying 'details', though 'details' remains somewhat generic and siblings are not explicitly named.

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 use when a single campaign's general details are needed from an allow-listed customer, but it does not provide explicit when-to-use or when-not-to-use guidance. It also does not reference alternatives like get_campaign_cost, get_campaign_performance, or list_campaigns.

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

get_campaign_performanceC
Read-onlyIdempotent

Return campaign performance rows for one or more explicitly allow-listed customers.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNo
end_dateYes
start_dateYes
customer_idNo
campaign_idsNo
customer_idsNo

TDQS

C2.9/5.0
Behavior3/5

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

Annotations already declare read-only, idempotent, non-destructive behavior, so the safety profile is covered. The description adds a useful access-related detail about allow-listing, but it does not explain output shape, pagination, result limits, or how filters interact.

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 efficient sentence with no filler: the action and main constraint are front-loaded. It is appropriately concise, though slightly terse for a tool with six parameters.

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

Completeness2/5

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

With no output schema, six parameters, and overlapping sibling tools, the description is incomplete. It leaves important context implicit: what metrics 'performance' includes, how date parameters behave, how multiple customers are handled, and when this tool should be preferred over siblings.

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%, so the description carries the burden of explaining parameter meaning, but it only vaguely refers to customers. It does not clarify start_date/end_date formats, status filtering, campaign_ids, or how customer_id and customer_ids relate.

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 identifies the action ('Return'), the resource ('campaign performance rows'), and a key scoping constraint ('explicitly allow-listed customers'). It is distinguishable from siblings like get_campaign_details or get_campaign_cost, though it does not explicitly contrast itself with them.

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

Usage Guidelines2/5

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

There is no guidance about when to use this tool versus the sibling tools such as get_campaign_cost, get_campaign_details, or list_campaigns. The allow-listed customer phrase implies an access restriction, but prerequisites and alternative conditions are left unstated.

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

list_accountsA
Read-onlyIdempotent

Discover accessible Google Ads accounts using the configured MCC context.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior3/5

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

Annotations already cover the safety profile (readOnlyHint, idempotentHint, openWorldHint, destructiveHint false). The description adds useful scoping context—'accessible' accounts and MCC-based discovery—but does not disclose return format, pagination, or configuration prerequisites. Given the annotation coverage, this is a reasonable level of added context.

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, front-loaded sentence that states the action, resource, and scope without any filler. Its brevity is appropriate for a tool with no parameters and a simple discovery purpose.

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 zero-parameter, read-only list tool with rich annotations, the description is nearly complete. It does not explicitly state the return shape (e.g., list of account IDs and names) or mention that no filters are supported, but the tool's simplicity and clear focus keep the gap small.

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

Parameters4/5

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

The tool has zero parameters, and schema coverage is 100% by default. The description's reference to 'configured MCC context' clarifies the implicit environment/context, but there are no parameter semantics that need explanation. Baseline of 4 for zero parameters is appropriate.

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 ('Discover') and resource ('accessible Google Ads accounts'), and adds scope by mentioning the configured MCC context. This clearly distinguishes it from sibling tools like list_campaigns and get_account_details.

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 description clearly implies an account-discovery context and gives the relevant environmental framing ('configured MCC context'). However, it does not explicitly state when to use this tool versus siblings or mention exclusions, though the distinction is fairly obvious from the names.

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

list_campaignsA
Read-onlyIdempotent

List campaigns for one explicitly allow-listed Google Ads customer.

ParametersJSON Schema
NameRequiredDescriptionDefault
statusNo
customer_idYes
campaign_idsNo
channel_typeNo
campaign_name_containsNo

TDQS

A3.6/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, so the safety profile is well covered. The description adds useful context beyond annotations by emphasizing the authorization restriction and the single-customer scope.

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 sentence with no filler, front-loading the verb, resource, and key scope constraint. Every word earns its place.

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 description is adequate for a simple read-only list operation, especially with strong annotations and self-explanatory parameter names. However, with no output schema and 0% parameter coverage, it could still use more detail about return shape, optional filters, or guidance on selecting the right sibling tool.

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

Parameters1/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 any of the five parameters beyond the implicit customer scope. It provides no meaning or allowed values for status, campaign_ids, channel_type, or campaign_name_contains, leaving the agent to guess 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 states a specific verb ('List'), a concrete resource ('campaigns'), and a clear scope ('one explicitly allow-listed Google Ads customer'). This easily distinguishes it from sibling tools like list_accounts or get_campaign_details without needing to inspect schemas.

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 allow-listed customer constraint gives clear context about when the tool applies, and 'list' implies a broad listing use case. However, it does not explicitly mention when to prefer this over siblings like get_campaign_details, get_campaign_cost, or get_campaign_performance, so the routing guidance is only implied.

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. 6 tool updatesv0.1.0
    • First observedget_account_details
    • First observedget_campaign_cost
    • First observedget_campaign_details
    • First observedget_campaign_performance
    • First observedlist_accounts
    • First observedlist_campaigns

TDQS

A3.7/5.0

Scored across 6 tools

Disambiguation4/5

Tools are mostly distinct: list vs details pairs for accounts and campaigns are clear, and cost vs performance have separate purposes. There is minor potential confusion between get_campaign_cost and get_campaign_performance since cost could be considered part of performance, but the descriptions clarify the difference.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern with snake_case, such as list_accounts, get_account_details, and list_campaigns. The naming is predictable and makes the tool's purpose clear.

Tool Count5/5

Six tools is a well-scoped set for a Google Ads read-only gateway. Each tool covers a meaningful access or reporting need without unnecessary redundancy.

Completeness4/5

The tool surface covers core account discovery, campaign listing, details, cost, and performance reporting. It lacks deeper campaign management or ad-level reporting, but for a read-only function gateway the major workflows are covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides read-only access to Google Ads account data including campaigns, ad groups, keywords, and performance reports. Enables querying via GAQL through an MCP interface.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables reading and modifying Google Ads accounts, including campaign management, ad status changes, budget updates, and more.
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables reading and mutating Google Ads campaigns via MCP tools, and includes structured marketer workflows for search term mining, budget optimization, and weekly reviews.
    48 npm
    5
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    A read-only MCP server for the Google Ads API, exposing reporting tools for account summaries, campaigns, performance, search terms, and conversion actions. Enables natural-language queries to Google Ads data without write access.
    -