# Clients: placement, auth, approvals and traps

Where each client keeps MCP config, how sign-in works, how to see and approve tools, how to block writes, which org controls exist and which traps break a setup silently. Checked on 2026-11-01 against each client's MCP pages in `sources.yaml`, Claude Code permission modes and ChatGPT again on 2026-11-06; menu labels move between versions, so prefer file paths and commands, and follow the live UI when it differs.

## Rules for every client

- One job per profile or project. Enable only the servers a session needs and toggle the rest off.
- Prefer controls that deny new tools by default: a read-tool allowlist, a glob deny on write verbs, a workspace 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, and an approval for a write tool is never remembered.
- Server names encode vendor, account, environment and mode: `klaviyo-acme-prod-ro`.
- No secret in a shared file: use `${VAR}`, `${env:NAME}` or `${input:id}` references, or OAuth.
- Apply changes through the client's own command where one exists (`claude mcp add` and `claude mcp remove`, `codex mcp add` and `codex mcp remove`, `/permissions`, connector settings pages). For JSON-only clients (Cursor, Claude Desktop, VS Code) and keys with no command (Codex `enabled_tools`), give the user the block and the file path to paste it into. Never read a whole config file into the conversation; re-run the scanner on the file afterwards.
- Record where each config lives in the inventory's Clients and config line.
- A command that takes a client secret is run by the user in their own terminal: `claude mcp add --client-secret` prompts with masked input, and `codex mcp add --oauth-client-secret` reads an environment variable the user sets. The agent gives the command without the secret and never sees it.

## Claude Code

Add and scope:

- Remote: `claude mcp add --transport http <name> <url>` (`-t` for short). Fixed headers with `--header`. Whole entries with `claude mcp add-json`.
- Local stdio: `claude mcp add <name> -e KEY=VALUE -- <command> <args>`. Put the name before `-e`, or another option between `-e` and the name; otherwise the CLI reads the name as an env pair.
- Scopes: `-s local` (default; this project only, stored in `~/.claude.json` under the project path), `-s project` (`.mcp.json` at the project root, meant for version control), `-s user` (all projects, in `~/.claude.json`).
- Precedence: local, then project, then user, then plugin, then claude.ai connectors. The whole entry from the highest source wins; fields are not merged. Plugins and connectors count as duplicates when they point at the same URL or command. Scanner rule S10 flags a name defined in several scopes.

Sign-in:

- Run `/mcp` and complete the browser sign-in. The menu offers Reconnect, Disable, Clear authentication and Re-authenticate. `claude mcp login <name>` and `claude mcp logout <name>` do the same from the shell.
- Flags `--client-id`, `--client-secret` (masked prompt; `MCP_CLIENT_SECRET` in CI) and `--callback-port` (redirect `http://localhost:PORT/callback`) register your own OAuth client. `oauth.scopes` in the JSON entry restricts the scopes requested.
- `claude mcp remove` deletes local tokens but does not revoke them at the provider: run `claude mcp logout` first where supported.
- Non-interactive runs cannot complete OAuth: sign in interactively first.

Shared files and unattended runs:

- `.mcp.json` expands `${VAR}` and `${VAR:-default}` in command, args, env, url and headers. Credential variables such as `ANTHROPIC_API_KEY` read as empty inside url and headers.
- Interactive sessions ask before using `.mcp.json` servers (`claude mcp reset-project-choices` resets the answers). `claude -p`, the Agent SDK and cloud sessions load project servers without asking. Block them with `disabledMcpjsonServers`, `--setting-sources`, or `--strict-mcp-config` with `--mcp-config`.
- Settings can switch the prompt off. Check `~/.claude/settings.json` and the project's `.claude/settings.json` and `.claude/settings.local.json` for `enableAllProjectMcpServers` (approves every project server) and `enabledMcpjsonServers` (approves the servers it names). In user settings they apply in every repository, even untrusted ones; committed to `.claude/settings.json` they apply once a teammate trusts the folder; an untracked `settings.local.json` applies in a trusted folder. Remove them for repositories that hold marketing servers. A `disabledMcpjsonServers` entry in any settings file still rejects a server. Scanner rule S14 reports all three.
- Never commit a write-capable server to a project file in a repository used by scheduled or non-interactive runs.

Servers that are not in config files:

