# Vendor write surfaces and read-only modes

What each server can change, where a read-only boundary is actually enforced, and how to read back and revert. Every point was checked against the vendor's own page on 2026-10-17 and the sections marked "re-checked" again on 2026-10-20. Write surfaces change without notice: re-list the tools in the live client each session and compare with this file. Tool lists below are partial where marked; the live tool pages govern.

Terms used below:

- Enforcing layer: where a read-only boundary is enforced by the vendor or the source system. Client confirmation prompts and tool annotations are not enforcement.
- Hard-to-reverse: starts spend, sends, publishes, deletes, merges, changes suppression, consent or permissions, reclassifies or hides data, or moves data out.
- Tool-list evidence: some read-only layers remove write tools from the tool list (Klaviyo, PostHog). Permission and IAM layers leave write tools listed and reject the call (Meta, GTM, HubSpot, Salesforce profiles, Braze, Mixpanel, BigQuery). For those, the evidence is a denied harmless write in a test account, not the tool list.

## Meta Ads (Meta Ads MCP)

Sources: developers.facebook.com, Ads MCP Server overview, get-started and tool pages; Marketing API budgets page. Re-checked 2026-10-20.

- Server: `https://mcp.facebook.com/ads`, hosted by Meta. Seven tool categories: reporting, ad creation and management, catalogs, signals and datasets, Help Center search, A/B tests and lift studies, activity logs.
- Token permissions: at minimum `ads_mcp_management` plus `ads_read` or `ads_management`. The others (`ads_management`, `catalog_management`, `business_management`, `pages_show_list`, `instagram_basic`) "enable access to more tools".
- System user tokens: only Employee-role system user tokens are supported. Admin-role system user tokens cannot access the server.
- OAuth: the client opens Facebook Login for Business. Record which permissions the person granted; a person who grants `ads_management` gives the session write access.
- No read-only mode, draft mode or undo is documented.
- Writes, by risk (partial list):
  - Spend: `ads_activate_entity` moves a paused campaign, ad set or ad to active and starts spending. `ads_update_entity` updates fields on an existing campaign, ad set or ad. The docs do not exclude status, budget or bid, so treat it as able to activate an entity or raise spend. `ads_boost_ig_post` turns an organic post into an ad.
  - Experiments, hard-to-reverse because they split or hold out live audiences: `ads_experiment_abtest_create_test`, `ads_experiment_abtest_update_test` (edit or cancel an A/B test), `ads_experiment_lift_create_test`. No update or cancel tool is listed for lift studies.
  - Creates and updates, reversible: `ads_create_campaign`, `ads_create_ad_set`, `ads_create_ad` (created paused; Meta says the AI client asks for confirmation before activation, which is client behaviour, not your approval), `ads_create_creative`, `ads_create_custom_audience`, `ads_update_custom_audience`, `ads_catalog_create`, `ads_catalog_update_catalog`, `ads_catalog_product_create`, `ads_catalog_update_product` (price and availability feed live catalog ads), `ads_catalog_create_product_set`, `ads_catalog_update_product_set`, `ads_catalog_create_product_feed`, `ads_catalog_update_product_feed`, `ads_catalog_create_feed_rule`.
  - Personal data: `ads_update_custom_audience_users` adds or removes users by uploading hashed personal data. Needs the privacy owner's sign-off.
  - Deletes: `ads_delete_custom_audience`, `ads_pixel_event_delete`, `ads_pixel_parameter_delete`, `ads_catalog_delete_product`, `ads_catalog_product_set_delete`, `ads_catalog_product_feed_delete` (also removes products that feed ingested, asynchronously), `ads_catalog_product_feed_delete_rule`.
  - Measurement: `ads_pixel_event_create` (starts inactive), `ads_pixel_event_update` (activates or deactivates a rule), parameter create and update, `ads_catalog_event_source_connect` and `ads_catalog_event_source_disconnect`.
