# Search Term Pro — MCP connector > Find and block wasted spend in Google Ads from your own Claude. Hosted MCP server; the analysis is deterministic and free, the judgement runs on your subscription. ## What this is Search Term Pro is a hosted MCP server that finds wasted spend in a Google Ads account and blocks it. Installed as a connector, it lets your own Claude read an account's search terms, decide which ones are waste, and write negative keywords back — with every write checked server-side first. The division of labour is the point. Pulling the account, filtering it, verifying a proposed negative, and applying it are all deterministic, and they happen here. The single judgement call — is this search term waste for this advertiser? — happens in your model, on your subscription. That is why this tier is free: the expensive step isn't ours. ## Install Sign in at /claude to mint a token, then run the line it gives you. The token is shown once. ``` Claude Code claude mcp add --header "Authorization: Bearer stp_mcp_YOUR_TOKEN" \ --transport http search-term-pro https://app.twospouts.com/api/mcp ``` You need a Search Term Pro account and at least one connected Google Ads account. You do NOT need a Google Ads API developer token, an OAuth client, or anything deployed — the server is hosted and holds the Ads credentials for you. ## Protocol | | | | --- | --- | | Endpoint | https://app.twospouts.com/api/mcp | | Transport | Streamable HTTP (JSON-RPC 2.0 over POST, single JSON response) | | Protocol version | 2025-06-18 | | Auth | Authorization: Bearer stp_mcp_… — required on every method except initialize and ping | | Capabilities | tools | A tool that fails returns a normal result with isError: true and a readable message, not a JSON-RPC error — so the model can correct a bad account id or ask the user to connect an account instead of the turn aborting. JSON-RPC errors are reserved for malformed requests and unknown methods. ## Tools | Tool | Arguments | Returns | | --- | --- | --- | | list_accounts | — | Connected accounts: account_id, name, website, customer_id, status, and any schedule. Call this first — every other tool needs an account_id from here. | | connect_account | customer_id, brand, website — all optional | With no arguments, the ad accounts the workspace's stored Google sign-ins already reach. Connecting one of those needs no browser: call again with its customer_id plus brand and website. A sign-in URL comes back too, for accounts under a Google login the workspace doesn't have yet. | | disconnect_account | account_id, confirm | Removes an account from the workspace, with its reviews, change log and schedule. Permanent, so confirm must repeat the account's name back. Google Ads itself is untouched. | | get_search_terms | account_id, days (default 30) | The candidates worth reviewing, plus the context needed to judge them: brand terms, active keywords, converting queries, negative lists, and the operator's own notes and rules. | | add_negatives | account_id, negatives[] (text, match_type, list, reason; optional campaign, ad_group) | Applies the negatives that pass verification. Returns what was applied and, for each rejection, the reason. A shared list is the default and preferred home; campaign or ad_group writes the negative at that level instead, for exclusions that should not apply account-wide. | | audit_negative_lists | account_id | Findings on the negatives already in the account: ones blocking active keywords, lists attached to nothing, duplicates, and negatives another already covers. | | set_schedule | account_id, cadence (daily/weekly/monthly/off), auto_apply | Saves the preference and returns the exact recurring task to create on your side. | ## What get_search_terms removes before you see it The candidate set is not the whole window. Four filters run first, all free, and on a real account they remove most of it — a recent 7-day pull went from 131 terms to 4 candidates. What comes back is only what a human would actually have to think about: - Terms with no spend — nothing to save. - Terms that converted — converted traffic is not waste, whatever it looks like. Fractional conversions count. - Terms matching the advertiser's own brand, resolved from the site rather than typed in. - Terms an existing negative already blocks, checked per campaign against shared lists and campaign-level negatives. Candidates come back sorted by cost, so the first ones you read are the ones worth the most. ## What is checked before anything is written add_negatives re-pulls the account and re-verifies every negative before writing. This runs regardless of which model proposed it, and it is why attempting an apply is safe: a bad proposal is rejected with a reason, not applied. - Would it block a keyword the advertiser actively bids on? Rejected, naming the keyword. - Would it block a query that has converted? Rejected, naming the query and its conversions. - Is it already in one of the account's negative lists? Rejected as a duplicate. - Does it match the advertiser's own brand? Rejected. - Is it valid Google Ads negative text — length, word count, no stray brackets or quotes? Rejected if not. The re-pull matters as much as the checks: a classification made hours or days ago is verified against the account as it is now, so a term that has converted since is caught rather than blocked. A negative aimed at one campaign or ad group is checked against that scope rather than the whole account: it must not block a keyword or converting query in that campaign, and it is rejected if the same negative is already there. The account-wide duplicate check is deliberately skipped for these — the same text sitting in a shared list that doesn't reach the campaign is not a duplicate of a negative meant for it. Everything applied is recorded in the account's change log with the resource name that undoes it, so any single negative can be reverted from the dashboard. ## Scheduling, and its one real limit set_schedule saves the cadence and whether to apply automatically, then returns the exact recurring-task prompt to create. It has to work this way: an MCP server is passive — it answers when called and cannot wake anything up. Nothing here can reach into your Claude and create a schedule for you. So a scheduled review is a recurring task on your side, running on your subscription, and it runs when your Claude runs. If you want reviews that happen unattended at 6am whether or not anything of yours is awake, that is what the paid dashboard does — it runs the same analysis on our inference and bills credits for it. ## How this differs from Google's own Ads MCP server Google publishes an official MCP server for the Google Ads API (github.com/googleads/google-ads-mcp). It is a different kind of thing, and for a lot of work it is the right one — the two are complementary rather than competing. | | Google's official server | This server | | --- | --- | --- | | Shape | A thin wrapper over the Ads API: search (GAQL), get_resource_metadata, list_accessible_customers | Task-shaped: get the terms worth reviewing, add negatives, audit the lists | | Writes | Read-only | Writes negatives, behind a deterministic safety gate | | Hosting | You run it — pipx locally, or your own Cloud Run deploy | Hosted. Nothing to deploy | | Credentials | Your own Google Ads developer token, OAuth client, and ADC | Sign in with Google once; the developer token is ours | | Opinion | None. You write the GAQL and interpret the rows | Opinionated: it knows what a wasted term is and what makes a negative unsafe | The practical differences are the developer token and the guard rails. Google's server needs one of your own, which means applying to Google and waiting for approval before you can call anything; this one uses ours, so installing is a token and a paste. And because Google's server is read-only and unopinionated, a model using it to manage negatives has to invent both the definition of waste and the safety checks — nothing stops it proposing a negative that blocks a keyword the advertiser is actively bidding on. That check is most of what this server is. Use Google's for open-ended reporting and any part of the API this doesn't cover. Use this one for search-term work you intend to act on. ## Cost The connector is free and unmetered: unlimited accounts, unlimited reviews, no credits consumed. Nothing on this server calls a language model, so there is nothing for us to bill. The tokens your model spends reading the candidates and deciding on them are billed by Anthropic to your own plan, as with any other conversation. A large account returns a large payload — a few thousand candidates plus context is tens of thousands of tokens in your context window. Narrow the window with the days argument if that matters to you. ## Links - Install command (sign-in required): https://app.twospouts.com/claude - Human documentation: https://app.twospouts.com/docs/mcp - MCP endpoint: https://app.twospouts.com/api/mcp