/ README.md
README.md
1 <p align="center"> 2 <picture> 3 <source media="(prefers-color-scheme: dark)" srcset="docs/img/logo-dark.png"> 4 <img src="docs/img/logo.png" alt="mureo" width="300"> 5 </picture> 6 </p> 7 8 <p align="center"> 9 <a href="https://mureo.io">Website</a> · 10 <a href="README.ja.md">日本語</a> 11 </p> 12 13 <p align="center"> 14 <a href="https://pypi.org/project/mureo/"><img alt="PyPI" src="https://img.shields.io/pypi/v/mureo.svg"></a> 15 <a href="https://pypi.org/project/mureo/"><img alt="Python versions" src="https://img.shields.io/pypi/pyversions/mureo.svg"></a> 16 <a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache%202.0-blue.svg"></a> 17 <a href="https://github.com/logly/mureo/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/logly/mureo/actions/workflows/ci.yml/badge.svg"></a> 18 </p> 19 20 **mureo** — Local-first, safety-gated AI ad-ops framework for Claude Code, Codex, Cursor & Gemini. 21 22 Strategy-aware agents that autonomously analyze and operate Google Ads, Meta Ads, Search Console & GA4 — **credentials never leave your machine**. 23 24 ## What is mureo? 25 26 mureo is a framework for AI agents to autonomously operate ad accounts. Once installed, AI agents (Claude Code, Cursor, Codex, Gemini, etc.) can work across Google Ads, Meta Ads, Search Console, and GA4 -- running campaign diagnostics, search term analysis, budget evaluation, ad validation, and more. Every operation is grounded in your business strategy (`STRATEGY.md`), so the agent makes decisions based on your persona, USP, goals, and brand voice -- not just raw metrics. 27 28 mureo also learns. When you correct the agent's analysis or share an operational insight, `/learn` saves it to a persistent knowledge base. That knowledge is automatically loaded in every future session, so the agent gets increasingly attuned to your account's specific patterns and makes better decisions over time. 29 30 31 ## Features 32 33 ### Strategy-driven decisions 34 35 Every operation starts from `STRATEGY.md` -- your persona, USP, brand voice, goals, and operation mode. The agent doesn't just optimize metrics; it optimizes toward your business objectives. 36 37 ``` 38 /creative-refresh reads your Persona and USP before drafting a single headline. 39 /budget-rebalance checks your Operation Mode before shifting a single dollar. 40 /rescue cross-references your Goals before recommending what to fix first. 41 ``` 42 43 ### Cross-platform analysis 44 45 mureo orchestrates across Google Ads, Meta Ads, Search Console, and GA4 in a single workflow: 46 47 - `/daily-check` -- pulls delivery status, ad performance, organic search trends, and site behavior across all platforms, then correlates them into one health report. 48 - `/search-term-cleanup` -- compares paid keywords against organic rankings to eliminate wasteful overlap. 49 - `/competitive-scan` -- combines auction insights with organic position data for a complete competitive picture. 50 51 The agent auto-discovers your configured platforms. Add Meta Ads later? Every command adapts automatically. 52 53 ### Built-in marketing expertise 54 55 Campaign diagnostics that pinpoint *why* ads aren't delivering -- budget constraints, bidding misconfiguration, policy disapprovals, and more. Search term intent classification. Budget efficiency scoring. RSA ad validation and asset auditing. Landing page analysis. Device-level CPA gap detection. The kind of knowledge experienced ad operators carry in their heads -- built into every workflow. 56 57 ### Learnable operational know-how 58 59 When you correct the agent or share an operational insight, `/learn` saves it to a persistent knowledge base. That knowledge is loaded at the start of every future session, so the agent doesn't repeat the same mistakes and applies what it learned to similar situations across your account. 60 61 ``` 62 You: "That's not a real CPA spike -- this industry always dips in Golden Week." 63 Agent: Saved. I'll flag this as seasonal next time. 64 65 → Written to the diagnostic knowledge base. 66 → Every future /daily-check and /rescue will factor this in. 67 ``` 68 69 ### Security by design 70 71 Marketing accounts are a high-value target. mureo is built with defense-in-depth for AI-driven operations: 72 73 - **Credential guard** — `mureo setup claude-code` installs a PreToolUse hook that blocks AI agents from reading `~/.mureo/credentials.json`, `.env`, and similar secrets, so a prompt-injection payload cannot exfiltrate tokens via the file-system tools. 74 - **GAQL input validation** — every ID, date, date-range constant, and string literal that enters a Google Ads query flows through one whitelist-based surface (`mureo/google_ads/_gaql_validator.py`), and `BETWEEN` clauses pattern-match and revalidate their dates instead of passing raw caller input into GAQL. 75 - **Anomaly detection** — `mureo/analysis/anomaly_detector.py` compares current campaign metrics against a median-based baseline from the action log and emits prioritized alerts for zero spend, CPA spikes, and CTR drops, with sample-size gates that suppress single-day noise. Exposed to agents via the `analysis.anomalies.check` MCP tool; `state_file` is sandboxed inside the MCP server's CWD so a prompt-injected agent cannot redirect it at an attacker-crafted `STATE.json`. 76 - **Rollback with allow-list gating** — `mureo/rollback/` turns agent-authored `reversible_params` hints into concrete `RollbackPlan` records. Only operations on an explicit allow-list are planned; destructive verbs (`.delete`, `.remove`, `.transfer`) and unexpected parameter keys are refused, so a compromised agent cannot smuggle a privileged call through the rollback path. `mureo rollback list` / `show` let operators preview plans, and the `rollback.apply` MCP tool executes them by re-dispatching through the same handler used for forward actions so the reversal re-enters the full policy gate (auth, rate limit, validation). Apply requires `confirm=true` (literal boolean), refuses `rollback.*` self-recursion, records the reversal as an append-only `action_log` entry tagged with `rollback_of=<index>`, and refuses a second apply of the same index. 77 - **Immutable data models** — every state object (`StateDocument`, `ActionLogEntry`, `CampaignSnapshot`, `Anomaly`, `RollbackPlan`) is a `frozen=True` dataclass; an agent cannot silently mutate its own record of what happened. 78 - **Local-only credentials** — tokens are loaded from `~/.mureo/credentials.json` or environment variables and transmitted only to the official ad-platform APIs. mureo itself has no telemetry. 79 80 See [SECURITY.md](SECURITY.md) for the full threat model and vulnerability reporting process. 81 82 <details> 83 <summary>Full capability list</summary> 84 85 | Area | Capabilities | 86 |------|-------------| 87 | **Diagnostics** | Automatic root cause identification for delivery issues (budget, bidding, policy, structure), learning period detection, smart bidding classification, zero-conversion analysis | 88 | **Performance** | Period-over-period comparison, cost spike investigation, cross-campaign health checks, CPA/CV goal tracking | 89 | **Search terms** | N-gram distribution, intent pattern detection, add/exclude candidate scoring, paid vs organic overlap analysis | 90 | **Creative** | RSA validation (prohibited expressions, character width, ad strength prediction), asset-level performance audit, LP analysis, message match scoring | 91 | **Budget** | Cross-campaign allocation analysis, reallocation recommendations, efficiency scoring | 92 | **Competitive** | Auction insights, impression share trends, organic position correlation | 93 | **Meta Ads** | Placement analysis (Facebook/Instagram/Audience Network), cost investigation, A/B comparison, creative suggestions | 94 | **Monitoring** | Delivery goal evaluation, CPA/CV goal tracking, device analysis, B2B-specific checks | 95 96 </details> 97 98 ## Workflow Commands 99 100 | Command | What it does | 101 |---------|-------------| 102 | `/onboard` | Discover your platforms, generate STRATEGY.md, initialize STATE.json | 103 | `/daily-check` | Cross-platform health monitoring + organic pulse + site behavior correlation | 104 | `/rescue` | Emergency performance fix: platform-side vs site-side root cause diagnosis | 105 | `/search-term-cleanup` | Keyword hygiene with paid/organic overlap elimination | 106 | `/creative-refresh` | Multi-platform ad copy refresh using your Persona, USP, and organic keyword data | 107 | `/budget-rebalance` | Cross-platform budget optimization informed by organic coverage | 108 | `/competitive-scan` | Paid + organic competitive landscape analysis | 109 | `/goal-review` | Multi-source goal progress evaluation with operation mode recommendations | 110 | `/weekly-report` | Cross-platform weekly operations summary | 111 | `/sync-state` | Refresh STATE.json from live platform data | 112 | `/learn` | Save a diagnostic insight to the knowledge base for future sessions | 113 114 ### Getting started 115 116 ``` 117 pip install mureo 118 mureo setup claude-code 119 120 # Then in Claude Code: 121 /onboard # First time: set up strategy + state 122 /daily-check # Daily: check all campaigns 123 /rescue # When performance drops 124 ``` 125 126 ### Example: `/creative-refresh` in action 127 128 ``` 129 You: /creative-refresh 130 131 Agent reads STRATEGY.md: 132 Persona: "Budget-constrained SaaS marketer" 133 USP: "AI reduces ad ops workload by 10h/week" 134 Brand Voice: "Data-driven, no hype" 135 136 Agent discovers platforms from STATE.json: 137 → Google Ads + Meta Ads configured 138 139 Agent pulls data across platforms and data sources: 140 → Creative audit → 3 underperforming Google Ads assets 141 → Landing page analysis → LP highlights: free trial, ROI improvement 142 → Search Console → "ad automation" has strong organic clicks 143 → GA4 → high bounce rate on pricing page 144 145 Agent generates platform-appropriate copy from your strategy: 146 Google Ads: "Cut Ad Ops Time by 60% with AI" ← Persona pain point 147 Google Ads: "Free Trial | Ad Automation" ← LP + organic keyword 148 Meta Ads: "Stop drowning in ad reports..." ← Brand Voice + social format 149 150 Agent validates, then asks for approval: 151 "I suggest replacing 3 Google Ads headlines and 2 Meta ads. Here's why..." 152 153 You approve → Agent updates each platform. 154 ``` 155 156 ### Analysis & domain knowledge (built-in) 157 158 <details> 159 <summary>Click to expand full capability list</summary> 160 161 **Campaign Diagnostics & Performance** 162 163 | Capability | Description | 164 |------------|-------------| 165 | Campaign diagnostics | Automatic root cause identification for delivery issues, learning period detection, smart bidding classification | 166 | Performance analysis | Period-over-period comparison, cost increase investigation, cross-campaign health checks | 167 | Search term analysis | N-gram distribution, intent pattern detection, automated add/exclude candidate scoring | 168 | Budget efficiency | Cross-campaign budget allocation analysis, reallocation recommendations | 169 | Device analysis | CPA gap detection, zero-conversion device identification | 170 | Auction insights | Competitive landscape analysis, impression share trends | 171 | B2B optimization | Industry-specific campaign checks and recommendations | 172 173 **Creative & Landing Page** 174 175 | Capability | Description | 176 |------------|-------------| 177 | RSA ad validation | Prohibited expression detection, character width calculation, auto-correction, ad strength prediction | 178 | RSA asset audit | Asset-level performance analysis, replacement/addition recommendations | 179 | Landing page analysis | HTML parsing with SSRF protection, CTA/feature/price detection, industry estimation | 180 | Creative research | Aggregates LP + existing ads + search terms + keyword suggestions into a unified research package | 181 | Message match evaluation | Ad copy <-> landing page alignment scoring (screenshot capture via Playwright) | 182 183 **Monitoring & Goals** 184 185 | Capability | Description | 186 |------------|-------------| 187 | Delivery goal evaluation | Campaign status + diagnostics + performance -> critical/warning/healthy classification | 188 | CPA goal tracking | Actual vs target CPA with trend analysis | 189 | CV goal tracking | Daily conversion volume monitoring against targets | 190 | Zero-conversion diagnosis | Root cause analysis for campaigns with no conversions | 191 192 **Meta Ads Analysis** 193 194 | Capability | Description | 195 |------------|-------------| 196 | Placement analysis | Performance breakdown by Facebook, Instagram, Audience Network | 197 | Cost investigation | CPA degradation root cause analysis | 198 | Ad comparison | A/B performance comparison within ad sets | 199 | Creative suggestions | Data-driven creative improvement recommendations | 200 201 </details> 202 203 ## Quick Start 204 205 ### Prerequisites 206 207 - **Google Ads** -- [Developer Token](https://developers.google.com/google-ads/api/docs/get-started/dev-token) and OAuth Client ID / Client Secret 208 - **Meta Ads** -- Create an app on [Meta for Developers](https://developers.facebook.com/) to obtain an App ID / App Secret (development mode is fine) 209 210 The `mureo auth setup` wizard walks you through both. 211 212 ### Browser-based auth wizard (`mureo auth setup --web`) 213 214 Pasting long secrets into a terminal prompt is error-prone. After installation, `mureo auth setup --web` starts a short-lived local wizard on `http://127.0.0.1:<random-port>/` and opens your browser to a local form where you paste the Developer Token / App ID / App Secret (each field has a deep link to Google Cloud Console / Google Ads API Center / Meta for Developers). The OAuth flow completes in the same browser window and the wizard writes `~/.mureo/credentials.json` for you. 215 216 <details> 217 <summary>Why the wizard is safe to run locally</summary> 218 219 The wizard binds only to `127.0.0.1` on a random OS-assigned port. The form is CSRF-protected (token rotates after every successful submit); the OAuth `state` parameter is validated with `secrets.compare_digest` on callback; a `Host`-header allow-list blocks DNS-rebinding attacks; redirect URLs are pinned to `https://accounts.google.com/` and `https://www.facebook.com/` so the wizard cannot be tricked into an open-redirect; session secrets are zeroed in memory after credentials are persisted. POST bodies are capped at 16 KiB and the process shuts the server down once the `/done` page is served. All dependencies are stdlib — no external web framework to supply-chain-compromise. 220 221 </details> 222 223 ### Claude Code (recommended) 224 225 ```bash 226 pip install mureo 227 mureo setup claude-code 228 ``` 229 230 This single command handles everything: 231 1. Google Ads / Meta Ads authentication (OAuth) 232 2. MCP server configuration for Claude Code 233 3. Credential guard (blocks AI agents from reading secrets) 234 4. Workflow commands (`/daily-check`, `/rescue`, `/learn`, etc.) 235 5. Skills (tool references, strategy guide, evidence-based decisions, diagnostic knowledge) 236 237 After setup, run `/onboard` in Claude Code to get started. 238 239 ### Cursor 240 241 ```bash 242 pip install mureo 243 mureo setup cursor 244 ``` 245 246 Cursor supports MCP tools but does not support workflow commands or skills. 247 248 ### Codex CLI 249 250 ```bash 251 pip install mureo 252 mureo setup codex 253 ``` 254 255 Full parity with Claude Code: MCP server, credential guard (PreToolUse hook), workflow commands, and skills are all installed under `~/.codex/`. Workflow commands are installed as Codex skills at `~/.codex/skills/<command>/SKILL.md` (Codex CLI 0.117.0+ no longer surfaces `~/.codex/prompts/`, see [openai/codex#15941](https://github.com/openai/codex/issues/15941)); invoke them with `$daily-check` or the `/skills` picker. 256 257 ### Gemini CLI 258 259 ```bash 260 pip install mureo 261 mureo setup gemini 262 ``` 263 264 Registers mureo as a Gemini CLI extension at `~/.gemini/extensions/mureo/` with MCP server config and `CONTEXT.md` as the context file. Gemini CLI does not support PreToolUse hooks or the `.md` command format mureo bundles, so those layers are not installed. 265 266 ### CLI only (authentication management) 267 268 ```bash 269 pip install mureo 270 mureo auth setup 271 mureo auth status 272 ``` 273 274 ### Docker 275 276 Run the mureo MCP server in an isolated container. Useful for: 277 278 - **Non–Claude Code MCP clients**: Cursor, Codex CLI, Gemini CLI, Continue, Cline, Zed, or any custom MCP client. 279 - **CI/CD pipelines**: scheduled anomaly checks, rollback dry-runs, weekly reports. 280 - **Multi-tenant / agency ops**: isolated credentials per client via separate containers. 281 - **MCP registry health checks** (Glama, etc.). 282 283 > Slash commands (`/daily-check`, `/rescue`) and the credential-guard hook are Claude Code–specific UX. For those, use `pip install mureo` with `mureo setup claude-code` instead. 284 285 #### Build and run 286 287 ```bash 288 docker build -t mureo . 289 docker run --rm -v ~/.mureo:/home/mureo/.mureo mureo 290 ``` 291 292 Connect your MCP client by pointing its config at this `docker run` command. 293 294 #### Authentication 295 296 Credentials are loaded from `/home/mureo/.mureo/credentials.json` inside the container (via bind mount) or from environment variables. Three common patterns: 297 298 **1. Mounted credentials file** — if `~/.mureo/credentials.json` already exists on the host (from a prior `mureo auth setup`, a team-shared vault, or hand-crafted), the bind mount above is enough. 299 300 Schema for hand-crafting: 301 302 ```json 303 { 304 "google_ads": { 305 "developer_token": "...", 306 "client_id": "...apps.googleusercontent.com", 307 "client_secret": "...", 308 "refresh_token": "...", 309 "login_customer_id": "1234567890" 310 }, 311 "meta_ads": { "access_token": "..." } 312 } 313 ``` 314 315 Required: Google needs `developer_token` / `client_id` / `client_secret` / `refresh_token`. Meta needs `access_token`. Search Console reuses the Google OAuth credentials (OAuth app must include the `https://www.googleapis.com/auth/webmasters` scope). 316 317 **2. Environment variables** — useful for CI/CD where secrets come from a secret manager: 318 319 ```bash 320 docker run --rm \ 321 -e GOOGLE_ADS_DEVELOPER_TOKEN=... \ 322 -e GOOGLE_ADS_CLIENT_ID=... \ 323 -e GOOGLE_ADS_CLIENT_SECRET=... \ 324 -e GOOGLE_ADS_REFRESH_TOKEN=... \ 325 -e GOOGLE_ADS_LOGIN_CUSTOMER_ID=... \ 326 -e META_ADS_ACCESS_TOKEN=... \ 327 mureo 328 ``` 329 330 Supported: `GOOGLE_ADS_{DEVELOPER_TOKEN, CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, LOGIN_CUSTOMER_ID, CUSTOMER_ID}`, `META_ADS_{ACCESS_TOKEN, APP_ID, APP_SECRET, TOKEN_OBTAINED_AT, ACCOUNT_ID}`. 331 332 **3. Interactive wizard inside Docker** — if you don't have OAuth tokens yet and don't want to install mureo on the host: 333 334 ```bash 335 docker run -it --rm -v ~/.mureo:/home/mureo/.mureo mureo mureo auth setup 336 ``` 337 338 Walks you through the OAuth flow in the terminal and writes `credentials.json` to the mounted volume. Subsequent runs pick it up automatically (pattern 1). 339 340 To obtain OAuth tokens outside mureo: 341 342 - Google Ads OAuth 2.0 refresh token: https://developers.google.com/google-ads/api/docs/oauth/overview 343 - Meta long-lived access token: https://developers.facebook.com/docs/facebook-login/guides/access-tokens/get-long-lived 344 345 ### What gets installed 346 347 | Component | `mureo setup claude-code` | `mureo setup cursor` | `mureo setup codex` | `mureo setup gemini` | `mureo auth setup` | 348 |-----------|:---:|:---:|:---:|:---:|:---:| 349 | Authentication (~/.mureo/credentials.json) | Yes | Yes | Yes | Yes | Yes | 350 | MCP configuration | Yes | Yes | Yes | Yes | Yes | 351 | Credential guard (PreToolUse hook) | Yes | N/A | Yes | N/A | Yes | 352 | Workflow commands | Yes (~/.claude/commands/) | N/A | Yes (~/.codex/skills/ — invoke with `$cmd` or `/skills`) | N/A | No | 353 | Skills | Yes (~/.claude/skills/) | N/A | Yes (~/.codex/skills/) | N/A | No | 354 | Extension manifest (contextFileName) | N/A | N/A | N/A | Yes (~/.gemini/extensions/mureo/) | No | 355 356 ### Skills reference 357 358 | Skill | Purpose | 359 |-------|---------| 360 | `mureo-google-ads` | Google Ads tool reference (parameters, examples) | 361 | `mureo-meta-ads` | Meta Ads tool reference (parameters, examples) | 362 | `mureo-shared` | Authentication, security rules, output formatting | 363 | `mureo-strategy` | STRATEGY.md / STATE.json format and usage guide | 364 | `mureo-workflows` | Orchestration paradigm, Operation Mode matrix, KPI thresholds, command reference | 365 | `mureo-learning` | Evidence-based marketing decision framework (observation windows, sample sizes, noise guards) | 366 | `mureo-pro-diagnosis` | Learnable diagnostic knowledge base (grows with use via `/learn`) | 367 368 ### Connecting GA4 (Google Analytics 4) 369 370 mureo's workflow commands can leverage GA4 data (conversion rates, user behavior, landing page performance) when a GA4 MCP server is configured alongside mureo. GA4 data is optional — all commands work without it. 371 372 Setup using [Google Analytics MCP](https://github.com/googleanalytics/google-analytics-mcp): 373 374 1. Enable the required APIs in your GCP project: 375 - [Google Analytics Admin API](https://console.cloud.google.com/apis/library/analyticsadmin.googleapis.com) -- click "Enable" 376 - [Google Analytics Data API](https://console.cloud.google.com/apis/library/analyticsdata.googleapis.com) -- click "Enable" 377 378 2. Install and authenticate: 379 380 ```bash 381 pipx install analytics-mcp 382 383 gcloud auth application-default login \ 384 --scopes https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform 385 ``` 386 387 3. Add to `~/.claude/settings.json` alongside mureo: 388 389 ```json 390 { 391 "mcpServers": { 392 "mureo": { 393 "command": "python", 394 "args": ["-m", "mureo.mcp"] 395 }, 396 "analytics-mcp": { 397 "command": "pipx", 398 "args": ["run", "analytics-mcp"], 399 "env": { 400 "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/application_default_credentials.json", 401 "GOOGLE_PROJECT_ID": "your-gcp-project-id" 402 } 403 } 404 } 405 } 406 ``` 407 408 ### Connecting Other MCP Servers 409 410 mureo works alongside any MCP server in the same client session. Add them to your settings and workflow commands will incorporate their data when available. See [docs/integrations.md](docs/integrations.md) for details. 411 412 ## Authentication 413 414 ### Interactive Setup (Recommended) 415 416 ```bash 417 mureo auth setup 418 ``` 419 420 The setup wizard walks you through: 421 422 1. **Google Ads** -- Enter Developer Token + Client ID/Secret, open browser for OAuth, select a Google Ads customer account 423 2. **Meta Ads** -- Enter App ID/Secret, open browser for OAuth, obtain a Long-Lived Token, select an ad account. Your Meta App can stay in **Development Mode** -- no App Review is needed since mureo operates your own ad account. You may see a permission warning for `business_management` during OAuth; this is safe to accept and required for accessing pages managed through Business Portfolio. 424 3. **MCP config** -- Automatically writes `.mcp.json` (project-level) or `~/.claude/settings.json` (global) so Claude Code / Cursor can discover the server 425 426 Credentials are saved to `~/.mureo/credentials.json`. Search Console reuses the same Google OAuth2 credentials as Google Ads -- no additional authentication is required. 427 428 ### credentials.json 429 430 ```json 431 { 432 "google_ads": { 433 "developer_token": "YOUR_DEVELOPER_TOKEN", 434 "client_id": "YOUR_CLIENT_ID", 435 "client_secret": "YOUR_CLIENT_SECRET", 436 "refresh_token": "YOUR_REFRESH_TOKEN", 437 "login_customer_id": "1234567890" 438 }, 439 "meta_ads": { 440 "access_token": "YOUR_ACCESS_TOKEN", 441 "app_id": "YOUR_APP_ID", 442 "app_secret": "YOUR_APP_SECRET" 443 } 444 } 445 ``` 446 447 ### Environment variables (fallback) 448 449 | Platform | Variable | Required | 450 |------------|-----------------------------------|----------| 451 | Google Ads | `GOOGLE_ADS_DEVELOPER_TOKEN` | Yes | 452 | Google Ads | `GOOGLE_ADS_CLIENT_ID` | Yes | 453 | Google Ads | `GOOGLE_ADS_CLIENT_SECRET` | Yes | 454 | Google Ads | `GOOGLE_ADS_REFRESH_TOKEN` | Yes | 455 | Google Ads | `GOOGLE_ADS_LOGIN_CUSTOMER_ID` | No | 456 | Meta Ads | `META_ADS_ACCESS_TOKEN` | Yes | 457 | Meta Ads | `META_ADS_APP_ID` | No | 458 | Meta Ads | `META_ADS_APP_SECRET` | No | 459 460 Verify your setup: 461 462 ```bash 463 mureo auth status 464 mureo auth check-google 465 mureo auth check-meta 466 ``` 467 468 ## MCP Server 469 470 ### Setup with Claude Code 471 472 **Project-level** (recommended) -- add to `.mcp.json` in your project root: 473 474 ```json 475 { 476 "mcpServers": { 477 "mureo": { 478 "command": "python", 479 "args": ["-m", "mureo.mcp"] 480 } 481 } 482 } 483 ``` 484 485 **Global** -- add to `~/.claude/settings.json`: 486 487 ```json 488 { 489 "mcpServers": { 490 "mureo": { 491 "command": "python", 492 "args": ["-m", "mureo.mcp"] 493 } 494 } 495 } 496 ``` 497 498 > Tip: `mureo auth setup` can write this configuration automatically. 499 500 ### Setup with Cursor 501 502 Add to `.cursor/mcp.json`: 503 504 ```json 505 { 506 "mcpServers": { 507 "mureo": { 508 "command": "python", 509 "args": ["-m", "mureo.mcp"] 510 } 511 } 512 } 513 ``` 514 515 ### Tool list 516 517 #### Google Ads 518 519 <details> 520 <summary>Click to expand Google Ads tools</summary> 521 522 **Campaigns** 523 524 | Tool | Description | 525 |------|-------------| 526 | `google_ads.campaigns.list` | List campaigns | 527 | `google_ads.campaigns.get` | Get campaign details | 528 | `google_ads.campaigns.create` | Create a campaign (search or display, via `channel_type`) | 529 | `google_ads.campaigns.update` | Update campaign settings | 530 | `google_ads.campaigns.update_status` | Change campaign status (ENABLED/PAUSED/REMOVED) | 531 | `google_ads.campaigns.diagnose` | Diagnose campaign delivery status | 532 533 **Ad Groups** 534 535 | Tool | Description | 536 |------|-------------| 537 | `google_ads.ad_groups.list` | List ad groups | 538 | `google_ads.ad_groups.create` | Create an ad group | 539 | `google_ads.ad_groups.update` | Update an ad group | 540 541 **Ads** 542 543 | Tool | Description | 544 |------|-------------| 545 | `google_ads.ads.list` | List ads | 546 | `google_ads.ads.create` | Create a responsive search ad (RSA) | 547 | `google_ads.ads.create_display` | Create a responsive display ad (RDA); image files are uploaded automatically | 548 | `google_ads.ads.update` | Update an ad | 549 | `google_ads.ads.update_status` | Change ad status | 550 | `google_ads.ads.policy_details` | Get ad policy approval details | 551 552 **Keywords (8)** 553 554 | Tool | Description | 555 |------|-------------| 556 | `google_ads.keywords.list` | List keywords | 557 | `google_ads.keywords.add` | Add keywords | 558 | `google_ads.keywords.remove` | Remove a keyword | 559 | `google_ads.keywords.suggest` | Get keyword suggestions (Keyword Planner) | 560 | `google_ads.keywords.diagnose` | Diagnose keyword quality scores | 561 | `google_ads.keywords.pause` | Pause a keyword | 562 | `google_ads.keywords.audit` | Audit keyword performance and quality | 563 | `google_ads.keywords.cross_adgroup_duplicates` | Find duplicate keywords across ad groups | 564 565 **Negative Keywords (5)** 566 567 | Tool | Description | 568 |------|-------------| 569 | `google_ads.negative_keywords.list` | List negative keywords | 570 | `google_ads.negative_keywords.add` | Add negative keywords to a campaign | 571 | `google_ads.negative_keywords.remove` | Remove a negative keyword | 572 | `google_ads.negative_keywords.add_to_ad_group` | Add negative keywords to an ad group | 573 | `google_ads.negative_keywords.suggest` | Suggest negative keywords based on search terms | 574 575 **Budget (3)** 576 577 | Tool | Description | 578 |------|-------------| 579 | `google_ads.budget.get` | Get campaign budget | 580 | `google_ads.budget.update` | Update budget | 581 | `google_ads.budget.create` | Create a new campaign budget | 582 583 **Accounts (1)** 584 585 | Tool | Description | 586 |------|-------------| 587 | `google_ads.accounts.list` | List accessible Google Ads accounts | 588 589 **Search Terms (2)** 590 591 | Tool | Description | 592 |------|-------------| 593 | `google_ads.search_terms.report` | Get search terms report | 594 | `google_ads.search_terms.analyze` | Analyze search terms with intent classification | 595 596 **Sitelinks (3)** 597 598 | Tool | Description | 599 |------|-------------| 600 | `google_ads.sitelinks.list` | List sitelink extensions | 601 | `google_ads.sitelinks.create` | Create a sitelink extension | 602 | `google_ads.sitelinks.remove` | Remove a sitelink extension | 603 604 **Callouts (3)** 605 606 | Tool | Description | 607 |------|-------------| 608 | `google_ads.callouts.list` | List callout extensions | 609 | `google_ads.callouts.create` | Create a callout extension | 610 | `google_ads.callouts.remove` | Remove a callout extension | 611 612 **Conversions (7)** 613 614 | Tool | Description | 615 |------|-------------| 616 | `google_ads.conversions.list` | List conversion actions | 617 | `google_ads.conversions.get` | Get conversion action details | 618 | `google_ads.conversions.performance` | Get conversion performance metrics | 619 | `google_ads.conversions.create` | Create a conversion action | 620 | `google_ads.conversions.update` | Update a conversion action | 621 | `google_ads.conversions.remove` | Remove a conversion action | 622 | `google_ads.conversions.tag` | Get conversion tracking tag snippet | 623 624 **Targeting (11)** 625 626 | Tool | Description | 627 |------|-------------| 628 | `google_ads.recommendations.list` | List optimization recommendations | 629 | `google_ads.recommendations.apply` | Apply an optimization recommendation | 630 | `google_ads.device_targeting.get` | Get device targeting settings | 631 | `google_ads.device_targeting.set` | Set device targeting bid adjustments | 632 | `google_ads.bid_adjustments.get` | Get bid adjustment settings | 633 | `google_ads.bid_adjustments.update` | Update bid adjustments | 634 | `google_ads.location_targeting.list` | List location targeting criteria | 635 | `google_ads.location_targeting.update` | Update location targeting | 636 | `google_ads.schedule_targeting.list` | List ad schedule targeting | 637 | `google_ads.schedule_targeting.update` | Update ad schedule targeting | 638 | `google_ads.change_history.list` | List account change history | 639 640 **Analysis (13)** 641 642 | Tool | Description | 643 |------|-------------| 644 | `google_ads.performance.report` | Get performance report | 645 | `google_ads.performance.analyze` | Analyze performance trends and anomalies | 646 | `google_ads.cost_increase.investigate` | Investigate sudden cost increases | 647 | `google_ads.health_check.all` | Run a comprehensive account health check | 648 | `google_ads.ad_performance.compare` | Compare ad performance across variants | 649 | `google_ads.ad_performance.report` | Get detailed ad-level performance report | 650 | `google_ads.network_performance.report` | Get network-level performance breakdown | 651 | `google_ads.budget.efficiency` | Analyze budget utilization efficiency | 652 | `google_ads.budget.reallocation` | Suggest budget reallocation across campaigns | 653 | `google_ads.auction_insights.get` | Get auction insights (competitor analysis) | 654 | `google_ads.rsa_assets.analyze` | Analyze RSA asset performance | 655 | `google_ads.rsa_assets.audit` | Audit RSA assets for best practices | 656 | `google_ads.search_terms.review` | Review search terms and suggest additions/exclusions | 657 658 **B2B (1)** 659 660 | Tool | Description | 661 |------|-------------| 662 | `google_ads.btob.optimizations` | Get B2B-specific optimization suggestions | 663 664 **Creative (2)** 665 666 | Tool | Description | 667 |------|-------------| 668 | `google_ads.landing_page.analyze` | Analyze landing page relevance and quality | 669 | `google_ads.creative.research` | Research competitive creative strategies | 670 671 **Monitoring (4)** 672 673 | Tool | Description | 674 |------|-------------| 675 | `google_ads.monitoring.delivery_goal` | Monitor campaign delivery against goals | 676 | `google_ads.monitoring.cpa_goal` | Monitor CPA against target goals | 677 | `google_ads.monitoring.cv_goal` | Monitor conversion volume against goals | 678 | `google_ads.monitoring.zero_conversions` | Detect campaigns with zero conversions | 679 680 **Capture (1)** 681 682 | Tool | Description | 683 |------|-------------| 684 | `google_ads.capture.screenshot` | Capture a screenshot of a URL | 685 686 **Device (1)** 687 688 | Tool | Description | 689 |------|-------------| 690 | `google_ads.device.analyze` | Analyze device-level performance | 691 692 **CPC (1)** 693 694 | Tool | Description | 695 |------|-------------| 696 | `google_ads.cpc.detect_trend` | Detect CPC trend (rising/stable/falling) | 697 698 **Assets (1)** 699 700 | Tool | Description | 701 |------|-------------| 702 | `google_ads.assets.upload_image` | Upload an image asset | 703 704 </details> 705 706 #### Meta Ads 707 708 <details> 709 <summary>Click to expand Meta Ads tools</summary> 710 711 **Campaigns (6)** 712 713 | Tool | Description | 714 |------|-------------| 715 | `meta_ads.campaigns.list` | List campaigns | 716 | `meta_ads.campaigns.get` | Get campaign details | 717 | `meta_ads.campaigns.create` | Create a campaign | 718 | `meta_ads.campaigns.update` | Update a campaign | 719 | `meta_ads.campaigns.pause` | Pause a campaign | 720 | `meta_ads.campaigns.enable` | Enable a paused campaign | 721 722 **Ad Sets (6)** 723 724 | Tool | Description | 725 |------|-------------| 726 | `meta_ads.ad_sets.list` | List ad sets | 727 | `meta_ads.ad_sets.create` | Create an ad set | 728 | `meta_ads.ad_sets.update` | Update an ad set | 729 | `meta_ads.ad_sets.get` | Get ad set details | 730 | `meta_ads.ad_sets.pause` | Pause an ad set | 731 | `meta_ads.ad_sets.enable` | Enable a paused ad set | 732 733 **Ads (6)** 734 735 | Tool | Description | 736 |------|-------------| 737 | `meta_ads.ads.list` | List ads | 738 | `meta_ads.ads.create` | Create an ad | 739 | `meta_ads.ads.update` | Update an ad | 740 | `meta_ads.ads.get` | Get ad details | 741 | `meta_ads.ads.pause` | Pause an ad | 742 | `meta_ads.ads.enable` | Enable a paused ad | 743 744 **Creatives (6)** 745 746 | Tool | Description | 747 |------|-------------| 748 | `meta_ads.creatives.create_carousel` | Create a carousel creative (2-10 cards) | 749 | `meta_ads.creatives.create_collection` | Create a collection creative | 750 | `meta_ads.creatives.list` | List ad creatives | 751 | `meta_ads.creatives.create` | Create a standard ad creative | 752 | `meta_ads.creatives.create_dynamic` | Create a dynamic product ad creative | 753 | `meta_ads.creatives.upload_image` | Upload an image for use in creatives | 754 755 **Images (1)** 756 757 | Tool | Description | 758 |------|-------------| 759 | `meta_ads.images.upload_file` | Upload an image from local file | 760 761 **Insights (2)** 762 763 | Tool | Description | 764 |------|-------------| 765 | `meta_ads.insights.report` | Get performance report | 766 | `meta_ads.insights.breakdown` | Get breakdown report (age, gender, placement, etc.) | 767 768 **Audiences (5)** 769 770 | Tool | Description | 771 |------|-------------| 772 | `meta_ads.audiences.list` | List custom audiences | 773 | `meta_ads.audiences.create` | Create a custom audience | 774 | `meta_ads.audiences.get` | Get audience details | 775 | `meta_ads.audiences.delete` | Delete a custom audience | 776 | `meta_ads.audiences.create_lookalike` | Create a lookalike audience | 777 778 **Conversions API (3)** 779 780 | Tool | Description | 781 |------|-------------| 782 | `meta_ads.conversions.send` | Send a conversion event (generic) | 783 | `meta_ads.conversions.send_purchase` | Send a purchase event | 784 | `meta_ads.conversions.send_lead` | Send a lead event | 785 786 **Pixels (4)** 787 788 | Tool | Description | 789 |------|-------------| 790 | `meta_ads.pixels.list` | List pixels | 791 | `meta_ads.pixels.get` | Get pixel details | 792 | `meta_ads.pixels.stats` | Get pixel firing statistics | 793 | `meta_ads.pixels.events` | List pixel events | 794 795 **Analysis (6)** 796 797 | Tool | Description | 798 |------|-------------| 799 | `meta_ads.analysis.performance` | Analyze overall performance trends | 800 | `meta_ads.analysis.audience` | Analyze audience performance and overlap | 801 | `meta_ads.analysis.placements` | Analyze placement performance breakdown | 802 | `meta_ads.analysis.cost` | Analyze cost trends and efficiency | 803 | `meta_ads.analysis.compare_ads` | Compare performance across ads | 804 | `meta_ads.analysis.suggest_creative` | Suggest creative improvements based on data | 805 806 **Product Catalog (11)** 807 808 | Tool | Description | 809 |------|-------------| 810 | `meta_ads.catalogs.list` | List product catalogs | 811 | `meta_ads.catalogs.create` | Create a product catalog | 812 | `meta_ads.catalogs.get` | Get catalog details | 813 | `meta_ads.catalogs.delete` | Delete a catalog | 814 | `meta_ads.products.list` | List products in a catalog | 815 | `meta_ads.products.add` | Add a product to a catalog | 816 | `meta_ads.products.get` | Get product details | 817 | `meta_ads.products.update` | Update a product | 818 | `meta_ads.products.delete` | Delete a product | 819 | `meta_ads.feeds.list` | List catalog feeds | 820 | `meta_ads.feeds.create` | Create a catalog feed (URL + scheduled import) | 821 822 **Lead Ads (5)** 823 824 | Tool | Description | 825 |------|-------------| 826 | `meta_ads.lead_forms.list` | List lead forms (per Page) | 827 | `meta_ads.lead_forms.get` | Get lead form details | 828 | `meta_ads.lead_forms.create` | Create a lead form | 829 | `meta_ads.leads.get` | Get leads (per form) | 830 | `meta_ads.leads.get_by_ad` | Get leads (per ad) | 831 832 **Videos (2)** 833 834 | Tool | Description | 835 |------|-------------| 836 | `meta_ads.videos.upload` | Upload a video from URL | 837 | `meta_ads.videos.upload_file` | Upload a video from local file | 838 839 **Split Tests (4)** 840 841 | Tool | Description | 842 |------|-------------| 843 | `meta_ads.split_tests.list` | List A/B tests | 844 | `meta_ads.split_tests.get` | Get A/B test details and results | 845 | `meta_ads.split_tests.create` | Create an A/B test | 846 | `meta_ads.split_tests.end` | End an A/B test | 847 848 **Ad Rules (5)** 849 850 | Tool | Description | 851 |------|-------------| 852 | `meta_ads.ad_rules.list` | List automated rules | 853 | `meta_ads.ad_rules.get` | Get automated rule details | 854 | `meta_ads.ad_rules.create` | Create an automated rule (CPA alerts, auto-pause, etc.) | 855 | `meta_ads.ad_rules.update` | Update an automated rule | 856 | `meta_ads.ad_rules.delete` | Delete an automated rule | 857 858 **Page Posts (2)** 859 860 | Tool | Description | 861 |------|-------------| 862 | `meta_ads.page_posts.list` | List Facebook Page posts | 863 | `meta_ads.page_posts.boost` | Boost a Page post | 864 865 **Instagram (3)** 866 867 | Tool | Description | 868 |------|-------------| 869 | `meta_ads.instagram.accounts` | List connected Instagram accounts | 870 | `meta_ads.instagram.media` | List Instagram posts | 871 | `meta_ads.instagram.boost` | Boost an Instagram post | 872 873 </details> 874 875 #### Search Console 876 877 <details> 878 <summary>Click to expand Search Console tools</summary> 879 880 **Sites (2)** 881 882 | Tool | Description | 883 |------|-------------| 884 | `search_console.sites.list` | List verified sites | 885 | `search_console.sites.get` | Get site details | 886 887 **Analytics (4)** 888 889 | Tool | Description | 890 |------|-------------| 891 | `search_console.analytics.query` | Query search analytics data | 892 | `search_console.analytics.top_queries` | Get top search queries | 893 | `search_console.analytics.top_pages` | Get top pages by clicks/impressions | 894 | `search_console.analytics.device_breakdown` | Get performance breakdown by device | 895 | `search_console.analytics.compare_periods` | Compare search performance across time periods | 896 897 **Sitemaps (2)** 898 899 | Tool | Description | 900 |------|-------------| 901 | `search_console.sitemaps.list` | List sitemaps for a site | 902 | `search_console.sitemaps.submit` | Submit a sitemap | 903 904 **URL Inspection (1)** 905 906 | Tool | Description | 907 |------|-------------| 908 | `search_console.url_inspection.inspect` | Inspect a URL for indexing status | 909 910 </details> 911 912 #### Rollback 913 914 <details> 915 <summary>Click to expand rollback tools</summary> 916 917 Cross-platform tools that inspect and apply the reversal plan for a previously-recorded `action_log` entry. Apply re-dispatches through the same MCP handler used for forward actions, so the reversal re-enters the full policy gate (auth, rate limit, input validation, allow-list). 918 919 | Tool | Description | 920 |------|-------------| 921 | `rollback.plan.get` | Inspect the reversal plan for an `action_log` entry (`supported` / `partial` / `not_supported`), its `operation` + `params`, and any caveats. Read-only; does not execute. | 922 | `rollback.apply` | Execute the reversal plan for `action_log[index]`. Requires `confirm=true` as a literal boolean. Appends a new log entry tagged `rollback_of=<index>`; a second apply of the same index is refused. `state_file` is resolved strictly inside the MCP server's CWD — path traversal, symlink escape, and `rollback.*` self-recursion are all refused. | 923 924 </details> 925 926 #### Analysis 927 928 <details> 929 <summary>Click to expand analysis tools</summary> 930 931 Cross-platform detection tools that operate on STATE.json's `action_log` history rather than on platform APIs directly. 932 933 | Tool | Description | 934 |------|-------------| 935 | `analysis.anomalies.check` | Compare a campaign's current metrics against a median-based baseline built from `action_log` history. Returns severity-ordered anomalies — zero spend (CRITICAL), CPA spike (HIGH/CRITICAL, gated by 30+ conversions), CTR drop (HIGH/CRITICAL, gated by 1000+ impressions). Sample-size gates follow the `mureo-learning` skill's statistical-thinking rules. Returns `baseline=null` when history is shorter than `min_baseline_entries` (default 7); `baseline_warning` surfaces a parseable-but-corrupt `STATE.json` without silencing live zero-spend detection. | 936 937 </details> 938 939 ## CLI 940 941 ```bash 942 mureo setup claude-code # One-command setup for Claude Code 943 mureo setup cursor # Setup for Cursor 944 mureo auth status # Check authentication status 945 mureo auth check-google # Verify Google Ads credentials 946 mureo auth check-meta # Verify Meta Ads credentials 947 ``` 948 949 ## Strategy Context 950 951 Two local files drive strategy-aware operations. Run `/onboard` to generate them interactively. 952 953 - **STRATEGY.md** -- Persona, USP, Brand Voice, Goals, Operation Mode. See [docs/strategy-context.md](docs/strategy-context.md). 954 - **STATE.json** -- Campaign snapshots, action log. Updated automatically by workflow commands. 955 956 ## Architecture 957 958 ``` 959 mureo/ 960 ├── __init__.py # Package root 961 ├── auth.py # Credential loading (~/.mureo/credentials.json + env vars + Meta token auto-refresh) 962 ├── auth_setup.py # Interactive setup wizard (OAuth + MCP config) 963 ├── throttle.py # Rate limiting (token bucket + rolling hourly cap) 964 ├── _image_validation.py # Image file validation utilities 965 ├── google_ads/ # Google Ads API client (google-ads SDK wrapper) 966 │ ├── client.py # GoogleAdsApiClient (8 Mixin: Ads, Keywords, Analysis, Creative, Diagnostics, Extensions, Media, Monitoring) 967 │ ├── mappers.py # Response mapping to structured dicts 968 │ └── _*.py # 8 Mixin modules (ads, keywords, analysis, extensions, diagnostics, 969 │ # creative, monitoring, media) + validators + classifiers 970 ├── meta_ads/ # Meta Ads API client (15 Mixins, httpx-based) 971 │ ├── client.py # MetaAdsApiClient (Campaigns, AdSets, Ads, Creatives, Audiences, Pixels, 972 │ │ # Insights, Analysis, Catalog, Conversions, Leads, PagePosts, Instagram, 973 │ │ # SplitTest, AdRules) 974 │ ├── mappers.py # Response mapping to structured dicts 975 │ └── _*.py # 15 Mixin modules (campaigns, ads, creatives, audiences, etc.) 976 ├── search_console/ # Google Search Console API client (reuses Google OAuth2 credentials) 977 │ └── client.py # SearchConsoleApiClient 978 ├── analysis/ # Cross-platform analysis utilities 979 │ └── lp_analyzer.py # Landing page analysis engine 980 ├── context/ # File-based context (STRATEGY.md, STATE.json) 981 │ ├── models.py # Immutable dataclasses (StrategyEntry, StateDocument) 982 │ ├── strategy.py # STRATEGY.md parser / renderer 983 │ ├── state.py # STATE.json parser / renderer 984 │ └── errors.py # Context-related errors 985 ├── cli/ # Typer CLI (setup + auth only) 986 │ ├── main.py # Entry point (mureo command) 987 │ ├── setup_cmd.py # mureo setup claude-code / cursor 988 │ └── auth_cmd.py # mureo auth setup / status / check-* 989 └── mcp/ # MCP server 990 ├── __main__.py # python -m mureo.mcp entry point 991 ├── server.py # MCP server setup (stdio transport) 992 ├── _helpers.py # Shared handler utilities 993 ├── tools_google_ads.py # Google Ads tool definitions (aggregator) 994 ├── _tools_google_ads_*.py # Tool definition sub-modules 995 ├── _handlers_google_ads.py # Google Ads base handlers 996 ├── _handlers_google_ads_extensions.py # Extensions handlers 997 ├── _handlers_google_ads_analysis.py # Analysis handlers 998 ├── tools_meta_ads.py # Meta Ads tool definitions (aggregator) 999 ├── _tools_meta_ads_*.py # Tool definition sub-modules 1000 ├── _handlers_meta_ads.py # Meta Ads base handlers 1001 ├── _handlers_meta_ads_extended.py # Extended handlers 1002 ├── _handlers_meta_ads_other.py # Other handlers 1003 ├── tools_search_console.py # Search Console tool definitions 1004 └── _handlers_search_console.py # Search Console handlers 1005 ``` 1006 1007 **Design principles:** 1008 1009 - **No database** -- all state is either in the ad platform APIs or in local files (`STRATEGY.md`, `STATE.json`). 1010 - **No LLM dependency** -- mureo does not embed an LLM. Inference, planning, and decision-making are the agent's responsibility. 1011 - **Immutable data models** -- all dataclasses use `frozen=True` to prevent accidental mutation. 1012 - **Credentials stay local** -- loaded from `~/.mureo/credentials.json` or environment variables. Never sent anywhere except the official ad platform APIs. 1013 1014 ## Development 1015 1016 ```bash 1017 git clone https://github.com/logly/mureo.git && cd mureo 1018 pip install -e ".[dev]" 1019 pytest tests/ -v # run tests 1020 pytest --cov=mureo --cov-report=term-missing # with coverage 1021 ruff check mureo/ && black mureo/ && mypy mureo/ # lint & format 1022 ``` 1023 1024 Python 3.10+ required. See [CONTRIBUTING.md](CONTRIBUTING.md) for full development guidelines. 1025 1026 ## License 1027 1028 Apache License 2.0