- Money units: the Marketing API sets bid and budget amounts "at ad account currencies minimum denomination level, such as cents for USD" (its USD 20 example is `daily_budget=2000`). The MCP pages do not state units for `ads_update_entity`. Before proposing, read the field type with `ads_get_field_context`, compare a read-back with Ads Manager, and record the raw value and its unit.
- Budget level: check whether the budget sits on the campaign or on the ad set before proposing. Read both levels and edit the one that holds it.
- Learning phase: the Marketing API ad set reference exposes `learning_stage_info`, whether delivery is still learning. Meta's Business Help Centre page "About the learning phase" (find it by title) says significant edits, which can include budget or bid changes depending on their size and changes to targeting, creative or the optimisation event, can send an ad set back into learning. Quote its current wording at run time and list it as a side effect.
- Untrusted text the tools return: competitor ad copy from `ads_library_search`, creative text from `ads_get_creatives`, Instagram captions from `ads_get_ig_media`. No documented tool returns ad comments.
- Enforcing layer: the token's permissions plus the person's ad account access. For the read pass, use a user token or an Employee-role system user token with `ads_mcp_management` and `ads_read` only, without `ads_management`. Meta does not document which tools such a token rejects, so in a test ad account call one harmless write (for example renaming a paused test ad set) and record the denial before relying on it. Use a separate token with `ads_management` only in the write session.
- Read back: `ads_get_ad_entities` for current values, `ads_account_get_activity_logs` for change history (filter by object, time window, category or user).
- Revert: no undo. Re-apply before values, including status, from the change log. Deleted audiences, rules and feeds must be rebuilt from their source. Delivery already split or held out by an experiment cannot be undone.

## Google Ads (official Google Ads MCP)

Source: developers.google.com, Google Ads MCP server page (updated 2026-10-16), budget and mutate reference pages. Re-checked 2026-10-20.

- `googleads/google-ads-mcp` is "strictly read-only". Tools: `list_accessible_customers`, `search` (GAQL), `get_resource_metadata`.
- Runs locally (stdio, usually `pipx run`) or self-hosted on Cloud Run. A hosted endpoint is a pilot for allowlisted participants. Developer tokens were sunset on 2026-09-09.
- Writes do not happen through it. They go through Google Ads Editor imports, the UI, scripts or another API client. Mutate code generated by Google's Developer Assistant is a draft, and goes through this procedure before it runs.
- Validate without writing: most Google Ads API mutate requests accept `validate_only`, which validates the request without executing it. Batch jobs do not support it.
- Money units: budget amounts are `amount_micros`; one million micros is one unit of the account currency.
- Shared budgets: a campaign budget created through the API is shareable by default (`explicitly_shared` defaults to true). A shared budget or a portfolio bid strategy spreads one change across several campaigns. List every campaign it covers in the row.
- Third-party Google Ads MCP servers that can write exist. Treat any of them as unreviewed until a server review passes.
- Vendor skills: `google/skills` includes `google-ads-api-mcp-setup`, `google-ads-api-account-diagnostics` and `google-ads-api-quickstart`.
- Read back: GAQL on `change_event`. It returns old and new values for updates, the client type, and the user where Change History shows it. The query must filter `change_event.change_date_time` within the past 30 days and include `LIMIT`; at most 10,000 rows. Changes can take up to three minutes to appear, and some changes in Change History may be missing.
- Revert: re-apply before values, usually as an Editor import built from the before snapshot.

## Google Tag Manager (Stape GTM MCP)

Sources: Google Cloud MCP supported products page (updated 2026-10-16), `stape-io/google-tag-manager-mcp-server` README and source (Apache-2.0, last pushed 2026-10-16), Tag Manager API reference, Tag Manager help on user permissions, versions and workspaces. Re-checked 2026-10-20.

- Google lists no Tag Manager MCP server. Stape's open-source server is the common choice: hosted at `https://gtm-mcp.stape.io/mcp` with Google OAuth, or a local CLI using a service account key or refresh token.
- It requests every Tag Manager API scope: `tagmanager.manage.accounts`, `tagmanager.edit.containers`, `tagmanager.delete.containers`, `tagmanager.edit.containerversions`, `tagmanager.manage.users`, `tagmanager.publish`, `tagmanager.readonly` (from `packages/core/src/constants/scopes.ts`).
- Tools are multiplexed by an `action` argument, so a client allowlist cannot allow reads and block writes on the same tool:
  - `gtm_tag`: create, get, list, update, remove, revert.
  - `gtm_version`: get, live, publish, remove, setLatest, undelete, update.
  - `gtm_workspace`: create, get, list, update, remove, createVersion, getStatus, sync, quickPreview, resolveConflict, bulkUpdate.
  - `gtm_container`: includes remove, combine and moveTagId.
  - `gtm_user_permission`: create, get, list, update, remove.