- Plugin-provided servers come with installed plugins, appear in `/mcp` marked as plugin servers, and are named `plugin:<plugin>:<server>`; their tools are `mcp__plugin_<plugin>_<server>__<tool>`. Toggle one off in `/mcp`; remove it by uninstalling the plugin.
- claude.ai connectors appear only when Claude Code signs in with a claude.ai subscription, not with `ANTHROPIC_API_KEY`, `apiKeyHelper`, Bedrock, Vertex or a setup token. An entry you add with the same URL takes precedence, and the connector shows as hidden. Turn fetched connectors off with `disableClaudeAiConnectors` or `ENABLE_CLAUDEAI_MCP_SERVERS=false`.
- Vendors whose OAuth accepts only registered web callbacks (Google Cloud remote MCP; Salesforce External Client Apps set up for claude.ai): add the connector in claude.ai and let Claude Code inherit it. Only Claude Code inherits claude.ai connectors; Codex, Cursor and VS Code need their own connection, so check the vendor's supported client list for them.
- Org tool controls on claude.ai connectors (ask or blocked) are enforced locally, and ask prompts even in permissive modes.

Approvals and limits:

- Permission rules: `mcp__<server>__*` or `mcp__<server>__<tool>`. Dots and spaces in server names become underscores. Run `/mcp` and select a tool to see its full name. Tools whose full name passes 128 characters are left out. Connectors fetched from claude.ai appear as `mcp__claude_ai_<server>__<tool>`.
- Rules evaluate deny, then ask, then allow, and an allow rule cannot carve an exception out of a deny rule. A deny rule that matches a tool name, glob included, removes the tool from the agent's context, so the agent cannot call it and its own tool list leaves it out: take tool snapshots from `/mcp`.
- Deny and ask rules accept globs in the tool-name position, so `mcp__<server>__manage_*` or `mcp__<server>__delete_*` covers tools the vendor adds later under the same verb. Allow globs work only after a literal server prefix: `mcp__hubspot-acme-prod-ro__get_*` allows that server's `get_` tools, and an unanchored allow glob is skipped.
- Deny by default: in `dontAsk` mode every call that would prompt is denied, while tools in `permissions.allow` still run. An allowlist of read tools plus `dontAsk` keeps tools a vendor adds later switched off, which suits unattended runs.
- From v2.1.283, interactive sessions start in `auto` mode when nothing sets a mode, so a classifier, not a person, decides any call no rule covers. In Manual mode (config value `default`) those calls prompt. `bypassPermissions` skips prompts. For sessions with marketing servers, set `permissions.defaultMode` to `default` in `~/.claude/settings.json` or managed settings, and check the mode the session really starts in. `auto` in a project settings file falls back to the built-in default and drops a user-level mode, so such a file can undo your setting (scanner S15). Admins can turn both modes off by setting `permissions.disableAutoMode` and `permissions.disableBypassPermissionsMode` to `"disable"`.
- `--tools` restricts built-in tools, not MCP tools.
- `MAX_MCP_OUTPUT_TOKENS` defaults to 25,000 tokens per tool result, with a warning at 10,000; text results over 50,000 characters are saved to a file. `MCP_TIMEOUT` sets the startup timeout. A result that hits a cap is partial: T5 must say so.

Audit:

- Session transcripts record each tool call. OpenTelemetry with `OTEL_LOG_TOOL_DETAILS=1` adds MCP server and tool names to `tool_result` and `tool_decision` events; without it, user-configured MCP calls show only as `mcp_tool`.

Admin controls (managed settings):

- `managed-mcp.json` at `/Library/Application Support/ClaudeCode/` (macOS), `/etc/claude-code/` (Linux and WSL) or `C:\Program Files\ClaudeCode\` (Windows) takes exclusive control of MCP servers; servers from `managedMcpServers` still load alongside it.
- `managedMcpServers` provides remote servers alongside users' own, as an object keyed by server name, and takes precedence over the same name in local, project or user scope. The scanner reads it from `managed-settings.json`; a copy delivered by server-managed settings or MDM is not in that file. `allowedMcpServers` and `deniedMcpServers` match `serverUrl` (with `*` wildcards), `serverCommand` (exact array) or `serverName` (a user label, not a security control). `allowManagedMcpServersOnly` makes the managed allowlist authoritative. Deny always wins.

## Claude Desktop and claude.ai

Remote connectors:

- Custom connectors exist on Free (one), Pro, Max, Ultra, Team and Enterprise.
- Individuals: Customize > Connectors > + Add > Add custom connector, then name and URL > Continue > review authentication > Add.
- Team and Enterprise owners: Organization settings > Connectors > Add > Custom > Web; members then connect from Customize > Connectors.
- The authentication step chooses the OAuth client: "Use Claude's published identity (Recommended)", "Register automatically", or "Use your own OAuth client" (Salesforce needs your own). Fixed request headers are possible for servers without OAuth; avoid them for credentials and prefer OAuth.
- Remote connector traffic comes from Anthropic's cloud, not the user's device: private servers must allowlist Anthropic's IP ranges.

Tool permissions:

- Per connector: Always allow, Needs approval or Blocked, per tool or per category (read-only tools, write and delete tools). Block the write and delete category for every read-only connection.
- Team and Enterprise owners can enforce these settings org-wide. They only narrow what the source system permits; they never widen it.
- Research can call connector tools without further approval: block write tools on any connector used there. Click Allow always only for tools you trust unsupervised.

Local servers:

- Desktop extensions (`.mcpb`) under Settings > Desktop app, Manage next to Extensions (Browse extensions, or Advanced settings > Install Extension; older layout Settings > Extensions), or `claude_desktop_config.json`: `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows. Extensions installed from the Extensions page are not in `claude_desktop_config.json`: list them from that page for the inventory.
- Local servers are not available in Cowork or claude.ai. Team and Enterprise owners can allowlist extensions.
- Claude Code sessions inside the desktop app receive connectors in-process: the org's blocked-tool settings apply there; MCP settings and `managed-mcp.json` do not.

