/ 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