PerfoAds MCP

com.perfoads由 perfoads.com 發布v0.2.0-alpha.1更新於 Nov 8, 2026

Checks your Google Ads accounts (162 checks) and changes them only after you approve. Self-hosted.

已驗證STDIO僅桌面資料與分析商業與電商

概覽

AI 產生的概覽

自架式 MCP 伺服器,讓助理讀取 Google Ads 帳戶、執行 162 項帳戶檢查,且只在人工核准後修改帳戶。

可以做什麼
  • 檢查安裝環境並列出可存取的 Google Ads 帳戶,包含經理帳戶下的帳戶
  • 讀取帳戶總覽、廣告活動、廣告群組、關鍵字、搜尋字詞、預算、排除關鍵字、廣告、目標對象、轉換與變更記錄
  • 執行唯讀 GAQL 查詢並檢視資源欄位說明
  • 建立快照並執行 162 項確定性檢查,列出問題、證據與嚴重程度
  • 提出、核准、套用、復原並追蹤對預算、狀態、出價、排除關鍵字、關鍵字與廣告的變更
適用情境
適合想讓助理稽核 Google Ads 帳戶、說明哪些花費被浪費,並在明確核准下做有限變更,同時資料不經過第三方伺服器的情況。
執行需求
透過 npx 在本機執行(需 Node.js 22.13 以上)。需要自己的 Google Cloud 專案並啟用 Google Ads API、一個「桌面應用程式」類型的 OAuth 用戶端,以及已核准的 Explorer 存取權;核准前只能使用測試帳戶。可選環境變數 PERFOADS_MCP_HOME 指定設定資料夾。需要連線至 Google API 的網路。
安裝前請注意
會在本機設定資料夾中加密儲存 OAuth 用戶端 ID、密鑰與更新權杖;同一作業系統使用者下的任何程式都能讀取金鑰,若助理可存取終端機或瀏覽器,應採用加固設定。帳戶未列入 policy.yaml 的 accounts.write 前無法變更;核准須在本地頁面輸入代碼,AI 說「同意」不算核准。auto 等級可不逐次核准即暫停廣告活動或調降預算。目前為早期 alpha 版本。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 PerfoAds MCP,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[PerfoAds MCP]

PerfoAds MCP

A self-hosted MCP server that works with Google Ads. It lets an AI assistant (Claude Desktop, Claude Code, Cursor and other MCP apps) read your Google Ads accounts, run 162 deterministic account checks, and change things only with your approval and within limits you set in a policy file.

PerfoAds MCP works with Google Ads. It is not made, endorsed or supported by Google (not a Google product). It works with the Google Ads API through your own Google Cloud project and your own sign-in; nothing goes through anyone else's servers.

Package and CLI: perfoads-mcp. License: Apache-2.0. Status: early alpha. On npm: perfoads-mcp (0.2.0-alpha.1).

Work with Paweł

The checks say what is wrong: the evidence, how serious it is and the money it wastes. They do not say how to fix it. Want fixes, not just findings? PerfoAds adds fix plans; Paweł Wilk offers a free 1-hour call - perfoads.com

The safety promise

No change can be applied through MCP without a human approving it outside the model. On hosts where the agent also has a shell or controls the browser (Claude Code, Cursor, browser-control tools) this holds only with the hardened setup (server + store under a separate OS user or in a container, approval_mode: code_only and open_browser: false), and only if you do the human steps (your password, approving, policy edits) from a session the AI can't change: another account's login session, SSH from another device, or with the AI agent sandboxed. An AI with a shell on your user can change your shell startup files, fake sudo and capture what you type.

The one exception is one you make yourself: the auto tier in your policy file (off unless you set it, and only for pausing a campaign or lowering a budget).