- `update` replaces the whole entity. The tool text says "any field omitted here is deleted" and tells the caller to `get` first and send the complete object. `bulkUpdate` entities are also full replacements.
- `publish` and `update` on versions require the entity `fingerprint` (optimistic concurrency). Version `remove` has no confirmation parameter.
- The README documents no read-only mode, dry run or confirmation step.
- `quickPreview` calls the API's `workspaces.quick_preview`, which creates a fake container version from the workspace and returns only `containerVersion`, `syncStatus` and `compilerError`. It is a compile and sync check. It does not fire tags or show parameter values. It needs the `tagmanager.edit.containerversions` scope; whether an Edit-only user can call it is not documented, so test it in the inventory.
- Firing and values: a person confirms them in GTM Preview mode or Tag Assistant on a test transaction and records name, role, time and what they saw.
- Shared workspaces: `createVersion` builds a version "from the entities present in the workspace", so versioning a shared workspace publishes colleagues' unfinished edits. Create one workspace per proposal with `gtm_workspace` `create`. Before any version is created, `getStatus` must list only the approved entities; stop if it lists anything else. Standard containers have a small workspace limit; check the current number in Tag Manager help before creating one.
- Enforcing layer: the Google account's container permission in GTM.
  - Read: see the container, no changes.
  - Edit: create workspaces and edits, no versions, no publish.
  - Approve: create versions, no publish.
  - Publish: everything including publish.
  - Account Administrator adds creating containers and changing user permissions.
  - Use Read for audits and Edit for drafting in a workspace. Keep Publish and Administrator with a person.
- Read back: `get` the entity and compare every field with the before snapshot; `getStatus` and `quickPreview` on the workspace; `gtm_version` `live` after a publish.
- Revert before publish: `gtm_tag` `revert` reverts a tag's changes in the workspace (API `workspaces.tags.revert`). Pass the current `fingerprint`; Stape's handler does not check that one was given.
- Revert after publish: publish the previous version (Versions, select the version, Actions, Publish), or use Set as Latest Version to restore it as the draft first. Record the live version ID before any publish.

## HubSpot (remote MCP server)

Sources: HubSpot developer page on integrating with the remote MCP server, the GA changelog, the user permissions guide, the ChatGPT connector page, the Agent CLI guide. Re-checked 2026-10-20.

- Server: `https://mcp.hubspot.com`, OAuth. GA announced 2026-04-13.
- Read tools include `search_crm_objects`, `get_crm_objects` (up to 100 per request), `query_crm_data`, `search_properties`, `get_properties`, `search_owners`.
- Write tools include `manage_crm_objects` (create or update records and activities), `manage_segment`, `manage_custom_properties`, `manage_custom_pipelines`, `manage_campaign_objects`, `submit_feedback`. Content tools such as `manage_landing_page`, `manage_website_page`, `manage_blog_post` and `manage_marketing_email` both read and write.
- Writes listed on 2026-10-20:
  - CRM records: contacts, companies, deals, tickets, leads, line items, products, appointments, courses, listings, projects, services, custom object records.
  - Activities: calls, emails, meetings, notes, tasks.
  - Segments: "Create static and dynamic segments"; add or remove records in static segments. Pipelines and custom properties. Quotes (beta).
  - Campaigns: create, update, manage asset associations.
  - Content: create, edit and publish landing pages, website pages and blog posts, and restore a page revision.
  - Marketing emails: create and update drafts. No send is documented.
