Skip to content

Releases: logly/mureo

v0.9.9 — analytics module for external-integration platforms

23 May 08:23
7d4b42d

Choose a tag to compare

Per-platform analytics module surface for external-integration platforms (#120)

External-integration platforms — official MCPs and third-party plugins alike — now have an opt-in path to mureo's deep analytics on the same terms as the built-in google_ads / meta_ads adapters. Workflow skills (daily-check, rescue, …) consult a new registry and either run the platform's analytics module or honestly report analytics_not_available_for_<platform>. Auto-deriving heuristics from tool schemas is explicitly rejected — it would fabricate plausible-but-wrong analysis and violate mureo's trustworthiness principle.

New public surface in mureo.analytics

  • ProtocolAnalyticsModule (runtime_checkable, opt-in) with four methods: detect_anomalies, diagnose_performance, audit_creative, analyze_budget_efficiency. Modules advertise their actually-supported subset via capabilities(); un-advertised methods raise NotImplementedError. Discovery validates structurally and detects un-overridden Protocol stubs by qualified name.
  • Capability enumAnalyticsCapability (DETECT_ANOMALIES, DIAGNOSE_PERFORMANCE, AUDIT_CREATIVE, ANALYZE_BUDGET_EFFICIENCY).
  • Frozen-dataclass modelsAnomaly, AnomalySeverity, PerformanceDiagnosis (with optional per_campaign_metrics for DEEP scope), PerformanceScope, CreativeAudit (with findings + per_campaign_summary), CreativeFinding (with campaign_id), BudgetEfficiency.
  • Registry — entry-point group mureo.analytics, independent of mureo.providers / mureo.skills. Plugin packages register one class via [project.entry-points."mureo.analytics"]. Built-in adapters auto-register; broken plugins are skipped with AnalyticsModuleWarning and never crash the MCP server.
  • MCP toolmureo_analytics_modules_list returns the registered platforms + advertised capabilities + source distribution so skills can branch dynamically.
  • TypedDictsGoogleLivePerformanceRow, GoogleByodPerformanceRow, GoogleMetricsDict, GooglePerformanceRow, MetaLivePerformanceRow, MetaByodPerformanceRow, MetaActionEntry, MetaPerformanceRow, GoogleAdRow, MetaAdRow — re-exported for plugin-side typing. All total=False; field-set contract documented in docs/ABI-stability.md §4a.

Built-in adapters wired against live + BYOD clients

  • google_ads and meta_ads advertise all four capabilities.
  • detect_anomalies runs per-campaign fan-out — single-campaign anomalies are no longer masked by offsetting movements at the aggregate.
  • diagnose_performance returns aggregate metrics by default and per-campaign drilldown (sorted by spend descending, one finding per campaign with spend / CV / CPA) at PerformanceScope.DEEP.
  • audit_creative checks RSA / RDA / Meta ad shape against Google's Ad-Strength thresholds and Meta's creative requirements, stamps campaign_id on every finding, and exposes a sorted CreativeAudit.per_campaign_summary.
  • analyze_budget_efficiency normalises conversions/cost across campaigns and emits a concrete reallocation suggestion when the spread is wide.
  • Lazy auth + BYOD routing; missing credentials in live mode produce sentinel responses (empty anomaly tuple, sentinel PerformanceDiagnosis headline). Tolerates both live row shape and BYOD flat shape — regression tests pin both.

Skill integration

daily-check, rescue, _mureo-shared updated to consult mureo_analytics_modules_list before running deep diagnostics on any external-integration platform.

Docs

  • docs/plugin-authoring.md §14 "Shipping analytics with your plugin"
  • docs/ABI-stability.md §4a (Protocol contract) + §6 (entry-point group mureo.analytics) + TypedDict field-set rules

PRs in this release

  • #137 — Protocol + registry + skill integration + docs
  • #138 — Live client wiring + BYOD shape fix
  • #139 — Per-campaign fan-out + audit_creative + analyze_budget_efficiency
  • #140 — Per-campaign drilldown for audit_creative
  • #141 — DEEP scope drilldown for diagnose_performance + TypedDicts
  • #142 — Release commit

Closes #120.

v0.9.8 — Meta Ads period silent-fallback fix (#134)

22 May 07:28
9c259e2

Choose a tag to compare

Fixed — Meta Ads period argument no longer silently falls back to last_7d (#134, #135)

The Meta Ads MCP tools' period argument advertised last_14d, last_90d, and explicit YYYY-MM-DD..YYYY-MM-DD ranges, but the implementation accepted only six hard-coded preset names and silently returned last_7d data for anything else. Period-over-period analyses (meta_ads_analysis_performance, meta_ads_analysis_cost) doubled the bug: the "previous" window was likewise mapped via a tiny dict that, for last_7d, returned last_30d — a superset that overlaps the current window, making every delta meaningless.

This release wires the full advertised surface through to the Meta Graph API:

  • New mureo/meta_ads/_period.py: resolve_period(period) returns either ("date_preset", str) or ("time_range", (since, until)). Unknown values raise ValueError — there is no silent fallback. ISO date validation, ordering, and .. separator counting all surface clear errors at the boundary.
  • get_performance_report and get_breakdown_report now build their request params via the resolver, so a YYYY-MM-DD..YYYY-MM-DD period is forwarded as Meta's time_range, and last_14d / last_90d are forwarded as date_preset (instead of being silently downgraded).
  • previous_period(period, *, today=…) returns a same-length window that sits immediately before the current window. For last_7d the previous block is the 7 days before that — never the last_30d superset. this_month round-trips to last_month; last_month returns an explicit calendar-month range so callers don't need to do calendar arithmetic themselves.
  • AnalysisMixin.investigate_cost and InsightsMixin.analyze_performance both use the new helper, so their current/previous comparison is correct for every accepted period shape (including custom date ranges).
  • _PERIOD_PARAM tool description lists every preset the resolver accepts (this_month / last_month added) and clarifies that custom ranges are inclusive at both endpoints.

Backwards compatibility: callers relying on the silent last_7d fallback now see a ValueError. This is the bug being fixed — they were always getting the wrong window. Every previously-working preset continues to work, plus the previously-advertised-but-broken last_14d, last_90d, and YYYY-MM-DD..YYYY-MM-DD shapes.

Install

pip install --upgrade mureo

v0.9.7 — self-describing providers via account_credential_fields

22 May 06:09
038c441

Choose a tag to compare

Added — Optional account_credential_fields for self-describing providers (#131)

Provider plugins (built-in google_ads / meta_ads, and third-party plugins discovered via the mureo.providers entry-point group) can now declare an optional account_credential_fields: tuple[AccountCredentialField, ...] class attribute so introspection tooling — the mureo providers … CLI, configuration wizards, plugin authoring guides — can render setup prompts, validate config, and document plugins without hardcoding per-provider knowledge.

from mureo.core.providers import AccountCredentialField

class MyAdsProvider:
    name = "my_ads"
    display_name = "My Ads"
    capabilities = frozenset({...})
    account_credential_fields = (
        AccountCredentialField(
            key="advertiser_id",
            display_name="Advertiser ID",
            placeholder="adv-12345",
            required=True,
            description="From the MyAds dashboard.",
        ),
    )
  • New public surface in mureo.core.providers: AccountCredentialField (frozen dataclass — key, display_name, placeholder="", required=False, description="") and get_account_credential_fields(provider) -> tuple[AccountCredentialField, ...] accessor. The accessor reads the optional attribute defensively (returns () when absent) and validates the shape (tuple of AccountCredentialField only) — malformed declarations raise TypeError at introspection time, not deep inside the consuming UI.
  • BaseProvider Protocol is unchanged. The new attribute is documented as optional in the BaseProvider docstring; the Protocol body itself stays stable so every pre-feature plugin keeps loading without modification.
  • Built-in adapters updated: mureo.adapters.google_ads.GoogleAdsAdapter declares customer_id; mureo.adapters.meta_ads.MetaAdsAdapter declares ad_account_id. Operator-shared credentials (developer token, app secret, refresh token, MCC login_customer_id) intentionally do NOT appear — those belong to a separate operator-level layer.
  • Plugin author guide: docs/plugin-authoring.md §3 (BaseProvider) gains a Declaring per-account credential fields (optional) subsection covering the dataclass shape, defaults, and the accessor's defensive-read / strict-validation semantics.

Backward compatibility: providers that do not declare account_credential_fields continue to load unchanged; get_account_credential_fields() returns (), which downstream tooling treats as "no per-account configuration needed."

Install

pip install --upgrade mureo

v0.9.6 — optional per-locale labels for web-extension nav tabs

22 May 02:29
7874d78

Choose a tag to compare

Added — Optional per-locale labels for web-extension nav tabs (#129)

Web extensions can now ship an optional display_name_i18n: Mapping[str, str] class attribute alongside display_name so the configure-UI nav tab follows the active locale. Built-in nav tabs (Setup / Demo / BYOD / Danger Zone) are already translated via data-i18n keys in i18n.json; extension tabs now follow the same convention without extension authors having to touch the OSS i18n.json catalog.

class MyExtension:
    name = "acme-vault"
    display_name = "Acme Vault setup"            # fallback for any locale
    display_name_i18n = {
        "en": "Acme Vault",
        "ja": "Acme Vault 設定",
    }

Lookup priority on the renderer side: display_name_i18n[active_locale]display_name_i18n["en"]display_name. Operators who toggle the configure-UI locale see your tab name update without a page reload — the renderer listens for the existing mureo:locale_changed event.

  • mureo.web.extensionsWebExtensionEntry gains a display_name_i18n: Mapping[str, str] field that defaults to {} so existing constructors continue to work unchanged. The WebExtension Protocol is unchanged — the new attribute is read defensively via getattr so every pre-feature extension keeps loading without modification. Discovery validates the value as Mapping[str, str] (str keys and values both required) and skips the extension with a WebExtensionWarning if the shape is wrong (explicit None, list of pairs, int keys, int values).
  • HTTPGET /api/extensions includes a new display_name_i18n field per entry (empty {} when the extension did not declare any). JSON-only addition; existing consumers ignore unknown keys.
  • Front-end (mureo/_data/web/extensions.js) — initial render reads document.documentElement.lang and looks up display_name_i18n[locale] with the fallback chain above. A mureo:locale_changed listener (fired by app.js#setLocale) re-runs the lookup so every nav label updates the moment the operator toggles 日本語 / English.
  • Plugin author docsdocs/plugin-authoring.md §13 gains a Localising the nav-tab label subsection with the example class attribute, the documented lookup priority, and the empty-string fallthrough note.

Security: labels are injected via textContent (never innerHTML), so a label containing <script> or similar is harmless DOM text. The configure-UI Content-Security-Policy (script-src 'self'; style-src 'self') is unchanged.

Backward compatibility: extensions that do not declare display_name_i18n get an empty dict in their WebExtensionEntry; the renderer's fallback chain resolves to display_name, so the nav tab looks byte-identical to v0.9.5.

Install

pip install --upgrade mureo

v0.9.5 — web extensions for the configure UI

21 May 10:58
b31960f

Choose a tag to compare

Added — Web extensions: third-party tabs and API routes for mureo configure (#127)

A new entry-point group mureo.web_extensions lets a plugin register additional tabs and API routes inside the mureo configure wizard without each surface having to know about the plugin. The mechanism mirrors the existing mureo.providers / mureo.runtime_context_factory entry-point patterns: discovery iterates the group exactly once at startup, isolates per-plugin faults (WebExtensionWarning), and exposes survivors as frozen WebExtensionEntry records consumed by mureo.web.handlers.

  • mureo.web.extensions — public surface: WebExtension Protocol (name, display_name, routes(), view()), frozen dataclasses RouteContribution(method, subpath, handler), ViewContribution(html_fragment, scripts, styles), StaticAsset(filename, content_type, body), plus discover_web_extensions() / reset_web_extensions() and the regex constants (NAME_PATTERN, SUBPATH_PATTERN, FILENAME_PATTERN) shared with the dispatch layer.
  • HTTP surface in mureo.web.handlers:
    • GET /api/extensions — index for the front-end renderer (one entry per extension; view is null for headless / route-only plugins).
    • GET /api/ext/<name>/<subpath> — extension GET route; payload is the flattened query string (first-value-wins).
    • POST /api/ext/<name>/<subpath> — extension POST route, gated by the existing Host + body-cap + CSRF pipeline (the plugin author inherits CSRF protection for free).
    • GET /static/ext/<name>/<filename> — extension-shipped static asset served from in-memory bytes with the same Content-Security-Policy + X-Frame-Options + Cache-Control header stack as the bundled static files.
  • Front-end (mureo/_data/web/extensions.js): the configure UI fetches /api/extensions once when the dashboard opens, renders one nav tab per extension, and lazy-loads each extension's html_fragment / scripts / styles on first tab activation. Operators who never visit a given tab pay zero added page weight.
  • Plugin author guide: docs/plugin-authoring.md §13 documents the contract end-to-end (entry-point setup, sample WebExtension, URL surface, CSP / CSRF / fault-isolation model, lazy-load behaviour, debugging recipe).
  • Security: subpaths and filenames are regex-validated at both registration and dispatch so .., double-slash, trailing slash, ?, #, and directory separators cannot smuggle the dispatcher outside /api/ext/<name>/ or /static/ext/<name>/. Static asset bodies stay in memory; the dispatcher never reads from disk so filesystem traversal is impossible by construction. html_fragment is rejected at registration if it contains <script>, <style>, on*= event handlers, or javascript: URLs — the CSP (script-src 'self'; style-src 'self') is the runtime enforcement, the regex is the explicit author-feedback signal. Handler exceptions are caught by the dispatcher and surfaced as a generic {"error": "extension_handler_error"} 500 envelope; exception details are logged server-side only (they may carry secrets the handler touched).

Backward compatibility: when no third-party mureo.web_extensions entry points are installed, discover_web_extensions() returns an empty tuple, /api/extensions returns [], the renderer creates zero DOM nodes, and the configure UI is byte-identical to v0.9.4.

Install

pip install --upgrade mureo

v0.9.4 — extension protocols + mureo learn CLI

21 May 07:02
f06d1cd

Choose a tag to compare

Added — Extension Protocols and mureo learn CLI (#125)

A new public surface under mureo.core lets alternate backends and tests inject pluggable persistence without forking call sites. The shape mirrors the existing mureo.core.providers and mureo.core.skills extension patterns. Every default reproduces today's file-backed behaviour, so existing users see no change.

  • mureo.core.SecretStoreProtocol for credential round-trip (load / save / delete). Default FilesystemSecretStore reads and writes ~/.mureo/credentials.json byte-for-byte equivalent to the previous flow (atomic write, 0o600 via mureo.fsutil.secure_fchmod, ensure_ascii=False).
  • mureo.core.StateStoreProtocol for STATE.json / STRATEGY.md / action_log persistence. Default FilesystemStateStore composes the existing helpers in mureo.context.state / mureo.context.strategy.
  • mureo.core.KnowledgeStore — two-tier Protocol for /learn insights (operator + workspace). Default FilesystemKnowledgeStore writes to today's ~/.claude/skills/_mureo-pro-diagnosis/SKILL.md location with the same frontmatter scaffold.
  • mureo.core.ThrottleStoreProtocol for per-key API rate limiting. Default ProcessLocalThrottleStore wraps mureo.throttle.Throttler; register(key, config) pre-installs custom buckets matching the MCP server's _PLUGIN_TOOL_THROTTLERS pattern.
  • mureo.core.RuntimeContext — frozen dataclass aggregating the four stores plus a workspace_id. DEFAULT_WORKSPACE_ID = "default" is the canonical single-workspace sentinel.
  • mureo.core.default_runtime_context() — factory wiring the four file-backed defaults.
  • mureo.core.get_runtime_context() — process-cached resolver that discovers a single zero-arg factory under the mureo.runtime_context_factory entry-point group; raises RuntimeContextFactoryError on multiple registrations or a returning-non-RuntimeContext factory.

Added — mureo learn add CLI

mureo learn add <text> [--scope {operator,workspace}] persists /learn insights through RuntimeContext.knowledge_store rather than writing files directly. Default scope operator writes the cross-workspace tier (today's pro-diagnosis location); --scope workspace writes a workspace-scoped tier if one is configured. The /learn skill (skills/learn/SKILL.md) now invokes the CLI instead of carrying its own copy of the file scaffold.

Changed — Consumers routed through the new Protocols

These refactors are call-site changes only; all on-disk artefacts and CLI behaviour are byte-equivalent in the default file-backed runtime.

  • mureo.auth.load_google_ads_credentials / load_meta_ads_credentials read through SecretStore (get_runtime_context().secret_store when path is not passed; one-shot FilesystemSecretStore(path=…) when it is).
  • MCP handlers mureo_strategy_*, mureo_state_*, rollback_*, analysis_anomalies_check resolve their path / state_file argument against state_store.workspace rather than raw CWD. Error messages and traversal-refusal semantics are preserved; symlink refusal in the analysis handler is unchanged.
  • MCP plugin dispatch acquires its throttle slot via RuntimeContext.throttle_store. The default ProcessLocalThrottleStore is seeded with the existing per-tool Throttler instances (_PLUGIN_TOOL_THROTTLERS) on first call; alternate backends receive acquire(name) and own per-key fallback semantics.
  • mureo.cli.rollback_cmd --state-file default is now resolved through RuntimeContext (rather than the literal Path("STATE.json")).
  • mureo.byod.runtime.byod_data_dir() adds a middle-priority resolution path: when a non-default RuntimeContext exposes a filesystem workspace, BYOD data lives at <workspace>/byod/. MUREO_BYOD_DIR env var and the legacy ~/.mureo/byod/ fallback are unchanged.

Install

pip install --upgrade mureo

v0.9.3 — Windows compatibility

19 May 05:31
e19aed0

Choose a tag to compare

Fixed — Windows compatibility (#122)

mureo crashed on Windows. This release makes the core (CLI + mureo configure web UI) run on Windows, verified by a new real windows-latest CI job.

  • os.fchmod crash (Unix-only) in every credential/config write path → new mureo/fsutil.py (secure_fchmod/secure_chmod): owner-only 0o600 on POSIX (byte-identical, no Linux/macOS change), best-effort never-raising no-op on Windows. NTFS confidentiality relies on the %USERPROFILE% profile ACL (documented best-effort).
  • Interactive setup menu (simple_term_menu) imports Unix-only termios and raised NotImplementedError on Windows, making the plain number-input fallback unreachable → both fallbacks now except (ImportError, NotImplementedError) (also degrades gracefully in non-terminal envs: CI, pipes, PyCharm).
  • mureo configure resolved the wrong Claude Desktop config path on Windows → host_paths now returns %APPDATA%\Claude\claude_desktop_config.json (macOS unchanged; Linux keeps the Code-style fallback — Claude Desktop has no Linux build).

CI

  • Added a windows-latest CI job — the real-Windows verification + an automatic regression guard. POSIX-only test assertions were made platform-aware. Also deflaked a stop-lifecycle test (event-set race) surfaced by the new matrix.

Known limitations (out of scope; not crashes)

  • mureo install-desktop (CLI) is still macOS-only by explicit design — a Windows launcher is a separate feature; it errors gracefully off macOS.
  • Real-desktop UX not exercisable by headless CI (browser auto-open, native file/folder picker dialog) is not yet end-to-end verified on Windows.

Full changelog: CHANGELOG.md

v0.9.2 — configure Desktop host-confirm fix

18 May 03:55
1eae27a

Choose a tag to compare

Fixed

mureo configure no longer misroutes Claude Desktop users on the Meta connector finalize (#118)

A Claude Desktop user who had connected the Meta hosted MCP saw a misleading "not connected yet — finish the Meta login" on finalize. The in-memory session.host could reset to the claude-code default, so the Claude Code claude mcp list verification path ran for a Desktop user (no CLI ⇒ accusatory dead-end). The official Meta MCP itself was always usable — only the switch-native-off step was wrongly blocked (tool ambiguity, never a strand).

  • Client-authoritative host — confirm / native-toggle resolve host from the request payload (validated, self-heals a stale session).
  • Host-sync hardening — explicit choice persisted to localStorage and preferred over the server-echoed value; /api/host retries once + toast instead of silent swallow; host re-asserted on load.
  • Tri-state connectivityconnected / not_connected / unknown; unknown (no Claude Code CLI / timeout) is not "not connected". Desktop / unknown now offer an explicit "I've verified it" affirm path that applies the native↔official switch — no-strand preserved.

Full changelog: CHANGELOG.md

v0.9.1 — mureo safety layer for plugin tools

18 May 00:51
8ea35cc

Choose a tag to compare

Highlights

mureo safety layer for third-party plugin tools (#114, #116) — opt-in & purely additive via standard MCP Tool metadata, no plugin-side changes required:

  • Phase 1 — audit (~/.mureo/plugin_audit.jsonl, secret-masked, 0600) / throttle / fault-isolation (record-then-reraise; never crashes or silently swallows).
  • Phase 2 — classify via readOnlyHint (undeclared ⇒ mutating) + optional _meta["mureo"] (reversal, throttle); successful mutating calls promoted into STATE.json action_log (platform="plugin:<dist>", only when a STATE.json exists).
  • Phase 3 — provider-aware skill guidance (plugin platforms enumerated best-effort, treated advisory).
  • Phase 4 — structural strategy parity: mutating calls get an observation_due window (14-day default, _meta["mureo"]["observation_days"] overridable) so daily-check reviews outcomes like a built-in.

Honest scope: confirm + STRATEGY gating are skill-mediated; audit/action_log/observation/rollback-intent are mechanical — the same channel built-ins use. mureo's platform-specific analytics and executable auto-rollback for arbitrary ops are not generically possible and not claimed. See docs/plugin-authoring.md, docs/ABI-stability.md.

Also since v0.9.0

  • Fixed: mureo configure frees the terminal on finish / Ctrl+C (#111).
  • Docs: getting-started leads with mureo configure + 'Before you start' (#109, #110); BYOD/Demo are mureo-native only (#112).

Full changelog: CHANGELOG.md

v0.9.0

16 May 10:09
d4797d5

Choose a tag to compare

Fixed — /learn slash command restored (regression from #77)

  • Phase 3 plugin packaging (#77) migrated every .claude/commands/*.md slash command into an operational skill under skills/ + the bundled mureo/_data/skills/, but dropped learn entirely (deleted .claude/commands/learn.md, never created a learn skill). /learn became uninvocable while every other workflow command kept working, even though README/docs still document it. Restored as an operational skill (skills/learn/ + byte-identical bundled copy): name: learn (no _ prefix → appears in the picker), saves insights to ../_mureo-pro-diagnosis/SKILL.md (scaffolding that canonical-only knowledge base on first use), approval-required and append-only, never Claude memory or secrets/PII.

Changed — mureo configure visual refresh + official mureo logo

  • The configure Web UI got a cohesive design-system pass (refined spacing/type/color tokens, light and dark via prefers-color-scheme, crafted cards/buttons/focus states, system fonts only — strict CSP, no web fonts/CDN/build). The header now shows the official mureo wordmark (bundled logo.png / logo-dark.png, scheme-swapped). CSS-only; every data-* / data-i18n hook and EN/JA parity preserved.

Added — Google Ads OAuth-scope guidance in the auth step

  • The Web UI Google Ads auth step and docs/authentication.md now explain that a reused refresh token must carry the Google Ads scope https://www.googleapis.com/auth/adwords or API calls fail with ACCESS_TOKEN_SCOPE_INSUFFICIENT, with a link to the official Google scope reference. mureo's own OAuth already requests it; the note prevents the failure when users supply a hand-minted token.

Fixed — Meta hosted MCP on Claude Code goes through the Claude.ai connector (supersedes the earlier "/mcp register" Unreleased note)

  • A prior Unreleased change had mureo configure / mureo providers add / the wizard register meta-ads-official into ~/.claude.json on Claude Code and tell the user to finish OAuth via /mcp → Authenticate. Real-environment verification proved this cannot work: Meta's hosted MCP (https://mcp.facebook.com/ads) does not support OAuth Dynamic Client Registration, so Claude Code's /mcp OAuth fails with SDK auth failed: The provided redirect_uris are not registered for this client. Registering it locally only creates an unauthenticatable user-scope server. Corrected behavior: on Claude Code, mureo now does not register Meta locally at allinstall_provider / mureo providers add return manual_required (no ~/.claude.json write, no subprocess) and the UI/CLI point the user to add Meta as a Claude.ai account connector (claude.ai → Settings → Connectors → Add custom connector → https://mcp.facebook.com/ads; Anthropic brokers the Meta Business sign-in there, requires a paid plan, then works account-wide in Claude Code and Claude Desktop and surfaces as mcp__claude_ai_MetaAds__*). mureo-native Meta is still not auto-disabled (nothing registered/verified — native steps aside only via mureo providers confirm once the connector is verified Connected; no-strand preserved). Claude Desktop is unchanged (manual_required, Settings → Connectors). This re-aligns with the "no dead config entry / Connectors" behavior described in the bullets below; the intervening "/mcp register on Code" wording is withdrawn.

Docs — mureo configure is now the documented front door; auth setup --web removed

  • mureo auth setup --web was removed (its browser credential flow is now part of the unified mureo configure UI). README and docs (cli.md, authentication.md, getting-started.md/.ja.md, byod.md/.ja.md, architecture.md) updated: every auth setup --web reference now points to mureo configure (or terminal mureo auth setup). README gained a top-of-"Choose your setup" quickstart — pip install mureo + mureo configure — enumerating what the browser UI does (host pick, basic setup, OAuth/credentials, official MCP providers, native↔official toggle, Demo/BYOD).

Fixed — official/native precedence when mureo MCP is configured after an official provider

  • MUREO_DISABLE_<PLATFORM> (which makes the mureo-native MCP step aside so an official provider is the single source for a platform) was only auto-set by mureo providers add when a mcpServers.mureo block already existed. A user who registered the official provider first and configured the mureo MCP later ended up with native + official both active and no deterministic precedence (tool ambiguity). install_mureo_mcp (the path both basic-setup and the dashboard use) now backfills the disable env, after the mureo block is written, for already-registered pipx/npm official providers (google-ads-official, ga4-official) detected by a pure host-config registry read. Meta (hosted) is intentionally out of backfill scope — detecting it needs a network claude mcp list probe that must not run on the basic-setup path; Meta native↔official is the explicit mureo providers confirm / dashboard native-toggle (both gate on the verified connector — no-strand preserved). Best-effort and idempotent (never raises, never invents a mureo block); Search Console is never disabled. Works for both Claude Code (~/.claude.json) and Claude Desktop (claude_desktop_config.json).
  • Web-UI per-platform native↔official toggle — the dashboard now shows, per official provider (when the mureo MCP is configured), the current tool source for that platform and a button to switch. POST /api/providers/native-toggle sets/unsets MUREO_DISABLE_<PLATFORM>; status exposes the per-platform state. Switching to official is allowed only when the official path is actually usable (pipx/npm provider registered, or Meta connector verified Connected) — refused with an actionable message otherwise; switching back to native is always allowed (the un-strand path). Restart Claude to apply (the flag is read once at MCP start). Host-aware (Code + Desktop), EN + JA.

Fixed — GA4 wizard inputs were collected but never saved

  • The configure-UI auth wizard's GA4 step rendered the service-account-path / project-id inputs and a "Done" button whose handler only advanced the wizard — the entered values were discarded, so GOOGLE_APPLICATION_CREDENTIALS / GOOGLE_PROJECT_ID were never written to credentials.json and the official ga4-official MCP launched unauthenticated (same class as the earlier Google Ads bug). The Done handler now POSTs each value through the allow-listed /api/credentials/env-var writer (into the ga4 section) before advancing, only proceeding if every write succeeds (otherwise it surfaces a save-failed message and stays on the step). Host-accurate labels + saving/failure status added (EN + JA).

Changed — host selector clarity + Desktop-unavailable credential-guard hook note

  • The configure-UI host selector labels were ambiguous (Claude Code (terminal) implied terminal-only). Relabelled to Claude Code (CLI, Desktop app) vs Claude Desktop app (Chat, Cowork) so users running Claude Code inside the Desktop app correctly pick the Claude Code option (which targets ~/.claude.json). Japanese punctuation made consistent (fullwidth ).
  • The credential-guard hook has no surface on Claude Desktop (install_auth_hook is a noop:unsupported_on_desktop there). The basic-setup list (wizard and dashboard) now appends "(not available on the Desktop app)" / "(デスクトップアプリでは利用できません)" to that row when the chosen host is Claude Desktop, instead of implying it can be installed.

Fixed — dashboard "mureo integrations" listed GA4 (not native) and omitted Search Console

  • The configure-UI dashboard's mureo integrations section listed Google Ads / Meta Ads / GA4. mureo ships no native GA4 tools (GA4 is official-provider-only), so GA4 did not belong there; meanwhile the genuinely mureo-native Search Console was missing (only a sub-note under Google Ads). GA4's presence came from the ga4 credentials.json section, which actually stores the official GA4 MCP's service-account env — not a mureo-native integration.
  • Removed the GA4 row; added a Search Console row. Search Console has no own credentials section (it reuses the Google Ads Google OAuth — adwords + webmasters scopes), so the row is status-only: it shows configured the moment the wizard's Search Console / Google sign-in is done (driven by the existing credentials_oauth.google signal) and has no standalone Remove (a note directs removal to the Google Ads row, since the sign-in is shared).

Fixed — official Meta Ads provider registered a dead entry on Claude Code

  • Adding the official Meta Ads MCP (meta-ads-official, hosted at https://mcp.facebook.com/ads) on Claude Code wrote a raw {"type":"http","url":…} entry into ~/.claude.json and set MUREO_DISABLE_META_ADS=1. But Meta's hosted MCP has no OAuth Dynamic Client Registration, so Claude Code can never connect that raw entry (✗ Failed to connect) — while mureo-native Meta was disabled, leaving the user with zero Meta capability (the model fell back to a mureo-native "credentials not found" error). The Desktop path already short-circuited this; the Code path and the mureo providers add CLI did not.
  • Claude Code, the mureo providers add CLI, and Claude Desktop now treat hosted_http providers consistently: no dead config entry is written and mureo-native tools are NOT auto-disabled (auto-disabling before the official path is verified strands the user). The result is manual_required, and the UI/CLI now point the user to Claude's account-level Connectors (the working path mureo cannot create programmatically). Connectors setup guidance is host-accurate — terminal (Claude Code) vs Claude Desktop have genuinely different steps — EN + JA. mureo providers remove still self-heals a stale `MUREO_D...
Read more