# Client controls

What the review records about each AI client: how to capture tools, where configs live, approval settings, org allowlists, token storage and telemetry. Client features change monthly; every fact here was checked on 2026-11-01, Inspector versions, Claude Code permission modes and Codex OAuth keys again on 2026-11-06, and must be verified live before you quote it.

## Capturing the tool list with the MCP Inspector CLI

Pin the current 2.x release, 2.14.0 or later (2.14.0 on 2026-11-06). The commands below need 2.10.1 or later (npx fails on 2.10.0), 2.12.0 fixed two high advisories in earlier 2.x releases (GHSA-q978-78c7-phqw, GHSA-vcc9-3xgm-p9v4), and 2.14.0 fixed three medium ones from 2026-11-04 (GHSA-h9j8-vj8g-v3gg, GHSA-rwv7-mh2c-ff2p, GHSA-2fw8-7jmw-r937). The deprecated 1.x line gets security fixes only, currently 1.0.4; below 0.14.1 is CVE-2025-49596. Check npm and the Inspector's security advisories before running.

The person runs live captures in their own terminal and signs in with a test identity; the agent never handles credentials. For review captures, keep test tokens off disk: with `MCP_INSPECTOR_PERSIST_TOKENS=none` and `MCP_INSPECTOR_SECRET_STORE=memory`, nothing is written to the keychain or to the plaintext `~/.mcp-inspector/secrets.json` fallback, and each run signs in again.

Remote server:

```bash
export MCP_INSPECTOR_PERSIST_TOKENS=none MCP_INSPECTOR_SECRET_STORE=memory
npx @modelcontextprotocol/inspector@<version> --cli https://mcp.example.com/crm --transport http \
  --protocol-era auto --method tools/list --format json > exampleco-crm-2026-10-28-tools.json
npx @modelcontextprotocol/inspector@<version> --cli https://mcp.example.com/crm --transport http \
  --protocol-era auto --method initialize --format json > exampleco-crm-2026-10-28-initialize.json
```

Local server, only inside the container described in publisher-and-supply-chain.md. Put the container command in a review-only config file and select it by name, so no Inspector flag is confused with a Docker flag:

```bash
npx @modelcontextprotocol/inspector@<version> --cli --config ./review-servers.json \
  --server adsbridge --protocol-era auto --method tools/list --format json > adsbridge-2026-10-28-tools.json
```

- `--protocol-era auto` probes `server/discover` and falls back to `initialize`. The default era, `legacy`, sends a plain `initialize`, which a server that supports only the 2026-07-28 revision rejects. A stdio server that ignores unknown methods makes the probe wait for its timeout; if a capture hangs, rerun it with `--protocol-era legacy`.
- `--format json` prints one JSON object with `result.tools`; the review script reads it as is.
- If the output has `result.nextCursor`, it holds one page. Fetch the remaining pages (the CLI lists a `--cursor` flag; check that your version accepts it for tools/list) and merge the tools arrays into one file, or record the capture as partial. The review script fails T12 until then.
- Add top-level keys to the tools capture, as in assets/example-adsbridge-tools.json: `"server": {"name": <serverInfo.name>, "version": <serverInfo.version>, "source": "live capture", "capturedAt": "YYYY-MM-DD"}` and `"instructions": <the initialize or server/discover instructions>`. The script ignores `serverInfo` itself.
- Server name and version are self-reported, and the specification says not to base security decisions on them. For a remote server the list fingerprint anchors the decision; the record labels the version "as reported". The list fingerprint covers tools only; the instructions get a fingerprint of their own.
- Compare the capture with the tool list the team's client shows for the same identity. Add any tool the client shows that the capture lacks, from the client or the vendor docs, and treat a mismatch as an incomplete capture until it is resolved.
  - Why they differ: servers can gate tools by client, and the specification lets a list vary with the authorization on the request. HubSpot marks its website page, blog post and asset tools as available only to select AI clients (Claude, ChatGPT and Gemini), so an Inspector capture can omit exactly the outward tools. MCP Apps tools can depend on advertised extensions.
  - Where to look: Claude Code's `/mcp` tool list, the chat app's connector tool settings, Codex's `/mcp` (check what it lists at run time).
