# BYOD: Bring Your Own Data

> [日本語版](byod.ja.md)

Run mureo against your **real Google Ads / Meta Ads account** in 5
minutes — no OAuth Client ID to register, no Google Ads developer
token to apply for, no SaaS connection of any kind. You run a Google
Ads Script (or export from Meta Ads Manager), download the resulting
Sheet/Excel as XLSX, drop it into mureo, and ask Claude Code to run
`/daily-check`.

**No network calls reach Google or Meta from mureo. No mureo-managed
credentials are involved. The Sheet lives in your own Drive; mureo
only ever reads the local XLSX file.**

> **Status (Phase 2):** Google Ads (via the mureo Google Ads Script)
> and Meta Ads (via the Ads Manager Excel export) are both supported
> through the Sheet bundle pipeline. GA4 and Search Console remain on
> the existing Live API OAuth path (see `docs/authentication.md`) —
> they are **not** part of the BYOD bundle.

> **Compatibility — BYOD/Demo run on mureo-native only.** BYOD (and
> `mureo demo`, which is just a synthetic BYOD bundle) is served
> entirely by mureo's own CSV-backed MCP tools. The **official
> platform MCPs** (`mureo providers add google-ads-official` /
> `meta-ads-official`, or the Claude.ai Meta connector) are the
> **Live-API** path — they connect to the real platform and cannot
> read your local BYOD/demo data. Per platform the two are mutually
> exclusive: do **not** `mureo providers add` (which sets
> `MUREO_DISABLE_<PLATFORM>=1`) or switch a platform to the official
> MCP if you analyze that platform via BYOD or the demo — keep
> mureo-native enabled for it. Use the official MCP only for the Live
> API. (`mureo providers remove` / the dashboard native↔official
> toggle re-enables mureo-native.)

---

## Why BYOD?

The biggest barriers to local-first ad analysis are OAuth client
registration and Google Ads developer-token approval (days to weeks).
BYOD avoids both:

- Try mureo against **your actual account**, not a synthetic demo.
- No data governance review needed — the XLSX stays on your machine.
- Compatible with the `mureo setup claude-code` you already ran (no
  extra MCP config).
- Works on Google Workspace **organization** accounts where
  Apps Script is blocked from auto-creating a personal GCP project
  (Google Ads Scripts run inside the Ads UI, not Apps Script).
- **Read-only by construction**: every mutation tool (`create_*`,
  `update_*`, `pause_*`, etc.) returns
  `{"status": "skipped_in_byod_readonly"}`.

---

## 5-minute walkthrough

### Step 1 — Install mureo

```bash
pip install mureo
mureo setup claude-code --skip-auth   # registers MCP only; no OAuth
```

### Step 2a — Run the Google Ads Script (optional)

Open Google Ads → **Tools → Bulk actions → Scripts → +**.