- Confirmation wording: `manage_crm_objects` "requires explicit user confirmation before executing any create or update", `manage_segment` before each write, and the page tools before creating from a template, publishing or restoring a revision. This is advisory and client-side. It is not your approval.
- Drift: the April 2026 GA changelog listed campaigns, landing pages, blog posts and site pages as read-only. The developer page now lists them as writable. Expect further growth.
- Deletes and merges: none documented. Read-only revenue objects (carts, invoices, orders, subscriptions) are in beta.
- Scopes: the page says you do not define the connector's scopes yourself; they follow the tools at install and what the user grants. There is no read-only switch.
- Enforcing layer: HubSpot user permissions ("All actions respect your existing HubSpot user permissions"). It is not a read-only boundary on its own:
  - Recommended read-pass user: View set; Edit, Delete, Communicate and Merge set to None; Create off; Bulk delete off.
  - HubSpot's permissions guide says users with View "can still create notes or tasks on records of that object". So that user can still create notes and tasks through `manage_crm_objects`.
  - During the read pass, block every `manage_*` tool and `submit_feedback` at the client and allow only the search, get and query tools. In the sandbox test, try a note create and expect the client to block it.
- With Sensitive Data turned on, activity and conversation data is blocked from the MCP server.
- Validation: the remote MCP page says the server "respects conditional field logic, including custom pipeline stage validations". The ChatGPT connector page says custom validation rules, including pipeline stage and association label validations, are not applied through that connector, which also caps bulk writes at 10 records. Read the stage requirements and validation rules for every object you touch and enforce them in the proposal, whichever route writes.
- Side effects: a property update (lifecycle stage, lead status, owner) can enrol records in workflows. List active workflows that use the field before approval.
- Bulk changes: HubSpot's Agent CLI (beta) and the `hubspot/agent-cli-skills` skills (bulk-operations, crm-data-quality, workflow-automation and others). Every write command supports `--dry-run`. Destructive commands such as `workflows update`, `workflows delete`, `pipelines delete`, `properties batch-archive` and `associations batch-delete` require the dry-run safety digest. `history` shows a local log of destructive operations. No undo is documented.
- Read back: record reads; property history in the record UI.
- Revert: re-apply before values. For pages, the page tools can restore a revision (with confirmation); check the editor's unpublish option at run time.

## Salesforce (hosted MCP servers)

Verification note: developer.salesforce.com returned HTTP 403 to automated fetches on 2026-10-17 and 2026-10-20. These points come from Salesforce's own pages as shown in search results restricted to that domain. Open the pages in a browser before relying on them.

- Standard servers are activated per org by an admin in Setup (MCP Servers under API Catalog). Activation is org-wide, not per change. Each server has its own endpoint, with a separate sandbox endpoint:
  - `sobject-reads`: schema, query, search, relationships. Cannot create, update or delete.
  - `sobject-mutations`: create and update, no delete.
  - `sobject-deletes`: delete plus discovery and query, no create or update. Salesforce calls deletion the highest-risk mutation and asks for human review before delete tools wherever possible.
  - `sobject-all`: the full set.
- Configure two client connections: the `sobject-reads` endpoint for the pass and the `sobject-mutations` endpoint for the write session. Leave `sobject-deletes` and `sobject-all` unconnected unless a delete row is approved.
- The create tool takes one object name and one body of field values, so a batch of N creates is N calls. Verify this live.
- Auth: an External Client App with OAuth and the `mcp_api` scope; sources differ on the companion refresh scope. Calls run as the signed-in user, so object permissions, field-level security and sharing apply. An agent cannot delete a record the user cannot delete manually.
- Writes fail when they break a validation rule. Record-triggered flows, Apex triggers and assignment rules on the object are side effects to list; check in Setup which are active on each object you touch.
- No undelete tool is available through MCP. Deleted records go to the Recycle Bin and can be recovered in the UI; the SObject All page gives a window of up to 15 days.
- Enforcing layer: which endpoint the client connects to, plus the user's profile and permission sets.
- Read back: query the records; field history tracking where enabled.

## Braze (remote MCP server)

Sources: Braze MCP server docs, the Available API functions page, the Operator Connect page and the Manage OAuth settings page. Re-checked 2026-10-20.

