---
name: marketing-mcp-setup
description: Chooses, connects and scopes marketing MCP servers, read-only first, in Claude Code, Claude Desktop, ChatGPT, Codex, Cursor and VS Code. Picks the route (official, export, bridge or reviewed community server), client placement, narrowest identity and scopes and strongest read-only layer per tool, proves each connection with six tests and hands back a stack inventory. Covers HubSpot, Salesforce, GA4, Mixpanel, Amplitude, PostHog, Google Ads, Meta Ads, GTM, Braze, Klaviyo, Intercom, Customer.io, Search Console, BigQuery and Snowflake. Use when asked to set up, install, connect or configure an MCP server or connector, connect HubSpot, GA4, Google Ads, Meta, Klaviyo or Snowflake to an AI assistant, make an MCP connection read-only, choose between official and community servers, fix an MCP that shows the wrong account, region or no tools, audit which MCP servers are connected or revoke access when someone leaves, or when the Google Ads MCP asks for a developer token or an analytics answer disagrees with the UI.
license: CC-BY-4.0
compatibility: Any MCP-capable client. The three helpers in scripts/ (mcp_inventory.py, mcp_tools_diff.py, mcp_inventory_check.py) need Python 3.9 or later and no packages; mcp_inventory.py reads TOML configs with tomllib on 3.11 or later and a built-in reader on older versions.
metadata:
  tier: free
  category: martech-ai-mcp
  version: "0.2.0"
  last_reviewed: "2026-11-06"
  status: reviewed
---

# Marketing MCP setup

Connects an AI client to the marketing stack one MCP server at a time, read-only first, proves each connection with six tests and ends in a stack inventory a colleague can audit. It never writes to a production vendor system or handles credentials; its only write attempts are the harmless T4 tests in a sandbox the user names.

## When to use

Use this skill when:

- Someone wants a CRM, analytics, ads, lifecycle, SEO or warehouse tool connected to an AI client over MCP, or is choosing between a vendor server, a community server, a bridge or an export.
- A connection must become read-only, nobody knows what it can change, it shows the wrong account, region, no projects or no tools, or a vendor update, scope reinstall or new client changed what it exposes.
- Someone asks which MCP servers are configured on a machine, in a repository or in a team workspace, or an employee or agency leaves.

Use something else when:

- The agent will change anything through the connection (pause, edit, send, publish, merge, delete): agent-write-safety.
- Judging a server's code and publisher: mcp-security-review if installed; until then the publisher review in [references/choosing-servers.md](references/choosing-servers.md) is the gate.
- Auditing a GTM container: gtm-container-audit. Building an MCP server over warehouse models: warehouse-and-internal-mcp if installed.
- Creating `martech-context/` from scratch: marketing-context-kit. This skill only proposes the martech-context updates listed in step 9.

## Inputs

Read these first if they exist ([assets/example-acme-stack.md](assets/example-acme-stack.md) shows the fields): `martech-context/stack.md` (tools, account IDs, regions, owners, current Agent access lines), `decisions.md` (clients approved for customer data, standing restrictions) and `metrics.md` (definitions behind each known answer).

Then gather:

- One to five jobs, each with its system of record and one known answer readable in the vendor UI (a closed-period delivery value or one record).
- Who uses which client, on which plan or workspace, whether an org admin controls connectors, and the admin per tool who can enable MCP, change roles or create OAuth apps.
- A sandbox or test account per tool for the denied-write test, or "none".

Never ask for, accept or repeat tokens, API keys, client secrets or service account files; if one is pasted, tell the user its owner should rotate it.

## Procedure

### 1. Fix the jobs before the tools

- No job, no connection. Each job names the question, the system of record and a known answer.
- First pilot: at most three servers, each with every write tool absent, Blocked or denied at the source by step 6 before first use; a credential recorded as write-capable is fine when no write tool is reachable.
- Each person connects and tests one server at a time and starts the next only after T1 to T4 pass. Connections found in step 2 are remediation: keep, fix or remove them one server per person at a time.

### 2. Inventory what is already connected

```bash
python3 -I scripts/mcp_inventory.py --discover --project ~/work/acme-marketing --expect-region mixpanel=eu
```

- Run from the skill folder. It reads each client's config and settings files and any paths you pass, matches servers against [assets/server-registry.json](assets/server-registry.json) and prints a redacted inventory with findings S01 to S15 ([references/failure-modes.md](references/failure-modes.md)); exit 1 means a FAIL, exit 2 an unreadable file. Always pass `--project <repository>`: the default is the current directory. `--all-projects` adds every repository listed in the terminal agent's user config.
- Never open or paste client config files into the conversation; they can hold tokens. Without Python, record "scanner not run, because ...".
- Chat-app connectors, ChatGPT plugins, plugin-provided servers and desktop extensions are not in config files: list them from the terminal agent's `/mcp` and each app's settings.