- Prompts and resource templates reach the model when used. Capture them with `--method prompts/list` and `--method resources/templates/list` and add the arrays to the capture as `prompts` and `resourceTemplates`. The script scans them like descriptions but does not fingerprint them, so compare them by hand on a drift check.
- MCP Apps: `--method tools/list --app-info --advertise-apps` prints one JSON line per tool with `hasApp`, `resourceUri`, `csp` and `permissions`, without calling any tool. Some servers show App tools only to clients that claim App support, hence `--advertise-apps`. Save the output next to the capture and read the CSP origins and permission requests of every tool the script flags with T11.
- Exit code 3 (`auth_required`): without a terminal the CLI fails fast instead of opening a browser, which is what the agent's shell gets. Hand the commands to the person. `--stored-auth-only` never starts a browser sign-in. Reusing a web Inspector sign-in with `--use-stored-auth --server-url <url>` works only with persisted tokens; remove them afterwards.
- API-key servers: the person runs the capture in their own terminal with the key in an environment variable, never in chat or the agent's shell.
- `servers/show` redacts env values and sensitive headers, but not credentials inside a URL or stdio arguments, and headers typed into the Inspector's config are saved as written. Never paste its raw output into a ticket.
- Afterwards, remove any stored test credentials (`servers/remove` deletes an entry and its stored secrets) and revoke the test grant at the vendor.
- Vendor-docs route: copy the vendor's published tool list into `{ "tools": [{ "name": ..., "description": ..., "inputSchema": ..., "annotations": ... }] }` and set `source` to `vendor docs`. Vendor pages usually omit annotations; record them as not checked until a live capture. Fingerprints from docs and from live captures differ, so use one route consistently for drift checks.

## Config files to scan

Run `node scripts/scan-mcp-config.mjs` on every file the users will run. Do not open them with a file-read tool; they may hold secrets. When the scanner reports CF11 or exits 2, ask the file's owner to check the listed lines and record the answer, or list the file under Not checked.