- Endpoints: `https://mcp.braze.com/mcp` (US), `https://mcp.braze.eu/mcp` (EU). OAuth with the Braze dashboard login. The locally hosted beta server is deprecated.
- Agents inherit the dashboard user's permissions. Permission changes apply on the next request; deactivated users lose access.
- Tools return no data from user profiles and no user-level PII.
- Beta gates: tools marked (C) need the campaign APIs beta and (S) the segment APIs beta. Without the beta they return an error. Check which betas the account has.
- Writes, by risk:
  - Send: `launch_campaign` (C). Hard-to-reverse, with its own approval line. Prefer a person launching in the dashboard.
  - Containment: `stop_campaign` (C) stops a running campaign. The page does not say what happens to queued messages; treat it as stopping future sends only.
  - Campaign edits: `create_campaign`, `edit_campaign`, `archive_campaign`, `unarchive_campaign`, `create_campaign_message`, `update_campaign_message`, `create_campaign_message_variation`, `update_campaign_message_variation`, `update_campaign_distribution` (all C), and `duplicate_campaign` (no beta). An edit to a live campaign changes what its next send delivers.
  - Live audience: `create_segment`, `edit_segment` (S). Editing a segment that live campaigns or Canvases use changes who they reach.
  - Deletes, irreversible through the MCP: `delete_campaign_message`, `delete_campaign_message_variation` (C), `delete_catalog`, `delete_catalog_field`, `delete_catalog_items` (up to 50 per request), `delete_catalog_selection`.
  - Full replacement: `replace_catalog_items` replaces items and creates any that do not exist, up to 50 per request. Send complete items.
  - Catalog writes: `create_catalog`, `create_catalog_fields`, `create_catalog_items`, `edit_catalog_items` (up to 50 per request), `create_catalog_selection`.
  - Data movement: `trigger_integration_sync` starts a Cloud Data Ingestion sync.
  - Content: `create_email_template`, `update_email_template`, `create_content_block`, `update_content_block`, `create_media_library_asset`. A content block edit updates every message that inserts it through Liquid. Before editing one, read where it is used with `get_content_block_info` (it can return campaign and Canvas inclusion data) and list those as side effects.
- Canvas tools are read-only (`get_canvas_list`, `get_canvas_details`, `get_canvas_data_series`, `get_canvas_data_summary`).
- Operator Connect (beta, select customers): `send_operator_prompt`, `get_operator_result`, `cancel_operator_job`. It builds and edits campaigns, Canvases, segments, copy, templates and Content Blocks. Operator Connect cannot launch campaigns or Canvases; that limit applies to Operator Connect only, since `launch_campaign` is its own tool. It does not pause for approval at each step; requested actions run immediately.
- Edit history: the Operator Connect page says edits made with other MCP tools, such as `update_campaign_message_variation`, do not appear in campaign edit history. The main MCP page says campaign edits made through MCP tools are attributed to the signed-in user and appear in edit history. The pages disagree, so keep your own change log.
- No read-only toggle is described. Braze advises against auto-mode in MCP clients and recommends reviewing AI-proposed actions first.
- Accounts using IP allowlisting cannot use the remote server.
- Enforcing layer: the dashboard user's permissions. For a read pass, use a user without edit rights on campaigns, segments, catalogs and templates (check current permission names in Braze at run time).
- Containment: Settings, Admin Settings, OAuth (company-level Admin only).
  - Turning off MCP OAuth access denies MCP OAuth for every user and workspace and revokes existing MCP OAuth tokens.
  - Revoke MCP OAuth tokens invalidates every MCP OAuth token in the company at once. It cannot be undone, requests already in progress are not recalled, and it can take a few minutes.
  - OAuth connections to the MCP server are recorded in the security event report.
- Docs: Braze publishes a docs MCP through Context7 (`braze-inc/braze-docs`).
- Revert: re-apply before content from the change log. Deleted catalogs and items are rebuilt from their source. Launched sends cannot be reversed.

## Klaviyo (remote and local MCP servers)

Sources: Klaviyo "Connect to the MCP server" and "Available tools" pages. Re-checked 2026-10-20.