### 3. Choose the route per tool

Take the first rung that covers the job:

1. Vendor-hosted remote server.
2. Vendor-published local server.
3. Export or warehouse path: GTM container export, Search Console bulk export to BigQuery, GA4 BigQuery export.
4. A reporting bridge the team already licenses, with its write tools off.
5. A community server that passes the publisher review: not archived, a release or commit in the last 90 days, an OSI licence, a pinned install, a read-only option, documented token storage.

- Never connect production credentials to an unreviewed community server, or to a third-party host using the name of a vendor that publishes its own server.
- With mcp-security-review installed, run it before step 7 for rungs 4 and 5 and any failed publisher review; NO-GO stops the connection.
- Routes and traps per tool: [CRM](references/crm-servers.md), [analytics](references/analytics-servers.md), [ads](references/ads-servers.md), [lifecycle](references/lifecycle-servers.md), [SEO and warehouse](references/seo-and-warehouse-servers.md); retired servers: [references/choosing-servers.md](references/choosing-servers.md).

### 4. Place it in the right client

- Remote is required when any user works in a web, mobile or cloud-hosted client. Self-host a local server (Google documents Cloud Run for Google Ads and GA4) only when two or more such users need it and someone owns the deployment.
- Chat-app connectors call vendors from the provider's cloud, so IP allowlists (Braze) and network policies (Snowflake) block them.
- CRM, lifecycle and support servers go only into a client or workspace that `decisions.md` approves for customer data; otherwise draft the request for the privacy owner and stop for that tool.
- Personal connections stay in personal scope; a shared project file holds only servers read-only by an enforced layer, with secrets as environment references (non-interactive runs load project servers without a prompt).
- OAuth that accepts only registered web callbacks (Google Cloud remote MCP, some Salesforce setups): add the connector in the chat app; [references/clients.md](references/clients.md) says which terminal agent can inherit it and when.
- Agencies: one project folder or profile per client account. Name servers `<vendor>-<account>-<env>-<mode>`, for example `klaviyo-acme-prod-ro`.

### 5. Choose the identity and scopes

- Connect as the person who does the job, with the lowest role that passes T3; never an admin: add the one missing permission instead.
- Request read scopes where the vendor offers them (Customer.io `read`, Google OAuth scopes, a Search Console `--read-only` login). Where scopes are fixed (HubSpot, Google Ads `adwords`, BigQuery `bigquery`, Stape's hosted GTM endpoint), enforce through role, IAM or client blocks and record the credential as write-capable.
- Record granted scopes from the auth-status tool or consent screen, not the request. Unattended jobs get their own read-only identity with a named owner. Vendor detail, Google token expiry and custody: [references/identity-and-scopes.md](references/identity-and-scopes.md).

### 6. Enforce read-only below the prompt

Strongest layer per tool, checked 2026-11-06:

- HubSpot: View-only user with Create off, plus a client allowlist of read tools (View users can still create notes and tasks); with no allowlist, block `manage_*` and `submit_feedback`.
- Salesforce: the sobject-reads endpoint only.
- GA4: never `--allow-write` or `ANALYTICS_MCP_ALLOW_WRITE`; mint the credential with `analytics.readonly`.
- Mixpanel: a project role without write, plus client blocks.
- Amplitude: Viewer (or an Enterprise custom role with Use MCP (read) only) plus a client allowlist; Member, Manager and Admin write by default.
- PostHog: `readonly=true&project_id=<id>&mode=tools` on the URL.
- Google Ads: `labels: false` and `assets: false` in a tools config named by `GOOGLE_ADS_MCP_TOOLS_CONFIG` (the default lists four write tools); a dedicated or unattended identity gets Read-only account access, so its `adwords` token cannot edit that account; otherwise record the credential as write-capable.
- Meta Ads: `ads_mcp_management` plus `ads_read`, never `ads_management`; for a dedicated identity, also a reporting-only ad account role.
- GTM: the export JSON, or Read permission with `tagmanager.readonly` (gtm-container-audit).
- Braze: a dashboard user without edit rights, plus Use MCP Server.
- Klaviyo: `read-only=true&disable-tools-with-user-generated-content=true` on a custom connector (directory connectors ignore it), or the local server with a read-scoped key and `READ_ONLY=true`.
- Intercom: block `add_internal_note`, `create_article`, `update_article` and the Fin Operator tools.
- Customer.io: the default read scope only; live-data and sensitive-data toggles off.
- Search Console: the bulk export to BigQuery; a community server only with a `--read-only` login and `GSC_ENABLE_WRITES=0`.
- BigQuery: an IAM deny on tools not marked read-only (`execute_sql`, `cancel_job`); Data Viewer on approved datasets only.
- Snowflake: the `read_only` SQL tool and a SELECT-only `MCP_READER` set as the user's default role, alone in `ALLOWED_ROLES_LIST`.

Then in the client:

- Prefer controls that deny new tools by default (a read-tool allowlist, a glob deny on write verbs, the setting for tools added later, a Blocked write-and-delete category): a list of today's write tools misses tomorrow's.
- Write tools are Needs approval or Blocked, never always allowed; never remember an approval for a write tool.
- Mark each tool U (reads outsider text: CRM notes, reviews, conversations, ad copy), P (private data) or S (sends data out); if one profile holds all three, split the job into two sessions with a human hand-off.
- If only the client layer exists, tell the user the read-only pass depends on the agent not calling write tools, and record the date.

### 7. Connect

- Prepare the change from [assets/read-only-snippets.md](assets/read-only-snippets.md); apply it only after the user approves, through the client's own command or settings screen where one exists (the terminal agents' `mcp add` and `mcp remove`, `/permissions`, connector settings). For a file with no command (Cursor, desktop app or VS Code JSON, Codex keys such as `enabled_tools`), give the user the block and file path to paste; never read the file into the conversation. Re-run the scanner on every changed file.
- Admin changes (org MCP switches, roles, OAuth apps, IAM deny policies, Snowflake users and integrations) go to the admin as a draft.
- The user completes every sign-in and consent screen, after checking the account and company shown.