## ChatGPT

Add a server:

- Plugins page > + > Add custom MCP server > name and description > Connection: the URL including `/mcp`, or Tunnel (Secure MCP Tunnel reaches a customer-run stdio or HTTP server without a public listener) > Authentication: OAuth, No authentication, or OAuth or no authentication (static client credentials, otherwise CIMD or DCR) > accept the risk warning > Create as a plugin > review the discovered tools.
- Transports: SSE and streaming HTTP. After a server changes, open the connection, select Refresh, then start a new conversation.

Approvals:

- Write actions require confirmation by default, but the app permission setting changes when ChatGPT asks (Always ask, Allow read actions, Allow low-risk actions and, where supported, Allow all actions). Check that setting before relying on a prompt. `readOnlyHint` is honoured, and a tool without it is treated as a write.
- A remembered approve or deny lasts for the conversation; new or refreshed conversations ask again. Never remember an approval for a write tool.

Workspace controls (Business, Enterprise, Edu):

- Roles "Create plugins with MCPs" (developer mode) and "Upload plugins with custom MCP servers".
- Admin console > Plugins: Available for installation, Installed for workspace, Customize by role, Disable.
- Per plugin: the Tools or Actions list and the "New tools" or "New actions" setting for actions added later (enable all, only new read actions, or disable). Disabling it leaves actions already on as they are. Keep new tools off until reviewed.
- A member can use an MCP server's tools only when both the plugin and its app are available to their role.
- After an admin approves a server, ChatGPT keeps a frozen copy of its tools until an admin publishes an update, so T2 compares the published list, not the server's live one.

Plans and vendor specifics:

- OpenAI's help centre says full MCP is on Business and Enterprise/Edu, and Pro can connect custom servers with read and fetch permissions only. On Business only admins and owners create and publish; on Enterprise/Edu, roles can extend creation and only admins and owners publish.
- Custom MCP is ChatGPT web only, with no mobile setup. OAuth without `offline_access` may ask users to sign in again when access expires.
- Google Cloud remote MCP needs ChatGPT Business, per Google's client setup page.
- Klaviyo's listed app cannot take URL parameters: use a custom MCP server for read-only.
- Mixpanel's page still tells EU and India projects to turn on Developer Mode first.
- Amplitude gives ChatGPT a curated subset of its tools. `?discovery=progressive` reaches the wider catalog, but it hides the tools from T2 (failure F19).
- HubSpot's ChatGPT connector does not apply custom validation rules and caps bulk writes at 10 records.
- Google Cloud remote MCP redirect URI: `https://chatgpt.com/connector_platform_oauth_redirect`.
- Meta uses your Meta app ID as the OAuth client ID under the advanced OAuth settings.
- Salesforce requires the consumer secret here, though ChatGPT marks it optional, and its guide says to untick OIDC enabled.
- Add a custom MCP server from the ChatGPT Plugins page. No Developer Mode toggle is needed first, and workspace permissions still apply. OpenAI labels the older Apps > Create route legacy, and some vendor guides still show it (Settings > Apps; Settings > Security and login): follow the live UI.

## Cursor