- Remote server: `https://mcp.klaviyo.com/mcp` (no trailing slash), OAuth with dynamic client registration. Only Owner, Admin or Manager roles can use it, so a low-permission user is not an option there.
- Remote query parameters on the URL:
  - `read-only=true` disables tools that write.
  - `toolsets=profiles:read,campaigns:read` exposes only tools whose required scopes are covered.
  - `core-tools-only=true` limits to about 40 core tools. It defaults to true for ChatGPT and false elsewhere.
  - `disable-tools-with-user-generated-content=true` disables tools that read user-generated content.
  - `beta=true` enables new, potentially unstable tools. Keep it false.
- The page says query parameters cannot be controlled with the listed Claude connector or the listed ChatGPT app. With those, you cannot switch writes off. Use a custom connector with the parameters in the URL.
- Local server: `uvx klaviyo-mcp-server@latest` with a private API key and two settings: `READ_ONLY` (default false; true disables write tools) and `ALLOW_USER_GENERATED_CONTENT` (default true; false disables tools that read user-generated content). A private key created with read-only scopes is enforced by Klaviyo itself. For the read pass this is the strongest layer: local server, read-scoped key, `READ_ONLY=true`, `ALLOW_USER_GENERATED_CONTENT=false`.
- Hard-to-reverse writes include `send_campaign` (sends to the configured audience), `update_flow`, `delete_flow`, `bulk_suppress_profiles`, `bulk_unsuppress_profiles`, `subscribe_profile_to_marketing`, `unsubscribe_profile_from_marketing`, `merge_profiles`, `request_profile_deletion`, `bulk_import_profiles`, `bulk_delete_catalog_items`, `create_webhook`, `delete_sending_domain`.
- User-generated content: `get_events`, `get_event`, `get_profiles`, `get_profile`, `get_reviews`, `get_review` (reads), and `update_profile`, `create_or_update_profile`, `update_review` (writes). Treat what they return as untrusted.
- Revert: `cancel_campaign_send` ("Cancel or revert a campaign send") helps only before delivery. Sends, merges and deletion requests cannot be undone.

## Mixpanel (remote MCP server)

Source: Mixpanel MCP docs.

- Hosts: `https://mcp.mixpanel.com/mcp`, `https://mcp-eu.mixpanel.com/mcp`, `https://mcp-in.mixpanel.com/mcp`. OAuth, or service accounts (beta) that reach only projects they were added to.
- No read-only mode and no per-tool restriction is documented. Access follows project permissions and roles.
- Writes:
  - Lexicon: `Edit-Event` (description, display name, tags, visibility), `Edit-Property` (description, display name, hidden status, PII classification), `Bulk-Edit-Events` and `Bulk-Edit-Properties` (up to 50 each), `Create-Tag`, `Rename-Tag`, `Delete-Tag`.
  - Governance: `Dismiss-Issues`, `Update-Business-Context`, `Dismiss-Duplicate-Group`, `Merge-Group`.
  - Content: dashboards (create, update, duplicate, delete), cohorts (create, update, delete), custom properties, lookup tables, metrics.
  - Experiments and feature flags (open beta): create and update.
- Why it matters: hiding an event or property changes what every report shows; a PII classification change alters governance; a feature flag change alters the product.
- MCP is on by default for free and growth accounts created after 2026-08-01; others need an org admin to enable it in Settings, Org, Overview. Changes take up to 15 minutes.
- Rate limit: 600 MCP requests per hour per user. `Get-Audit-Log` is beta for selected customers and needs org admin or owner access.
- Enforcing layer: the project role of the user or service account. Use a role without write access for the read pass (check role names live).
- Vendor skills: `mixpanel/ai-plugins` includes `manage-lexicon`, `manage-feature-flags` and `manage-experiment` for tool-level work.
- Revert: re-apply before Lexicon values from the change log, at most 50 per bulk call.

## PostHog (remote MCP server)

Sources: PostHog MCP docs, FAQ and tools reference. Re-checked 2026-10-20.