What this means in practice:

  • Changes are off until your policy file lists an account under accounts.write.
  • Google checks the change first without applying it (validate only). Then it is shown to you on a local approval page with the account name, id and currency first, the exact change, every campaign it reaches, and the AI's reason in a separate "unverified" box.
  • You approve with the 6-character code from that page (typed into your AI app's form or a terminal), or a click on the page in apps without forms. An AI saying "yes" approves nothing.
  • The server applies the change once, reads it back from Google, and logs every step in a hash-chained audit log. Most changes can be undone within 30 days (as a new approved change); see the per-type table.

Read docs/THREAT-MODEL.md for what this protects against and what it does not.

Requirements

  • Node.js 22.13 or later (it uses the built-in node:sqlite).
  • Your own Google Cloud project with the Google Ads API enabled.
  • An OAuth client of type "Desktop app" in that project.
  • Explorer access to the Google Ads API, applied for in Google Cloud Console for that project. Until it is approved, only Google Ads test accounts work. No developer token is needed.

Step by step: docs/SETUP.md.

Install and connect

Always name the exact version (@0.2.0-alpha.1, not @latest or none), so npm can't hand you a different release. init asks for your OAuth client id and secret, opens Google's sign-in in your browser, saves the sign-in encrypted, writes a read-only starter policy, runs doctor, and prints the config for your AI app.

Or build from source:

sh
git clone https://github.com/vilovibe/perfoads-mcp.gitcd perfoads-mcppnpm install --frozen-lockfile && pnpm buildnode packages/cli/bin/perfoads-mcp.js init

init then prints your AI app's config for that copy (full path to node and to packages/cli/bin/perfoads-mcp.js). Wherever these docs say npx perfoads-mcp@<version> <command>, run that node command instead.

The AI app config (init prints it with your version filled in; for a source build it prints the node form):

Claude Desktop (Settings > Developer > Edit Config, claude_desktop_config.json), Cursor (~/.cursor/mcp.json):

json
{  "mcpServers": {    "perfoads-mcp": {      "command": "npx",      "args": ["-y", "perfoads-mcp@<version>", "serve"]    }  }}

Claude Code:

sh
claude mcp add --scope user perfoads-mcp -- npx -y perfoads-mcp@<version> serve

Or as a Claude Code plugin (it pins the release version; claude-plugin/):

/plugin install perfoads --marketplace vilovibe/perfoads-mcp

ChatGPT desktop app and Codex: they run local MCP servers too; see docs/SETUP.md.

More: examples/ (Claude Desktop, Claude Code, Cursor, a starter policy). A Claude Desktop extension (.mcpb) can be built with node scripts/build-mcpb.mjs; run init in a terminal first, the extension uses the same settings folder.

Then ask your AI app: "run doctor", "list my Google Ads accounts", "audit account 123-456-7890".

Quick tour

What to ask your AI, step by step: docs/USING.md. Full list with every input: docs/TOOLS.md (generated from the server).

  • Setup and accounts: doctor (checks the setup and says what to fix), list_accounts (incl. accounts under manager accounts).
  • Reads: get_account_overview, get_campaigns, get_ad_groups, get_keywords, get_search_terms, get_budgets_and_bidding, get_negatives, get_ads_and_assets, get_audiences, get_conversions, get_change_history, keyword_ideas, describe_resource, and query_gaql (one read-only GAQL SELECT). Summary first, then one page of rows and a cursor. Money is a decimal string in the account currency.
  • Checks: build_snapshot reads what the checks need into a local snapshot, run_checks runs them (findings with evidence; "not run" with the reason, never "clean" when data was missing), explain_check. Prompt weekly_audit. Catalog: docs/CHECKS.md.
  • Changes: propose_change, apply_change, undo_change, get_change, list_changes, resolve_change. Types: budgets, campaign status, bidding targets, negatives (campaign and shared lists), keywords, pausing keywords, responsive search ad text, sitelinks, callouts, audience exclusions, and creating a new Search campaign from plain fields (always made paused; undo only while it never ran).

Text that comes from Google or the AI (names, search terms, ad texts, errors) is wrapped as {"untrusted": "..."} so the AI treats it as data, not instructions.

How a change works

  1. Propose - the AI suggests a change. The server reads the live values, checks your policy, and asks Google to validate it without changing anything.
  2. Approve - the server opens a local approval page (127.0.0.1, one-time link, 10 minutes), or, with open_browser: false, you open it yourself with approvals open <id> in a terminal. You approve with its code, or reject.
  3. Apply - sent once. If the account changed since you approved, nothing is sent (stale). An unclear answer from Google is never re-sent: it is re-checked by reading the account.
  4. Read back - the server reads the account again to confirm the change landed (verified), or says loudly that it did not.
  5. Log and undo - every step is in the audit log (changes://log); undo_change proposes the reverse as a new change, with its own approval.

Approval paths and approval_mode: see docs/HARDENED-SETUP.md.

The policy file

policy.yaml in the settings folder decides which accounts the AI may read and change and how far: per-account spend limits in the account's currency, budget and target step limits, writes per minute and day, tiers per change type (auto, confirm, blocked), brand terms that are never negated, a Google Ads operations reserve for changes, and a kill switch. It is re-read on every check, and no tool can edit it.

sh
npx perfoads-mcp@<version> policy check   # is it valid, and what does it allow?npx perfoads-mcp@<version> policy show    # what the server uses now

Every key, range and default, with examples: docs/POLICY.md. Starter file: examples/policy.example.yaml.

What is stored, and how to delete it

Everything lives in one settings folder, never in your project: ~/.config/perfoads-mcp (or $XDG_CONFIG_HOME/perfoads-mcp), %APPDATA%\perfoads-mcp on Windows. Change it with PERFOADS_MCP_HOME, but keep it outside any folder the AI works in (doctor checks).

  • store.sqlite - your OAuth client id, client secret and refresh token (each encrypted, AES-256-GCM), the list of your accounts, proposed and applied changes, approval sessions (codes only as hashes), the audit log, Google Ads operations used, and every policy version loaded.
  • snapshots/ - account snapshots for the checks (gzipped JSON, owner-only, kept 24 hours, at most 20). They hold account data such as campaign names and search terms.
  • policy.yaml - your change policy. config.json - optional approval_mode, open_browser, retention_days and pro_plugin (an absolute path to a separately installed plugin package, loaded at start; a plugin runs as code with the server's rights, so set it only for a package you trust; it is refused when anyone but the user running the server, or root, can change its files or a folder above them; its text comes back wrapped as untrusted; doctor shows whether it loaded and why not).
  • Kept for 180 days by default ("retention_days" in config.json, 30 to 3650): when the server starts, changes in a final state (verified, failed, rejected, expired, stale, settled, abandoned), finished approval sessions, policy versions nothing still refers to, and the oldest audit log entries, all older than that, are deleted. Unfinished changes (proposed, approved, applying, in doubt, applied but not yet checked) and anything they need are never deleted. Before deleting, the server writes a retention_pruned entry to the audit log with what it removes and a checkpoint (the number and hash of the last entry removed), so the rest of the log still verifies. Snapshot temp files from interrupted saves go after 1 hour.
  • The encryption key: macOS Keychain, the Linux keyring (secret-tool), or a key file (key) in the settings folder (always the key file on Windows).
  • Owner-only, per OS: on macOS and Linux the store files, the key file, snapshot files and the starter policy.yaml are created 0600 and snapshot folders 0700 (a config.json you write yourself keeps the mode you give it). On Windows file modes do nothing, so each time the store opens the server sets an ACL on the whole settings folder with %SystemRoot%\System32\icacls.exe: inheritance from the parent removed, full control for your Windows user only, applied to everything already in it and inherited by everything created later (store, key file, snapshots, policy.yaml, config.json); the key file, snapshot folders and files and the starter policy get it again when they are written. doctor warns on Windows when the settings folder is outside %APPDATA%, and fails (with the fix) when icacls could not make it owner-only.

Tokens are encrypted at rest and never written to logs or shown in tool results. Encryption protects a copy of the folder (a backup, a synced folder). It does not protect against programs running as your own OS user: the Keychain entry, the keyring entry and the key file can be read by any process of the same user without a prompt, including an AI agent with a terminal. The hardened setup closes that, within its stated limits (human steps from a session the AI can't change).

Disconnect and delete: npx perfoads-mcp@<version> reset asks Google to revoke the saved sign-in (best effort; it says whether Google confirmed), then deletes the sign-in and its key (it asks first). Delete the settings folder to remove everything else. If reset says Google did not confirm, remove the app's access at myaccount.google.com/permissions.

Nothing is sent anywhere except to Google's APIs. No telemetry.

Commands

perfoads-mcp init                    guided setupperfoads-mcp auth                    sign in again (uses the saved OAuth client)perfoads-mcp doctor                  check the setupperfoads-mcp reset                   delete the saved sign-in and its key (asks first)perfoads-mcp serve                   run the MCP server over stdio (AI apps start this)perfoads-mcp approve <change_id>     approve with the code from the approval page (terminal only)perfoads-mcp reject <change_id>      reject a proposed change (terminal only)perfoads-mcp approvals open <id>     show a change's approval page from this terminalperfoads-mcp policy check [file]     check a policy file and say what it allowsperfoads-mcp policy show             the change policy the server uses now

Use npx perfoads-mcp@<version> <command> (or the command init printed) if it is not installed globally.

Known limits

  • Alpha. The Google Ads queries and writes are tested against recorded and fake replies. Live runs passed on a Google Ads test account (2026-10-25, every change type with its undo) and read-only on 4 real accounts (2026-10-26), and every change type with its undo on a real account (2026-11-01, $0 spent). Coverage per change type: docs/E2E-COVERAGE.md.
  • Same OS user, no hardened setup: an AI with a shell can read the store and key, start its own copy of the server, edit the policy, and fake a terminal (approve and approvals open are a convenience, not a guard). An AI that controls your browser can read the approval page, code included. Until you open it, the page link is visible for a moment in the browser opener's command line (ps) and stays in browser history; it stops working after the first open.
  • Access levels: Explorer access allows 2,880 operations a day on live accounts (15,000 on test accounts, counted separately) and no Keyword Planner (keyword_ideas needs Basic or Standard). The server keeps a share of each day's operations for approved changes.
  • OAuth app in "Testing" mode: Google ends the sign-in after 7 days. init and doctor warn; publish the app to production to avoid it (docs/SETUP.md).
  • Unclear outcomes: changes to portfolio bidding strategies and shared negative lists can't be settled from Google's change history; after an unclear answer they stay in_doubt until the new value shows, you settle them (resolve_change), or 30 days pass.
  • Change history reads at most 28 days. Every tool answers within about 4 minutes; very large accounts get samples, marked as such, and checks on a sample never say "clean".
  • Not supported yet: ChatGPT on the web (needs a remote server; the ChatGPT desktop app and Codex work), remote or team use, Merchant Center, billing.

Privacy Policy

PerfoAds MCP runs on your computer. Your Google Ads data goes only between your computer and Google; nothing is sent to PerfoAds, and there is no telemetry. What is stored locally, for how long, and how to delete it: What is stored, and how to delete it. Full policy: https://perfoads.com/privacypolicy

Terms of service: https://perfoads.com/termsofservice (the PerfoAds MCP section: free, as is, no warranty, Apache-2.0, your own Google account and costs, you approve every change).

Contact: [email protected] for privacy and security questions (security reports: see SECURITY.md). Bugs and support: https://github.com/vilovibe/perfoads-mcp/issues

Docs

Packages: ads-client, reads, snapshot, checks, changes, policy, store, mcp-server, cli (see packages/). Contributing: CONTRIBUTING.md.

Licence

Apache License 2.0. See LICENSE and NOTICE.

  • You are free to use, change, share and sell it, for any purpose.
  • Keep the licence and the NOTICE file with any copy you share, and say what you changed.
  • It comes with no warranty.

Google Ads is a trademark of Google LLC.

來源:README.md,提交 ce171c8

工具

25

doctor

Check setup

Checks the setup step by step: Node.js, where settings are stored, the saved Google sign-in, Google Ads API access level and limits, and risky Claude Code hooks. Each line says ok, warn, fail or skipped in plain English, with a fix; exact commands and file paths are in each check's details.

無需輸入參數。

list_accounts

List Google Ads accounts

Lists every Google Ads account this sign-in can work on, including accounts under manager (MCC) accounts, with currency, time zone and the login-customer-id each one needs.

輸入參數refresh

query_gaql

Run a read-only GAQL query

Runs one Google Ads Query Language SELECT on one account. Read-only: anything but a single SELECT on a known resource is refused. LIMIT is added (200) or lowered (max 1000). Returns a summary first, then up to max_rows rows; next_cursor fetches the rest. Costs 1 Google Ads operation per call.

輸入參數query必填cursormax_rowscustomer_id必填

get_account_overview

Account overview

One account at a glance: name, currency, time zone, totals for the date range (cost, clicks, conversions, value, ROAS), campaigns per channel. Example: {"customer_id":"123-456-7890"}

輸入參數date_rangecustomer_id必填

get_campaigns

Campaigns

Campaigns that are not removed, plus removed ones that had activity in the date range: status, channel, budget, bidding, targets, metrics, Search impression share; highest cost first. Example: {"customer_id":"123-456-7890","date_range":"LAST_7_DAYS"}

輸入參數cursorstatusmax_rowsdate_rangecampaign_idcustomer_id必填

get_ad_groups

Ad groups

Ad groups (not removed): status, type, CPC bid, metrics; highest cost first. Example: {"customer_id":"123-456-7890","campaign_id":"111"}

輸入參數cursorstatusmax_rowsdate_rangead_group_idcampaign_idcustomer_id必填

get_keywords

Keywords

Keywords with impressions: match type, quality score parts, bids, page CPC estimates, metrics; highest cost first. Example: {"customer_id":"123-456-7890","ad_group_id":"222"}

輸入參數cursorstatusmax_rowsdate_rangead_group_idcampaign_idcustomer_id必填

get_search_terms

Search terms

Search terms with clicks (Search + Performance Max) with triggering keyword, status and metrics; highest cost first; cost on terms with no conversions. Example: {"customer_id":"123-456-7890","date_range":"2026-09-01..2026-09-30"}

輸入參數cursormax_rowsdate_rangead_group_idcampaign_idcustomer_id必填include_pmax_categories

get_budgets_and_bidding

Budgets and bidding

Budgets with the campaigns sharing each, and portfolio bid strategies with their targets and campaigns: what a budget or strategy change touches. Example: {"customer_id":"123-456-7890"}

輸入參數cursormax_rowscustomer_id必填

get_negatives

Negatives

Negative keywords on ad groups and campaigns, shared negative lists (keywords, links) and account-level negatives and exclusions. Example: {"customer_id":"123-456-7890"}

輸入參數cursormax_rowscustomer_id必填

get_ads_and_assets

Ads and assets

Ads (RSA texts with pinning and labels, URLs, ad strength, policy) and asset links (sitelinks, callouts, snippets, images...) on account, campaigns, ad groups. Example: {"customer_id":"123-456-7890","kind":"ads"}

輸入參數kindcursorstatusmax_rowsdate_rangead_group_idcampaign_idcustomer_id必填

get_audiences

Audiences

User lists (type, status, size, targeted or not) and audience targeting/exclusions on campaigns and ad groups, with metrics. Example: {"customer_id":"123-456-7890"}

輸入參數kindcursormax_rowsdate_rangead_group_idcampaign_idcustomer_id必填

get_conversions

Conversions

Conversion actions (status, category, primary, counting, attribution, values) with conversions in the range; account/campaign goals; value rules. Example: {"customer_id":"123-456-7890"}

輸入參數cursormax_rowsdate_rangecustomer_id必填

get_change_history

Change history

Who changed what, when (1-28 days; Google keeps 30): resource, operation, fields, tool; users as "user at domain". Example: {"customer_id":"123-456-7890","days":7}

輸入參數dayscursormax_rowscampaign_idcustomer_id必填resource_type

keyword_ideas

Keyword ideas

Keyword Planner ideas from seed keywords and/or a URL: searches, competition, bids. Needs Basic/Standard access. Example: {"customer_id":"123-456-7890","keywords":["trail running shoes"]}

輸入參數urlkeywordslanguagecustomer_id必填max_resultsgeo_target_idsinclude_search_partners

describe_resource

Describe a GAQL resource

Fields of one GAQL resource from Google's field catalogue (type, selectable, filterable, sortable, enum values) plus the metrics and segments you can select with it. Example: {"resource":"campaign"}

輸入參數cursormax_rowsresource必填

build_snapshot

Build account snapshot

Reads everything the checks need (90 days) into a saved snapshot (kept 24 h; a complete one under 60 min old is reused unless refresh:true). Returns its id and how each read went; failed or cut reads are said. Then run_checks. Example: {"customer_id":"123-456-7890"}

輸入參數checksrefreshcustomer_id必填

run_checks

Run account checks

Runs the checks on a snapshot (snapshot_id) or on customer_id (reuses one under 60 min old unless refresh). Findings first, with evidence and severity, paged by next_cursor; after the last finding: info (not faults), clean, not_run (why) and suppressed (why). For an audit, call again with next_cursor until it is null and report every finding in the audit report format (server instructions, weekly_audit prompt), never only the main points. A keyword id looks like "<ad_group_id>~<criterion_id>"; pause_keyword takes it as two fields (ad_group_id, criterion_id). Example: {"snapshot_id":"snap_..."}

輸入參數cursordetailrefreshsettingsbrand_termscustomer_idsnapshot_idclient_context

explain_check

Explain a check

What one check looks for, the data it needs, its settings with defaults, and when it cannot run. Tell the user in plain words what it looks for and what it needs; do not suggest settings overrides or how to fix things. Example: {"check_id":"BB02"}

輸入參數check_id必填

propose_change

Propose a change

Builds one change, checks it with Google (validate only), never applies. params schema per type: schema://changes/{type}. Returns diff, risk, change_id; then apply_change.

輸入參數type必填params必填reasoncustomer_id必填

apply_change

Apply an approved change

Sends a proposed change once a person approves it: opens an approval page in the user's browser (approve there, or type its code when asked), sends once, reads back.

輸入參數change_id必填

undo_change

Propose an undo

Proposes a change restoring the values from before an applied change. Needs its own approval via apply_change.

輸入參數change_id必填

get_change

Get a change

One change: status in plain words, diff, approval, result. An in_doubt change is re-checked by reading.

輸入參數change_id必填

list_changes

List changes

Recent changes, newest first: id, type, status in plain words, account, a one-line summary and times. get_change shows one change in full.

輸入參數limitstatuscustomer_id

resolve_change

Settle an unclear change

Settles an in_doubt change after the user checked Google Ads (outcome + note). A person confirms on the approval page or types its code. Sends nothing.

輸入參數note必填outcome必填change_id必填

版本歷史

1
  1. v0.2.0-alpha.1最新Nov 8, 2026

評分

已滿足適用檢查項的 90%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分
  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具必需15 分
  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分