### 8. Test every server in every client

Run T1 to T6 per server and client and log each in [assets/connection-test-log.csv](assets/connection-test-log.csv); per-vendor calls, caps and evidence sources: [references/connection-tests.md](references/connection-tests.md).

- T1 identity: the identity call returns the account ID in `stack.md`; a mismatch is FAIL. More accounts than the job needs is WARN; another client's production account is FAIL.
- T2 tool surface: the tool list matches the expected read-only surface; save it as the drift baseline. A list of only an exec or discovery tool hides the tools: switch to tools mode first.
- T3 known answer: a delivery metric (spend, impressions, clicks, sends, sessions) or a record field for a closed period ending at least three full days ago, in the account's time zone and currency, matching exactly; an analytics difference passes only when traced to a named definition difference. Never use conversions or attributed revenue.
- T4 denied operation: an out-of-scope read fails without substitution, and a harmless write in the named sandbox is denied or the tool is absent. Get the user's go-ahead for the exact call, record and sandbox; match production's role and block rules, or record the layer as not proved. A write that succeeds is FAIL: stop and hand the object's removal to the user. Never test with a delete, send, launch, publish or spend change; a refusal without a tool call proves nothing. No sandbox: UNPROVEN, an open risk.
- T5 completeness: a request larger than one page states pagination or truncation.
- T6 revocation: who revokes, how, and one disconnect and reconnect during the pilot.
- For a client you are not running in, give the user a numbered test script (exact prompt, expected value, what to copy back) and log their answers with `tester_role`; a test they cannot run is UNPROVEN.

A server counts as connected only when T1 is PASS (or WARN with a dated narrowing request), T2 and T3 are PASS, and T4 is PASS or UNPROVEN with a reason. Results expire after 30 days.

### 9. Hand over the inventory

```bash
python3 -I scripts/mcp_inventory_check.py ~/scratch/mcp-inventory.md
```

- Fill [assets/stack-inventory-template.md](assets/stack-inventory-template.md) in a scratch file outside `martech-context/`, one block per server, environment and client, and run the check (omit `--as-of` on live runs). Exit 1 lists missing labels, placeholders and stale or failed tests.
- Propose: the inventory as `martech-context/mcp-inventory.md`, one Agent access line per tool in `stack.md`, a `decisions.md` entry per approved connection, tool snapshots in `martech-context/mcp-tools/` and a README line under Optional files present naming both. Write nothing until the owner approves.

### 10. Keep it current

```bash
python3 -I scripts/mcp_tools_diff.py current.json --baseline baseline.json --mode ro
```

- Build `current.json` like [assets/example-acme-tools-current.json](assets/example-acme-tools-current.json): tool names only, never results. Mark tools the client blocks `"blocked": true` and tools a source layer denied in T4 `"denied_by"` (`role`, `iam`, `grant`, `scope` or `server`).
- Re-test on any trigger in references/connection-tests.md. Offboarding someone triggers every T6 revoke step for their identities.

## Common failure modes