- Server: `https://mcp.posthog.com/mcp`; US or EU is chosen by the account you log in with.
- Read-only: `?readonly=true` (or `1`), or header `x-posthog-read-only: true`. The header wins if both are set. Tools that create, update or delete are removed from the tool list.
- Filtering: `?features=flags,dashboards,insights` and `?tools=dashboard-get,feature-flag-get-all`. When both are set, a tool is exposed if it matches either.
- A personal API key made with the MCP Server preset is scoped to one project.
- Enforcing layer: the read-only header or parameter, plus a personal API key with the MCP Server preset scoped to one project.
- Live product effect: `feature-flag-enable`, `feature-flag-disable`, `feature-flag-roll-out-to-everyone`, `update-feature-flag`, `experiment-launch`, `experiment-ship-variant`, `experiment-end`, `survey-launch`, `cdp-functions-publish`.
- Hard-to-reverse (partial list):
  - Person and data deletes, irreversible: `persons-bulk-delete`, `persons-property-delete`, `session-recording-bulk-delete`, `session-recording-delete`, `external-data-schemas-delete-data` ("Delete synced table data"), `external-data-sources-destroy`, `feature-flags-bulk-delete-create`, `dashboards-bulk-delete`.
  - Consent: `opt-outs-remove` removes a recipient from the opt-out list; `opt-outs-add` adds recipients.
  - Sends: `broadcasts-create`, `announcements-create-execute` ("Send announcement (execute)"), `conversations-tickets-reply-create`, `conversations-tickets-compose-create`.
  - Live automation: `workflows-publish`, `workflows-enable`, `workflows-run-batch`, `workflows-schedule-create`.
- The tools page does not say whether `execute-sql` is read-only. Check live before exposing it in a write session.
- Rate limit: 480 requests per minute and 4,800 per hour per credential.
- Vendor skills: `PostHog/ai-plugin`.
- Revert: disable the flag, end the experiment, stop the survey or disable the workflow. Person deletes, delivered broadcasts and announcements, and consent changes already acted on cannot be reversed.

## Fivetran (fivetran-mcp)

Sources: `fivetran/fivetran-mcp` README (MIT, last pushed 2026-10-15), Fivetran REST API reference. Re-checked 2026-10-20.

