Skip to content

Proposal: granular Ads toolsets (ads:read / ads:write / ads:plan) via double-tagging #56

Description

@luison

Context

We run a divergent fork of AdLoop (server.py +747/-105 vs upstream). One relevant architectural difference: in our setup, GSC and GTM are external MCP servers rather than the built-in clients. We kept our own GSC (~15 tools, URL inspection / sitemaps / compare) and GTM (~50 tools, full CRUD + publish) MCP servers, because the upstream built-ins are read-only and narrower in scope. So our AdLoop server is purely Ads + GA4 + codebase.

A consequence: the AdLoop tool-schema payload is the single biggest per-session context cost for us (~95K tokens of schemas). That's the pain that made us jump on toolsets the moment v0.13.0 shipped them — it's the highest-leverage, least-intrusive lever to cut that cost (touches only the registry, not tool logic).

Proposal

Subdivide the upstream ads toolset with a double-tag, so ads keeps working exactly as today while power users can opt into a granular subset:

Toolset Tools Use case
ads all Ads (38) full campaign management (= current behavior)
ads:read 14 reads + run_gaql + 3 cross-ref audit / diagnosis (no writes)
ads:write 18 drafts + pause/enable/remove + assets creation without diagnosis
ads:plan estimate_budget + discover_keywords lightweight keyword research

Mechanics are native FastMCP, zero dependency bump:

@mcp.tool(annotations=_READONLY, tags={"ads", "ads:read"})        # double-tag
def get_campaign_performance(...): ...

@mcp.tool(annotations=_READONLY, tags={"ads", "ads:read", "ga4"}) # cross-ref multi-tag
def attribution_check(...): ...

A tool is enabled if the intersection of its tags with the requested set is non-empty, so ads and ads:read coexist without conflict.

Why we think it's worth adopting upstream

  • Real savings, not cosmetic. With ads-only (today's upstream), a typical ads,ga4 session still loads ~46/47 tools (~2–4% reduction). Splitting ads unlocks ~47% reduction for audit flows (ads:read,ga4 → 25 tools) and ~91% for keyword research (ads:plan → 4 tools + external GSC/Serpstat).
  • Fully backwards-compatible. ads plain keeps its current semantics; the sub-tags are additive opt-in. Adopting them would let our fork drop this divergence and realign with upstream.
  • Generalizable, not fork-specific. Any user doing light audits or keyword research benefits — nothing here depends on our external-MCP setup.
  • Cross-ref tools get multi-tagged (attribution_check, etc. carry both ads:read and ga4), avoiding a silent functional regression for ga4-only sessions that still need Ads→GA4 diagnostics.

Ask

  • Feedback on the ads:read / ads:write / ads:plan split — naming and boundaries.
  • Whether this granularity aligns with the direction you see for toolsets, or if you'd prefer a different shape.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions