# ============================================================ # Samarth GTM MCP Server — Environment Configuration # Copy to .env and fill in. NEVER commit .env or *.gtm-mcp-tokens.json. # Every var below is optional except the OAuth client; defaults are shown. # ============================================================ # ── Google OAuth client (required) ─────────────────────────── # Create at https://console.cloud.google.com/apis/credentials → "Desktop app". # Enable: Tag Manager API, Google Analytics Admin API, Google Analytics Data API. # Scopes requested: GTM + analytics.readonly (GA4 is read-only by default). GOOGLE_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com GOOGLE_OAUTH_CLIENT_SECRET=your-client-secret GOOGLE_OAUTH_REDIRECT_URI=http://localhost:3001/oauth/callback # Aliases for the same credential, in precedence order — set ONE set only: # SAMARTH_GOOGLE_OAUTH_CLIENT_ID/_SECRET hosted Samarth app; self-hosters leave unset # GOOGLE_CLIENT_ID/_SECRET/GOOGLE_REDIRECT_URI legacy names, still honored # ── Tokens ─────────────────────────────────────────────────── # Run `npm run auth:google` — it completes the flow and writes this file. GTM_MCP_TOKEN_FILE=.gtm-mcp-tokens.json # Override the token file with literal tokens (env wins over file): # GOOGLE_ACCESS_TOKEN= # GOOGLE_REFRESH_TOKEN= # Service account JSON key. GTM needs domain-wide delegation — see README. # GOOGLE_SERVICE_ACCOUNT_KEY_FILE= # ── Transport ──────────────────────────────────────────────── # stdio = Claude Desktop / Cursor / Claude Code. http = cloud/team server. GTM_MCP_TRANSPORT=stdio GTM_MCP_HTTP_PORT=3001 # Auth for /mcp. With neither this nor STYTCH_PROJECT_ID set, the HTTP transport # REFUSES TO START — unauthenticated /mcp would serve this server's Google # credentials to anyone who can reach the port. GTM_MCP_HTTP_AUTH_TOKEN= # Local dev escape hatch: start without auth, binding loopback only. # GTM_MCP_HTTP_ALLOW_UNAUTHENTICATED=false # Bind host. Default: loopback when unauthenticated, all interfaces when authed. # GTM_MCP_HTTP_HOST= # ── Guardrails (read-only by default; every write also needs confirm=true) ── GTM_MCP_ENABLE_WRITES=false GTM_MCP_ENABLE_PUBLISH=false GTM_MCP_ENABLE_DELETES=false # GA4 writes need the analytics.edit scope (+ analytics.manage.users for access # bindings) — re-run auth if you first authorized read-only. GA4_MCP_ENABLE_WRITES=false # GA4 deletes AND archives; archive is permanent for dimensions/metrics/audiences. GA4_MCP_ENABLE_DELETES=false # Simulate every write/delete/publish without calling the API. DRY_RUN=false # ── Multi-user mode (Stytch Connected Apps) ────────────────── # Setting STYTCH_PROJECT_ID switches the HTTP transport to multi-user: each /mcp # request carries a Stytch JWT resolved to that user's Google identity. # Leave unset for single-identity mode. See docs/adr/0001. # STYTCH_PROJECT_ID= # STYTCH_SECRET= # STYTCH_PUBLIC_TOKEN= publishable, powers /oauth/authorize # GTM_MCP_PUBLIC_URL= this server's public origin # Derived from STYTCH_PROJECT_ID when unset; pin issuer/audience in production: # STYTCH_API_BASE STYTCH_JWKS_URL STYTCH_TOKEN_ENDPOINT # STYTCH_REGISTRATION_ENDPOINT STYTCH_JWT_ISSUER STYTCH_JWT_AUDIENCE # STYTCH_DEBUG_CLAIMS=true bring-up only: log decoded claims to stderr # ── Retry / backoff ────────────────────────────────────────── # READs retry on 408/429/5xx with backoff+jitter; mutations are never retried. # GTM_MCP_RETRY_MAX=3 attempts after the first failure; 0 disables # GTM_MCP_RETRY_MAX_DELAY_MS=30000 # GTM_MCP_RETRY_TOTAL_TIMEOUT_MS=60000 # Static assets for the /oauth/authorize page. # AUTHORIZE_UI_DIR=apps/mcp-authorize/dist # Portal/worker subsystems (Postgres, token vault, Redis, job queue) live in # apps/portal/.env.example, not here. See docs/PRODUCTION_CUTOVER_RUNBOOK.md.