- Claude Code: project `.mcp.json`; user and local scope in `~/.claude.json` (top-level `mcpServers` and `projects.<path>.mcpServers`); `managed-mcp.json` for an organisation's fixed set. Allow rules and approvals live in the settings files: `~/.claude/settings.json`, `.claude/settings.json`, `.claude/settings.local.json` and managed settings. The scanner reads their `permissions` and MCP approval keys, never their `env` block.
- Claude Desktop: `claude_desktop_config.json` in the app's config folder (macOS `~/Library/Application Support/Claude/`, Windows `%APPDATA%\Claude\`).
- Cursor: `.cursor/mcp.json` in the project and `~/.cursor/mcp.json`.
- VS Code and Copilot: `.vscode/mcp.json` and the user `mcp.json` (top-level `servers`; VS Code's guided setup labels both deprecated and keeps them for compatibility), `~/.copilot/mcp-config.json` (`mcpServers`; under `$COPILOT_HOME` when that is set) for Copilot's global config, and a workspace-root `.mcp.json` behind the experimental `chat.mcp.workspaceRootConfig.enabled` setting.
- Codex: `~/.codex/config.toml` and the project `.codex/config.toml`, which applies only to trusted projects. OpenAI's docs say the Codex CLI, the IDE extension and the ChatGPT desktop app share this configuration.
- Windsurf: its MCP docs now redirect to Devin Desktop ("Devin Local"), which lists `.devin/config.json` (project, checked in), `.devin/config.local.json` (gitignored) and `~/.config/devin/config.json`. Machines that ran Windsurf may still have `~/.codeium/windsurf/mcp_config.json`. Check the file format live; the scanner reports a file with no server entries instead of guessing. Enterprise admins manage an MCP allowlist and MCP registries in team settings ("MCP & ACP"); unless an admin allows a server by default, the agent asks before each MCP tool call.
- Windows: configs usually start npx through `cmd /c npx -y <package>`. The scanner unwraps `cmd /c`, `sh -c`, PowerShell `-Command`, `env` and `NAME=value` prefixes and checks the launcher inside like a direct one.

## Claude Code

- Scopes are local, project and user. Project servers in `.mcp.json` need approval, and a cloned repository cannot approve its own servers. `enableAllProjectMcpServers: true` removes that check (CF09); `enabledMcpjsonServers` pre-approves named servers.
- Unattended `-p` runs, Agent SDK sessions and some cloud sessions load project servers without the approval prompt. A scheduled run is Tier 3.
- Managed MCP allowlists and denylists match `serverUrl` or `serverCommand`. Entries that match only `serverName` are not a security control. `allowManagedMcpServersOnly` makes the allowlist authoritative; `managed-mcp.json` deploys a fixed set.
- `oauth.scopes` pins the scopes requested from the MCP server's own authorization server. It cannot narrow a proxy's upstream grant.
- Claude Code reads only its own credential variables (for example `ANTHROPIC_API_KEY`), cloud provider credentials (for example `AWS_BEARER_TOKEN_BEDROCK`) and a few others (for example `NPM_TOKEN`) as empty in a remote server's URL and headers. Any other name, such as `HUBSPOT_ACCESS_TOKEN` or `API_KEY`, expands as written, so an approved project `.mcp.json` can send an exported marketing token to a server it names.
- Run `claude mcp logout <name>` before `claude mcp remove`; removal alone deletes local tokens without revoking them.
- Permission rules address tools as `mcp__<server>__<tool>`. `mcp__<server>` and `mcp__<server>__*` allow every tool on the server, including tools it adds later (CF09 WARN). Allow globs work only after a literal `mcp__<server>__` prefix. Settings files skip any `mcp__` rule with parentheses; a parameter rule works only as a `--disallowedTools` deny rule. A project's `permissions.allow` rules apply only after the workspace trust dialog.
- From v2.1.283, interactive terminal and VS Code sessions start in auto mode when no `permissions.defaultMode` is set; on earlier versions that held on Pro, Max, Ultra and Team plans. A call with no matching allow, ask or deny rule then goes to a classifier, not a person, unless the tool carries `anthropic/requiresUserInteraction` or is an organisation connector set to ask.
- `auto` in `.claude/settings.json` or `.claude/settings.local.json` is ignored, and the session falls back to the built-in default, not to a mode in `~/.claude/settings.json`, so such a file can undo a user's manual setting. `bypassPermissions` in those two files starts in Manual mode. The VS Code extension does not read either file for the starting mode.
- For a write-capable server, require `permissions.defaultMode` `default` (Manual) in user or managed settings, or `permissions.disableAutoMode` `disable` in managed settings, and record the mode the sessions actually start in (the CLI status bar shows it). CF09 gives INFO for a settings file with no mode and WARN for `auto` or `bypassPermissions` (C15). `bypassPermissions` skips prompts; managed settings block it with `permissions.disableBypassPermissionsMode` set to `disable`.
- `-p` and Agent SDK runs start in Manual mode from v2.1.302. Earlier versions could start them in auto mode.
- Tools on organisation connectors that are set to ask prompt on every call, even in auto and bypass modes, and allow rules do not skip that prompt.
- `OTEL_LOG_TOOL_DETAILS=1` adds server and tool names to exported telemetry (C10 evidence).
- Session transcripts are plaintext and kept 30 days by default (`cleanupPeriodDays`). OAuth tokens live in the macOS Keychain or a 0600 `.credentials.json`.

## Claude apps (web, desktop, mobile)

- Team owners and primary owners add custom connectors; Enterprise also allows custom roles with Manage access to Libraries. Members then connect individually.
- Connector requests come from Anthropic's cloud, not the user's laptop. A firewalled server must allow Anthropic's published outbound ranges, which every Anthropic customer shares, so an IP allowlist proves nothing about identity.
- Per-tool permissions exist. Use "Allow always" only for tools the review classed as read and trusted.
- Research can call connector tools without further approval. Block write tools on any write-capable connector, or keep the connector out of Research (C15).
- A connector configured with a fixed credential on a team plan acts as that credential for everyone who uses it (C9).
- The apps render MCP Apps: a tool's `ui://` resource runs in a sandboxed frame that can call the server's tools and post into the conversation (injection-and-poisoning.md).

## ChatGPT and Codex

- ChatGPT: custom MCP servers are added from the Plugins page with a risk warning, and workspace policies decide who may add them (`Create plugins with MCPs`, `Upload plugins with custom MCP servers`). Private servers use Secure MCP Tunnel. Read the plugin's New tools setting and each app's permission setting (Always ask, Allow read actions, Allow low-risk actions, Allow all actions) before recording how write actions are confirmed.
- After an admin approves a server, ChatGPT keeps a frozen copy of its tools until an admin publishes an update. That limits a rug pull for members, and it means a fresh capture can differ from what they see: compare the capture with the published list, and review the update before publishing it.
- Codex `[mcp_servers.<name>]` keys: `command`, `args`, `env`, `env_vars` (names to forward), `cwd`; `url`, `auth` (`oauth` or `chatgpt`), `bearer_token_env_var`, `http_headers` (static values, checked by CF01), `env_http_headers` (header to variable name), `http_headers_helper` (a local command that prints headers); `scopes` and an `oauth` table (`client_id`, `callback_url`, and `client_secret`, saved as written in `config.toml`: CF01); `startup_timeout_sec`, `tool_timeout_sec`, `enabled`, `required`; `enabled_tools`, then `disabled_tools`; `default_tools_approval_mode`; `tools.<tool>.approval_mode`; `tools.<tool>.output_token_limit`.
- `default_tools_approval_mode` accepts `auto`, `prompt`, `writes` or `approve`. The docs define only `writes`: it prompts for tools that are not marked read-only. Use `prompt` for any server with a T02 or D04 finding, and `enabled_tools` as an allowlist of reviewed tools.
- Codex's approvals page says destructive app and MCP tool calls require approval when the tool advertises a destructive annotation, and that a read annotation takes priority. That is why one false readOnlyHint matters.
- Auto-review (`approvals_reviewer = "auto_review"`) routes eligible approval requests, side-effecting app and MCP tool calls among them, to a reviewer agent instead of a person. Record it per write-capable server and keep it off under C15.
- `--ask-for-approval never` (`-a never`) disables approval prompts; record that client as having no human approval. A granular `approval_policy` rejects whole prompt categories automatically, `mcp_elicitations` among them.
- `codex.tool_decision` telemetry events record whether each tool call was approved or denied, and whether configuration or the user decided (C10 evidence).
- Codex's command network proxy does not filter app or connector tool calls or MCP server connections. Sandbox egress rules are not an MCP control.
- OAuth sign-in is `codex mcp login <name>`. Scopes can be pinned per server with `mcp_servers.<name>.scopes` or per login with `--scopes`; like `oauth.scopes`, neither narrows a proxy's upstream grant.
- Codex reads the server's `instructions` from initialize as server-wide guidance, and its docs advise keeping the first 512 characters self-contained. That text reaches the model before any tool runs: capture it and scan it (T06), and diff it on drift checks (D05).

## Cursor

- Asks before MCP tool calls by default and shows the arguments.
- In Auto-review run mode, allowlisted MCP tools run immediately and the rest go to a classifier.
- The enterprise MCP Allowlist (Team Settings, MCP Configuration) matches by command or URL pattern and can allowlist tools per server.
- Stdio servers support environment interpolation and an `envFile`.

## VS Code and GitHub Copilot

- Tool calls need approval. Tools are managed under Chat: Open Customizations, Tools for Copilot sessions, or Configure Tools in the chat input for local sessions.
- Organisations can restrict which servers are available through an enterprise allowlist. Verify the current policy names and approval scopes live.
- With `sandboxEnabled: true` on a server, VS Code auto-approves its tool confirmations (CF09 WARN). Keep it off for a server with write tools.
- Portable configs do not support VS Code input variables or env files.
- VS Code Copilot and Microsoft 365 Copilot render MCP Apps; read the T11 tools' `--app-info` output before allowing them.

## Code mode and scripted tool calls

When a client lets the model write and run a script that calls tools, approving the script is not approving each call inside it, as the MCP client best practices point out. Treat that capability as Tier 3 and review every tool the script can reach.

## Second-opinion scanners

- Cisco MCP Scanner's offline YARA analyzer works on the saved tools JSON; its LLM and API analyzers send data out.
- Snyk Agent Scan starts stdio servers and sends definitions and redacted configs to Snyk. Use it only where approved.

## What to record per client

- Config and settings files, scope and who administers them.
- Token storage: keychain, managed store or plaintext fallback.
- Approval setting per write tool: always allow, prompt, blocked, or an annotation-based mode.
- Automatic approval: Codex Auto-review or approval never, Claude Code auto or bypassPermissions mode (auto is the starting mode when nothing sets one), Cursor Auto-review.
- Session permission mode: what each client actually starts in, and where that is set.
- Telemetry with server and tool names, and transcript retention.
- Whether unattended, research or scheduled modes are on.
