google-ads-function-gateway
This server is a read-only Google Ads Function Gateway MCP server that exposes six predefined, allow-list-enforced catalogue functions for discovering and reporting on Google Ads accounts and campaigns.
List accessible Google Ads accounts under the configured MCC context.
Get details for a single explicitly allow-listed Google Ads customer.
List campaigns for a customer, optionally filtered by status, campaign IDs, channel type, or name substring.
Get details for one campaign in an allow-listed customer.
Get daily campaign cost rows for a customer over a date range, optionally filtered by status or campaign IDs.
Get campaign performance rows for one or more allow-listed customers over a date range, optionally filtered by status or campaign IDs.
Access the same capabilities through the CLI or through MCP over stdio and Streamable HTTP transports.
No write/mutation operations, no arbitrary GAQL, and reporting customer IDs must be explicitly authorized.
Provides read-only access to Google Ads reporting data through predefined functions for account details, campaign listing/details, cost, and performance metrics, with explicit customer ID authorization.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@google-ads-function-gatewayShow me campaign cost for customer 1112223333 from 2026-08-30 to 2026-08-30"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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-clientB. Create Virtual Environment
python3 -m venv .venv
source .venv/bin/activateC. 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 .envNever 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-meIf you do not already have a refresh token, generate one locally:
python -m google_ads_function_gateway oauth-generate-refresh-tokenThe 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=1234567890The 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 doctorDoctor prints readiness statuses and package/API version details without printing secrets.
H. Discover Accounts
python -m google_ads_function_gateway list-accountsDiscovery 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,4445556666Reporting 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-30Each 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=0Generate 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: ")))
PYStart 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-httpDefault endpoint:
http://127.0.0.1:8000/mcpThe 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/revokeThe 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.readoffline_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-httpExample 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-tokenThen 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.comCurrent production MCP endpoint:
https://googleads-mcp.thebesads.com/mcpThe 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/mcpAuthentication: 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_TOKENGOOGLE_ADS_CLIENT_IDGOOGLE_ADS_CLIENT_SECRETGOOGLE_ADS_REFRESH_TOKENGOOGLE_ADS_LOGIN_CUSTOMER_IDGOOGLE_ADS_ALLOWED_CUSTOMER_IDSGOOGLE_ADS_API_VERSIONoptional; when omitted, the gateway uses the highest API version packaged bygoogle-ads-pythonGOOGLE_ADS_RETRY_ATTEMPTSoptional, default3GOOGLE_ADS_RUN_LIVE_TESTSoptional; set to1only when intentionally running live read-only testsGOOGLE_ADS_MCP_HOSToptional Streamable HTTP bind host, default127.0.0.1GOOGLE_ADS_MCP_PORToptional Streamable HTTP bind port, default8000GOOGLE_ADS_MCP_PUBLIC_HOSToptional public HTTPS tunnel or reverse-proxy hostname allowed by MCP DNS-rebinding protectionGOOGLE_ADS_MCP_PUBLIC_ORIGINoptional explicit OAuth issuer/origin; defaults tohttps://GOOGLE_ADS_MCP_PUBLIC_HOSTwhen a public host is configuredGOOGLE_ADS_MCP_AUTH_MODEoptional Streamable HTTP auth mode, defaultoauth; set tostatic_beareronly for the legacy bearer-token fallbackGOOGLE_ADS_MCP_OAUTH_DBSQLite OAuth persistence path, default/var/lib/google-ads-mcp/oauth.dbGOOGLE_ADS_MCP_OWNER_USERNAMEOAuth owner approval usernameGOOGLE_ADS_MCP_OWNER_PASSWORD_HASHArgon2id hash for the OAuth owner approval passwordGOOGLE_ADS_MCP_ACCESS_TOKEN_TTL_SECONDSOAuth access-token lifetime, default3600GOOGLE_ADS_MCP_AUTH_CODE_TTL_SECONDSOAuth authorization-code lifetime, default300GOOGLE_ADS_MCP_REFRESH_TOKEN_TTL_SECONDSOAuth refresh-token lifetime, default2592000GOOGLE_ADS_MCP_OAUTH_SECRETserver-side HMAC secret for hashing OAuth tokens, authorization codes, client secrets, and owner sessions at restGOOGLE_ADS_MCP_HTTP_DIAGNOSTICSoptional secret-free Streamable HTTP diagnostic logging, default0GOOGLE_ADS_MCP_AUTH_TOKENoptional bearer token used only whenGOOGLE_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_smokeThe tests require valid credentials, a configured login MCC when using MCC discovery, and at least one explicitly allowed reporting customer ID.
Available Tools
6 toolsget_account_detailsARead-onlyIdempotent
Return details for one explicitly allow-listed Google Ads customer.
| Name | Required | Description | Default |
|---|---|---|---|
| customer_id | Yes |
TDQS
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.
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.
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.
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.
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.
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_costARead-onlyIdempotent
Return daily campaign cost rows for an explicitly allow-listed customer.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | ||
| end_date | Yes | ||
| start_date | Yes | ||
| customer_id | Yes | ||
| campaign_ids | No |
TDQS
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.
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.
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.
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.
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.
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_detailsBRead-onlyIdempotent
Return details for one campaign in an explicitly allow-listed customer.
| Name | Required | Description | Default |
|---|---|---|---|
| campaign_id | Yes | ||
| customer_id | Yes |
TDQS
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.
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.
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.
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.
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.
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_performanceCRead-onlyIdempotent
Return campaign performance rows for one or more explicitly allow-listed customers.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | ||
| end_date | Yes | ||
| start_date | Yes | ||
| customer_id | No | ||
| campaign_ids | No | ||
| customer_ids | No |
TDQS
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.
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.
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.
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.
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.
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_accountsARead-onlyIdempotent
Discover accessible Google Ads accounts using the configured MCC context.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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_campaignsARead-onlyIdempotent
List campaigns for one explicitly allow-listed Google Ads customer.
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | ||
| customer_id | Yes | ||
| campaign_ids | No | ||
| channel_type | No | ||
| campaign_name_contains | No |
TDQS
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.
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.
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.
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.
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.
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.
6 tool updates
v0.1.0- First observed
get_account_details - First observed
get_campaign_cost - First observed
get_campaign_details - First observed
get_campaign_performance - First observed
list_accounts - First observed
list_campaigns
TDQS
Scored across 6 tools
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.
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.
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.
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
Related MCP Connectors
Read-only MCP access to your DEXUN AdWhiz account: ad accounts, AI recommendations, savings.
Hosted Google Ads MCP with OAuth, bounded reads, and prepare/confirm writes.
Google Ads analysis and operations — read performance, manage keywords, bids, and campaigns.
Google Ads MCP with 20,000+ account peer context and staged approve-then-execute writes.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceProvides 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
- AlicenseNot gradedqualityCmaintenanceEnables reading and modifying Google Ads accounts, including campaign management, ad status changes, budget updates, and more.MIT
- AlicenseNot gradedqualityDmaintenanceEnables reading and mutating Google Ads campaigns via MCP tools, and includes structured marketer workflows for search term mining, budget optimization, and weekly reviews.48 npm5MIT
- FlicenseNot gradedqualityCmaintenanceA 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.-