- Self-hosted only: stdio by default, or self-hosted streamable HTTP (default host `127.0.0.1`; the README's example binds `0.0.0.0`, which exposes it on the network). No Fivetran-hosted endpoint. Auth is an API key and secret.
- `FIVETRAN_SCOPE`: `read` (default), `read/write`, or `read/write/delete`. Legacy `FIVETRAN_ALLOW_WRITES=true` equals `read/write`; `FIVETRAN_SCOPE` wins if both are set.
- `DISALLOWED_ACTIONS` removes operations from the chosen scope. The README says system-key and user-API-key operations "are reachable within whatever `FIVETRAN_SCOPE` you grant", including reads of API keys. Its recommended denylist: `system-keys:read`, `users:read:get_user_api_key`, `users:read:list_api_keys`, `users:write:create_user_api_key`, `users:write:rotate_user_api_key`, `users:delete:delete_user_api_keys`.
- Confirmations are advisory: the README says the server "does not enforce confirmation".
- OAuth in HTTP mode is scaffolding only and not yet functional.
- Enforcing layer: `FIVETRAN_SCOPE=read` plus the recommended denylist for passes, and an API key owned by a user with a limited Fivetran role.
- Operations to look up in the live REST API reference before approval, recording for each the downstream table effect and the cost the Fivetran admin expects:
  - Historical re-sync: `is_historical_sync` on Update a Connection starts one immediately; Re-sync Connection Data and the table re-sync endpoint do the same for a connection or tables.
  - Schema config: `schema_change_handling` (`ALLOW_ALL`, `ALLOW_COLUMNS`, `BLOCK_ALL`) and `enabled` per schema, table and column.
  - Schedule: `sync_frequency`, `schedule_type`, `paused`.
  - Delete a Connection. The reference does not say what happens to data already in the destination; ask the admin.
- Vendor skills: `fivetran/skills` covers analysis (for example ad-performance-analysis), not writes.

## Snowflake (managed MCP server)

Source: Snowflake docs, Cortex Agents MCP, Time Travel, transient tables, CREATE TABLE, CLONE and SHOW TABLES; dbt Snowflake configs. Re-checked 2026-10-20.

- The server is an object in a database and schema. Tool types: `CORTEX_AGENT_RUN`, `CORTEX_SEARCH_SERVICE_QUERY`, `CORTEX_ANALYST_MESSAGE`, `SYSTEM_EXECUTE_SQL`, and `GENERIC` (UDFs and stored procedures).
- The SQL tool is read-only by default: `read_only` under `config`, default `true`.
- "Access to the MCP Server does not give access to the tools." Each tool needs its own grant plus the underlying privilege.
- A `GENERIC` tool does whatever its procedure does. Read the procedure body and whether it runs with owner's or caller's rights before exposing it.
- Limits: 50 tools per server; tool responses truncated at 250 KB.
- Auth: Snowflake OAuth by default, External OAuth optional; a programmatic access token should be scoped to the least-privileged role.
- Enforcing layer: the role. Use a SELECT-only role for passes.
- Validate without writing: `EXPLAIN` compiles a statement and returns its plan without running it.
- Retention you can actually promise:
  - Read it per table: `SHOW TABLES` gives `kind` (TABLE, TEMPORARY or TRANSIENT) and `retention_time` in days.
  - Permanent tables: 1 day by default, up to 90 days on Enterprise, then Fail-safe.
  - Transient and temporary tables: 1 day at most and no Fail-safe. dbt's Snowflake docs say every table dbt builds is transient by default, so most marketing models never have a long window.
  - `CREATE OR REPLACE TABLE` is equivalent to dropping the old table. To recover it, rename the new table, then `UNDROP` the old one within its retention.
- Before any DML or DDL row: take a zero-copy `CLONE` and name it in the row's rollback field.
- Revert: Time Travel (`AT` or `BEFORE`), `CREATE TABLE ... CLONE ... AT(...)`, `UNDROP`, or swap back from the pre-change clone.
- Vendor tooling: Snowflake's Cortex Code CLI ships bundled skills for platform work.

## BigQuery (Google-managed remote MCP)

Sources: BigQuery "Use the BigQuery MCP server" (updated 2026-10-13), Google Cloud "Prevent read-write MCP tool use" (updated 2026-10-13), BigQuery time travel and table snapshot pages. Re-checked 2026-10-20.

- Endpoint: `https://bigquery.googleapis.com/mcp`. Tools: `list_dataset_ids`, `list_table_ids`, `get_dataset_info`, `get_table_info`, `execute_sql`, `execute_sql_readonly`.
- Only `execute_sql` is not read-only. `execute_sql_readonly` rejects DML, DDL and Python UDFs.
- Roles: MCP Tool User, BigQuery Job User, BigQuery Data Viewer. Google recommends a separate identity for agents.
- Enforcing layer: an IAM deny policy on `mcp.googleapis.com/tools.call` with the condition `api.getAttribute('mcp.googleapis.com/tool.isReadOnly', false) == false`, created with `gcloud iam policies create ... --kind=denypolicies`. It does not cover non-Google Cloud MCP servers.
- Under the deny policy, `tools/list` still returns read-write tools; only calling them fails. Prove the layer with a denied call, not the tool list.
- Results are capped at 3,000 rows and queries cancelled after three minutes by default. A short answer can hide a truncated result.
- Validate without writing: a query dry run validates the query and estimates bytes processed without charge.
- Retention: time travel is set at the dataset or project level, from 2 to 7 days (7 by default). Read the actual setting before promising a window. After it, 7 days of fail-safe, recoverable only through Cloud Customer Care.
- Before any DML or DDL row: take a table snapshot (it can capture any point in the past seven days) or a table copy, and name it in the row's rollback field.
- Vendor skills: `google/skills` includes BigQuery basics, optimization and troubleshooting.

## Servers not covered here

Customer.io, Amplitude, Intercom, Attio and others also expose writes. Apply the same inventory and classification, and look up their read-only switches live. Do not assume a mode exists because a similar vendor has one.