Paste the contents of `scripts/sheet-template/google-ads-script.js`.
At the top, set `TARGET_SHEET_URL` to the URL of any Google Sheet you
own (create a fresh one if you don't have one yet). Click
**Authorize** and **Run**.

Four tabs populate in the Sheet: `campaigns`, `ad_groups`,
`search_terms`, `keywords`. Auction insights are intentionally
excluded — Google Ads Scripts cannot reach them. Use
`mureo auth setup` if you need `/competitive-scan` data.

### Step 2b — Export from Meta Ads Manager (optional)

In Ads Manager: **Reports → Customize → Export**.

Configure the report once with these columns (one row per
day × ad-set × ad):

- **Breakdowns:** *By Time → Day*
- **Level:** *Ad* (gives the full Campaign / Ad set / Ad hierarchy)
- **Metrics:** Day, Campaign name, Ad set name, Ad name,
  Impressions, Clicks (all), Amount spent, Results
- **Account language:** any of the 9 supported locales (see the
  multilingual section below) — Ads Manager always emits the spend
  header as `Amount spent (XXX)` where `XXX` is the account's ISO
  currency code (`JPY` / `USD` / `EUR` / …); mureo strips the
  currency suffix automatically

Click **Export → Excel (.xlsx)**. Save the file alongside the Google
Ads Sheet's XLSX export.

### Step 3 — Download as XLSX, import to mureo

In the Google Sheet, **File → Download → Microsoft Excel (.xlsx)**.
Then run mureo byod import once per file:

```bash
# Google Ads bundle
mureo byod import ~/Downloads/<google-ads-bundle>.xlsx

# Meta Ads export (separate file from Ads Manager → Reports → Export)
mureo byod import ~/Downloads/<meta-ads-export>.xlsx
```

Sample output:

```
=== mureo byod import ===

  [google_ads] format: mureo_sheet_bundle_google_ads_v1
    421 rows, date range 2026-04-01..2026-04-30
    written to /Users/you/.mureo/byod/google_ads/
      - campaigns.csv
      - metrics_daily.csv
      - ad_groups.csv
      - keywords.csv
      - search_terms.csv

Mode summary:
  google_ads        BYOD (421 rows, 2026-04-01..2026-04-30)
  meta_ads          not configured (no BYOD data, no credentials.json)

Next: ask Claude Code: 'Run /daily-check'
```

### Step 4 — Ask Claude Code

Open Claude Code in any directory that contains a `STRATEGY.md` (or
generate one with `mureo onboard` if you don't have one yet), then
type:

> Run /daily-check

mureo's MCP server detects your imported bundle automatically — no
flag required — and the agent reasons over your real Google Ads data:
search-term gaps, keyword quality scores, etc.

---

## CLI reference

| Command | Description |
|---|---|
| `mureo byod import <file.xlsx>` | Import a Sheet bundle. Aborts if the target platform already has BYOD data. |
| `mureo byod import <file.xlsx> --replace` | Overwrite existing BYOD data for the platform present in the bundle. |
| `mureo byod status` | Show per-platform mode (BYOD / Live API / not configured). |
| `mureo byod remove --<platform>` | Remove BYOD data for one platform (`--google-ads` / `--meta-ads`). |
| `mureo byod clear` | Wipe `~/.mureo/byod/` entirely. |
| `mureo byod clear --yes` | Skip the confirmation prompt. |

There is **no `--byod` flag** anywhere. mureo decides per-platform at
each tool call by checking `~/.mureo/byod/manifest.json`. Live API
credentials and BYOD imports coexist freely — see the next section.

---

## How activation works

```
┌──────────────────────────────┐
│ mureo byod import bundle.xlsx│──▶ ~/.mureo/byod/manifest.json
└──────────────────────────────┘                │
                                                ▼
                                  ┌───────────────────────┐
   Claude Code ─────MCP──────────▶│ mureo MCP server      │
                                  │                       │
                                  │  per-tool dispatch    │
                                  │  byod_has(platform)?  │
                                  └─────┬───────────────┬─┘
                                  yes   │             no │
                                        ▼                ▼
                              ┌────────────────┐  ┌─────────────────┐
                              │ Byod*Client    │  │ create_*_client │
                              │ (reads CSV)    │  │ (live API)      │
                              └────────────────┘  └─────────────────┘
```

`byod_has(platform)` returns True only when:

1. `~/.mureo/byod/manifest.json` exists and is parseable
2. Its `schema_version` is supported (currently `1`)
3. The platform is registered under `platforms`
4. `~/.mureo/byod/<platform>/` actually exists on disk

If you remove `~/.mureo/byod/google_ads/` out of band but leave the
manifest, mureo logs a warning and falls back to Live API mode for
that platform.

---

## Mixing BYOD and Live API

| Setup | Result |
|---|---|
| Bundle imported for Google Ads; meta_ads not configured | Google Ads = bundle, Meta = Live API |
| Bundle imported for Meta Ads; google_ads not configured | Meta = bundle, Google Ads = Live API |
| Bundles imported for both | Both = bundle |
| Nothing imported | All platforms = Live API |
| `mureo byod clear` | All platforms = Live API |

GA4 and Search Console always go through the Live API OAuth path —
the bundle pipeline does not cover them.

---

## What BYOD blocks

mureo BYOD is **read-only by design**. The agent can analyse your
data, diagnose, and propose actions, but never writes to a real
account. Every method on the BYOD clients with one of the following
name prefixes returns `{"status": "skipped_in_byod_readonly", ...}`:

`create_`, `update_`, `delete_`, `remove_`, `add_`, `send_`,
`upload_`, `pause_`, `resume_`, `enable_`, `disable_`, `apply_`,
`publish_`, `submit_`, `attach_`, `detach_`, `approve_`, `reject_`,
`cancel_`, `set_`, `patch_`.

---

## Switching back to the Live API

```bash
mureo byod remove --google-ads          # remove just one platform
# or
mureo byod clear                         # remove all BYOD data
```

`mureo byod clear` does **not** touch `~/.mureo/credentials.json`,
so your Live API OAuth tokens survive a BYOD reset. After removal,
restart Claude Code; the MCP server detects the missing manifest at
startup and falls back to credentials.json automatically.

If you see `not configured` for the platform you removed,
run `mureo auth setup` (or `mureo configure`) to populate
`~/.mureo/credentials.json`.

---

## Privacy guarantees

- **Local only.** mureo never uploads imported data anywhere. The
  network-isolation tests patch `httpx.AsyncClient.send` and
  `urllib.request.urlopen` and assert zero outbound calls during a
  BYOD-mode tool dispatch and during the bundle import itself.
- **No mureo-managed OAuth.** The Google Ads Script runs under your
  Google Ads account. mureo has no GCP project and no OAuth client
  for the BYOD path.
- **Path-traversal defense.** The bundle importer refuses to write
  outside `~/.mureo/byod/`.

---

## Tabs the bundle importer recognizes

| Source | Tab signal | → mureo CSV(s) |
|---|---|---|
| Google Ads Script | `campaigns` (required) | `campaigns.csv` + `metrics_daily.csv` |
| Google Ads Script | `ad_groups` | `ad_groups.csv` |
| Google Ads Script | `keywords` | `keywords.csv` |
| Google Ads Script | `search_terms` | `search_terms.csv` |
| Meta Ads Export | Any sheet whose header contains a Day column + `Campaign name` + `Impressions` | `meta_ads/{campaigns,ad_sets,ads,metrics_daily}.csv` |

The bundle importer dispatches the Google Ads adapter when at least
one of those tabs is present, and the Meta Ads adapter when it
detects a Meta Ads Manager export-style header. The two adapters are
disjoint — Google Ads tabs use `campaign` (short form) while Meta
uses `Campaign name` (long form), so a workbook can carry only one
adapter's data.

---

## What's *not* in BYOD

- **GA4 / Search Console.** Use the Live API OAuth path
  (`mureo auth setup`); they are not part of the bundle pipeline.
- **`/rescue` budget operations.** The agent can recommend rescues
  but cannot execute them — BYOD is read-only.
- **Live token refresh.** BYOD doesn't read or refresh OAuth tokens.

The Meta export adapter recognizes column headers in English /
日本語 / Español / Português / 한국어 / 繁體中文 / 简体中文 /
Français / Deutsch — exporting from Ads Manager in any of these
languages works without changing Account language.

The spend column is **currency-agnostic**: Meta exports the header
as `Amount spent (XXX)` where `XXX` is the account's ISO currency
code (`JPY` / `USD` / `EUR` / `GBP` / `KRW` / `INR` / etc.) and
mureo strips the suffix before alias matching. Cell values may also
carry a leading currency symbol (`¥` / `$` / `€` / `£` / `₩` / `₹`
/ etc.) which is stripped automatically. Cost values are stored
raw in the account's own currency — mureo does not convert
between currencies, so all per-account analysis (CTR / CPC / CPA)
treats the cost column as a single coherent unit.

For the full live-account experience, run `mureo auth setup` (or
`mureo configure`) and `mureo byod clear` to switch back to
Live API mode.

---

## Google Ads richer BYOD tools

The Sheet bundle's `search_terms` tab is exposed through the existing
Google Ads MCP surface:

| Tool | Returns |
|---|---|
| `google_ads_search_terms_report` | One row per search term: campaign_name, ad_group_name, impressions, clicks, cost, conversions, ctr, average_cpc |

`google_ads_auction_insights_get` / `analyze` return empty under BYOD
(the data isn't available from Google Ads Scripts). Use
`mureo auth setup` for the Live API path if competitor share data is
required.

---

## Troubleshooting

### `Error: <file>: failed to open as XLSX`

Verify the file is the **Microsoft Excel (.xlsx)** download from
*File → Download → Microsoft Excel*. Other formats (Google Sheets
native, ODS, CSV) won't work.

### `Error: <file>: no recognized tabs found`

The bundle importer expects at least one of the Google Ads tabs
listed above. Verify the Google Ads Script actually populated data —
check the Sheet tabs by hand.

### `BYOD data for 'google_ads' already exists.`

Pass `--replace` to overwrite, or run
`mureo byod remove --google-ads` first.

### `mureo byod status` shows BYOD active but `/daily-check` returns nothing

Run `mureo byod status` and look for a "manifest references X but X
is missing on disk" warning in the MCP server logs. If the platform's
directory was deleted out of band, run
`mureo byod import --replace` or `mureo byod remove --<platform>`.

---

## Related

- Sheet template scripts: `scripts/sheet-template/README.md`
- CLI reference: `docs/cli.md`
- Architecture: `docs/architecture.md`
- Live API auth: `docs/authentication.md`