- Write tools on by default or a read-only setting that does nothing (the HubSpot connector, a Meta grant with `ads_management`, Amplitude Member and above, Klaviyo parameters on a directory connector, Google Ads on its bundled config). Detect: T2.
- Tools hidden behind one exec or discovery tool (PostHog CLI mode, Amplitude progressive discovery). Detect: the T2 list.
- Wrong account or region; lookalike, retired or shadowed servers; truncated results read as totals. Detect: T1; scanner S01, S02, S07, S10; T5.

Full list with fixes: [references/failure-modes.md](references/failure-modes.md).

## Worked example

Acme Co is fictional: its [stack.md](assets/example-acme-stack.md) and six configs from three laptops in [assets/example-acme-client-configs.json](assets/example-acme-client-configs.json).

```bash
python3 -I scripts/mcp_inventory.py --fixture assets/example-acme-client-configs.json \
  --expect-region mixpanel=eu --as-of 2026-11-01
```

It ends `Summary: 12 FAIL, 9 WARN, 3 INFO, 8 PASS`: a HubSpot lookalike bridge (S01), a shared Klaviyo entry without `read-only=true` shadowed for one person (S06, S10, S11). The fixed configs scan `0 FAIL, 0 WARN, 2 INFO, 6 PASS`, and [assets/example-acme-inventory.md](assets/example-acme-inventory.md) passes the check. Decisions, tests, drift: [references/worked-example.md](references/worked-example.md).

## Acceptance checks

- Jobs: every connected server maps to a job with a recorded known answer. Pass: no server without a job.
- Existing configs: the scanner ran, or the inventory says why not; every finding has keep, fix or remove. Pass: no unresolved FAIL.
- Route: each server records its route and reason; community servers carry a dated publisher review. Pass: no unreviewed community server, lookalike host or retired endpoint.
- Identity: role, reachable accounts and granted scopes come from the auth-status tool or consent screen. Pass: no unexplained admin identity; no other client's production account reachable.
- Read-only: each server names its enforcing layer, the exact setting and the proof (tool list compared, or a denied test). Pass: nothing relies on prompt text; client-only layers are labelled, with the date the user was told.
- Tests: every server and client pair meets the T1 to T4 rule, T5 is PASS or its limits are recorded, T6 is documented, all within 30 days. Pass: `scripts/mcp_inventory_check.py` exits 0.
- Secrets: no token, key or secret in chat, config, inventory or a committed file. Pass: S03, S04 and S12 clean on the final configs.
- Client approvals: write tools are Blocked or Needs approval, new tools denied by default where the client allows, and CRM, lifecycle and support servers sit only in clients approved in `decisions.md`. Pass: no write tool always allowed or allowlisted; each of those servers cites its approval date.
- Drift baseline: a tool snapshot per server and client is proposed for `martech-context/mcp-tools/`. Pass: `scripts/mcp_tools_diff.py <snapshot> --mode ro` exits 0.
- Safety: no write, send, publish, delete or spend call ran outside the T4 sandbox tests, and the agent changed no admin setting. Pass: the call log shows reads, the user's sign-ins and only T4 attempts in named sandboxes, each denied or absent, confirmed in the source's own records where they exist; `martech-context/` changes have a recorded approval.

## Boundaries

- Read-only, apart from the T4 denied-write tests in a named sandbox; never a delete, send, launch, publish or spend change. Roles, org connector settings, OAuth apps, IAM and warehouse grants leave as drafts for the admin.
- Never asks for, prints or stores credentials.
- A publisher review is a gate, not a security audit. AI provider data terms and customer-data approval belong to the privacy or security owner.
- Hand-offs: writes to agent-write-safety; server trust to mcp-security-review when installed; GTM reads to gtm-container-audit; context files to marketing-context-kit; custom servers to warehouse-and-internal-mcp when installed.
- Official read-only modes per tool: step 6.
- Vendor setup skills: Mixpanel's install skill (`mixpanel/ai-plugins`), dbt's configuring-dbt-mcp-server (`dbt-labs/dbt-agent-skills`), the PostHog wizard, Customer.io plugins, HubSpot's connectors. Skip the developer token in Google's google-ads-api-mcp-setup (sunset 2026-09-09).

## Live docs

Platform settings, limits and menu paths change. Before stating any parameter name, limit, quota or UI path:

- Fetch the current page from the vendor docs MCP server or llms.txt listed in `sources.yaml`, or open the canonical URL.
- If the live page disagrees with this skill, follow the live page and tell the user what changed.
- Cite the URL you used.

Most used: the HubSpot, Klaviyo, Amplitude, PostHog and Meta Ads MCP pages, the Google Ads and GA4 repositories, the terminal agent's MCP and permissions pages, Google Cloud's MCP deny page and Salesforce hosted servers. Checked 2026-11-01, changed servers and clients 2026-11-06. developer.salesforce.com and help.openai.com refuse automated fetches; some help.salesforce.com pages return an error shell, so read those in a browser.
