feat(gtm): opt-in write tools — tags, triggers, delete, publish (replaces #35) - #69
Open
illia-sapryga wants to merge 5 commits into
Open
illia-sapryga wants to merge 5 commits into
illia-sapryga wants to merge 5 commits into
Conversation
Tag Manager edit + publish scopes are requested only when the user sets gtm.write_enabled, so audit-only users never grant publish authority. Enabling it on an existing read-only token triggers the usual re-consent (stored scopes no longer cover the requested set). Adds gtm.allow_custom_html (enforced by the write module), a separate gtm_write_credentials provider hook so hosted providers get a capability error until they opt in, and strict parsing for both flags so a quoted "false" can't switch a safety gate on.
Four tools under the gtm toolset, all draft -> preview -> confirm_and_apply: draft_gtm_tag / draft_gtm_trigger (create, or update by id), draft_delete_gtm_entity, draft_publish_gtm_workspace. - Apply handlers take the config confirm_and_apply passes (no load_config) - Updates are read-modify-write from the live resource, so unmanaged fields (priority, consentSettings, tagFiringOption, schedule, folder, setupTag, ...) round-trip; parameters merge by key - Updates/deletes pin the draft-time fingerprint; publish pins a digest of the workspace's pending changes — apply refuses if either moved - Custom HTML create/edit, and publishing a workspace that adds/changes one, is refused unless gtm.allow_custom_html; pause/delete always allowed - Canonical template IDs only (sp for Ads remarketing, cvt_ for gallery); trigger types use the API's camelCase enum - Publish refuses on merge conflicts and on compilerError (a boolean) - Dry run runs a GTM preflight (Tag Manager has no validate-only mode) - Missing-scope 403s map to an actionable re-consent message - List params use the JSON-string-tolerant aliases (kLOsk#28)
Covers the opt-in and scope handling, template/trigger validation, the Custom HTML gate (draft, apply-time recheck, UI-made HTML in publish), field preservation on update, fingerprint and workspace-digest refusal, compiler-error publish, dry-run preflight, audit logging, and JSON-string list coercion through the MCP layer.
4 tasks done
destructive_hint only exists on newer SDKs; destructiveHint works on both (matching test_reddit_write.py).
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #35. Rebuilt from fresh commits on current
main(on top of the #29 read layer you brought up), so the diff is only the write surface: no old history, no business data in fixtures.Your July blockers → what changed
_apply_*handlers calledload_config()confirm_and_applypasses. GTM plans dispatch from_execute_planvia agtm_prefix, like Reddit, before the Ads client is built.gtm.allow_custom_htmlgate, off by default, enforced not advisory. It refuses to create or edit anhtmltag, and refuses to publish a workspace that adds or changes one, including HTML someone added by hand in the GTM UI. It's re-checked at dry run and at apply, so flipping the flag off after drafting still blocks. Pausing or deleting an HTML tag is always allowed._ALL_SCOPESgtm.write_enabled: true(default off) via_requested_scopes(config). Enabling it on an existing token triggers the existing stale-scope re-consent. There's a separategtm_write_credentialsprovider hook, so Cloud gets a capability error (not write tokens) until you implement it.tests/test_gtm_write.py(65 tests) runs every apply path end to end against an in-memory Tag Manager fake. No source-grep dispatch tests._configcurrent_config(). The static test passes.write_enabled, deletetoken.json, re-consent, and check container Edit/Publish permission" message. The README and rules document the setup._pending_plansset_plan_store(InMemoryPlanStore())_DictListOpt/_StrListOpt, with a test that goes throughmcp.call_toolusing JSON-string lists (#28).priority,consentSettings, …priority,consentSettings,tagFiringOption,schedule*,parentFolderId,monitoringMetadataandsetupTag, and fail if the body is rebuilt from a field list. On update,parametersmerge by key.awcrvsspspfor Ads remarketing, matchingread.py), pluscvt_<id>for Gallery templates. The trigger types had the same class of bug (dom_ready,scroll_depth, … aren't API values). They now use the API's camelCase enum.Other safety additions
compilerError.compilerErroris a boolean onCreateContainerVersionResponse; the old code treated it as a list.preflight(Tag Manager has no validate-only mode). It reuses the existing Reddit branch inconfirm_and_apply, generalised by platform label.workspace_idinstead of guessing.blocked_operations:gtm_create_tag,gtm_update_tag,gtm_delete_tag,gtm_create_trigger,gtm_update_trigger,gtm_delete_trigger,gtm_publish_workspace.Footprint
Four tools instead of the seven in #35. Create and update share one tool per entity, and the two deletes are one tool. All four are tagged
gtm, and they refuse with setup guidance untilwrite_enabledis set. I updated thegtmdescription inTOOLSETSto mention writes, and the slug list is unchanged. Flagging it since the dashboard pins that contract.791 tests passing locally.