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.
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
adstoolset with a double-tag, soadskeeps working exactly as today while power users can opt into a granular subset:adsads:readrun_gaql+ 3 cross-refads:writeads:planestimate_budget+discover_keywordsMechanics are native FastMCP, zero dependency bump:
A tool is enabled if the intersection of its tags with the requested set is non-empty, so
adsandads:readcoexist without conflict.Why we think it's worth adopting upstream
ads-only (today's upstream), a typicalads,ga4session still loads ~46/47 tools (~2–4% reduction). Splittingadsunlocks ~47% reduction for audit flows (ads:read,ga4→ 25 tools) and ~91% for keyword research (ads:plan→ 4 tools + external GSC/Serpstat).adsplain keeps its current semantics; the sub-tags are additive opt-in. Adopting them would let our fork drop this divergence and realign with upstream.attribution_check, etc. carry bothads:readandga4), avoiding a silent functional regression forga4-only sessions that still need Ads→GA4 diagnostics.Ask
ads:read/ads:write/ads:plansplit — naming and boundaries.