- Config: `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global), top-level key `mcpServers`.
- Stdio entries: `type` stdio, `command`, `args`, `env`, `envFile`. Remote entries: `url`, `headers`, and `auth` with `CLIENT_ID`, optional `CLIENT_SECRET` (scanner S03 reports a literal value) and optional `scopes`.
- Interpolation: `${env:NAME}`, `${userHome}`, `${workspaceFolder}`, `${workspaceFolderBasename}`.
- OAuth with PKCE, using a static client or dynamic registration. Redirect URLs: desktop `http://localhost:8787/callback`; web and agents `https://www.cursor.com/agents/mcp/oauth/callback`.
- Cursor asks before using MCP tools by default. In Auto-review mode allowlisted MCP tools run immediately and everything else is routed through a classifier, so never allowlist a write tool, and keep Auto-review off for sessions with write-capable marketing servers. Servers toggle on and off from Customize. Cursor's MCP page documents no per-tool switch in your own settings: check the live MCP settings before relying on one.
- Team: Dashboard > Plugins & MCPs for distribution; Team Settings > MCP Configuration for an allowlist with command and URL patterns, per-server tool allowlists (an empty tool list allows all) and network modes per server.
- Some vendor guides use `npx -y mcp-remote <url>`. Prefer the native `url` entry when OAuth works; otherwise pin the proxy version (scanner S13).

## VS Code

- Two formats. VS Code format: `.vscode/mcp.json` and the user-profile `mcp.json` use the top-level key `servers` (plus `inputs` and `sandbox`). Portable format: `.mcp.json` at the workspace root or `~/.copilot/mcp-config.json` (under `$COPILOT_HOME` when that is set) use `mcpServers`. A `.vscode/mcp.json` with `mcpServers` never loads (scanner S09).
- Some vendor guides (Klaviyo's local server) put servers under `mcp.servers` in the user `settings.json`, which VS Code's MCP pages do not list. The scanner reads that block too.
- Copilot harness sessions read the portable format directly and do not read `.vscode/mcp.json`; servers that need input variables are not forwarded to them.
- Fields: stdio `type` stdio, `command`, `args`, `cwd`, `env`, `envFile` (VS Code format only); http `type` http or sse, `url`, `headers`, `oauth` with `clientId`.
- Secrets: `inputs` entries with `promptString` and `password: true`, referenced as `${input:id}`, prompted on first start and stored securely. `${env:NAME}` in the VS Code format, `${NAME}` in portable files.
- Commands: MCP: Add Server; MCP: List Servers (select a server, then Start Server to authenticate); Chat: Manage Tool Approval; Chat: Reset Tool Confirmations (clears every saved approval, so rerun the drift check afterwards); MCP: Reset Trust.
- Permission levels: Manual (the default), Assisted (an LLM judge assesses each call) and Allow all. Autopilot is an agent mode, and a permission level only in Local harness sessions. Keep the Allow all level and the Autopilot mode off for marketing servers, and set each write tool to false in `chat.tools.eligibleForAutoApproval` so it always asks.
- Trap: with `sandboxEnabled`, tool confirmations are auto-approved. Never sandbox a write-capable marketing server expecting confirmations.
- Org: `chat.mcp.access` controls which servers can be used; GitHub enterprise allowlists govern Copilot.

## Codex

- Add: `codex mcp add <name> --url <url>`; stdio `codex mcp add <name> --env KEY=VALUE -- <command>`; `--oauth-client-id` and `--oauth-client-secret "$MCP_CLIENT_SECRET"` for your own client. Codex saves the secret as `mcp_servers.<name>.oauth.client_secret` in `config.toml`, so keep that file private and out of version control (scanner S03 reports it).
- Sign in: `codex mcp login <name>`, with `--scopes` to narrow a login and `--oauth-client-registration` auto, cimd or dcr.
- Config: `~/.codex/config.toml`, or `.codex/config.toml` in trusted projects, under `[mcp_servers.<name>]`: `url`, `bearer_token_env_var`, `http_headers`, `env_http_headers`, `command`, `args`, `env`, `env_vars`, `startup_timeout_sec` (10), `tool_timeout_sec` (60), `enabled`, `required`, `enabled_tools`, `disabled_tools`.
- Approvals: `default_tools_approval_mode = "writes"` prompts for tools not marked read-only (values auto, prompt, writes, approve); `tools.<tool>.approval_mode` sets one tool. `writes` trusts each tool's read-only mark, so use `prompt` on a server with a falsely marked tool, and leave `approvals_reviewer` at `user`: `auto_review` sends approvals to a reviewer agent.
- `enabled_tools` is the strongest drift control in any client: new tools stay off until someone adds them. Use the names the client lists: on a server with prefixed tools (Google Ads `search_search`), the bare names match nothing and the server shows no tools. It has no command: hand the user the block to paste.
