# Find the MCP servers employees have installed

> No admin console lists the MCP servers employees have installed. The order that works: a device sweep, the vendor logs that exist, then a direct ask.

**TL;DR** The MCP servers employees have installed live in JSON and TOML files on their devices, and no client vendor documents an admin inventory of those files. The method that works is an MDM or EDR script that reads each client's config and collects only the server entries, then the three vendor admin logs that exist, then a short message to the teams using them. Elaichi is where the servers that reach company SaaS accounts go afterward: one organization-wide MCP endpoint behind OAuth, with connectors Elaichi authors itself.

## Why no admin console lists the MCP servers employees have installed

Most of the MCP servers employees have installed are lines in a file on a laptop, and no client vendor documents an admin inventory of those files. MCP (Model Context Protocol) is the standard way an AI assistant calls tools in other apps. A server is either a local process the client starts on the device, or a remote HTTPS address the client calls ([modelcontextprotocol.io](https://modelcontextprotocol.io/docs/develop/connect-local-servers), checked October 2026). Both are recorded the same way, as an entry in the client's own configuration file.

That is why network tooling finds so little. A local server never leaves the machine, so there is no request for a proxy to inspect. A remote one is an ordinary HTTPS call. For Claude's connectors, that call comes from Anthropic's cloud rather than the user's device, on every Claude client ([claude.com/docs/connectors/custom/add-unlisted](https://claude.com/docs/connectors/custom/add-unlisted), checked October 2026). Inline inspection still earns its place for other reasons. It is not how you build this list.

Vendor consoles report what connects through the vendor's own surface, which is a different set from what sits in a file. So the order is: read the files, then read the logs, then ask the people.

A note on sourcing: every claim below traces to a vendor's own documentation, linked and dated. "Checked [month, year]" marks when we last reconciled the claim against that page, not a publication date. Vendor docs change without notice, and the config path below moved when Windsurf became Devin Desktop. Re-verify against the live link before you build unattended automation on a specific path or key.

## Which file holds the server list on each client?

Each client keeps its MCP servers at a documented path, and usually there are two, one per user and one per project. These are the paths as of October 2026, read from each vendor's own page. This list covers the clients most likely to appear in a laptop fleet today: Claude Desktop, Claude Code, Cursor, VS Code with Copilot, Devin Desktop and Codex. JetBrains AI Assistant, Zed, and mobile or browser-based MCP clients aren't covered here; if your fleet includes them, find the equivalent path in that vendor's own docs before treating a sweep as complete.

- **Claude Desktop.** `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows, with servers under the `mcpServers` key. Anthropic gives no Linux path. Server names also appear in the log directory, `~/Library/Logs/Claude` or `%APPDATA%\Claude\logs`, as one `mcp-server-SERVERNAME.log` per server ([modelcontextprotocol.io](https://modelcontextprotocol.io/docs/develop/connect-local-servers), checked October 2026).
- **Claude Code.** Local and user scope live in `~/.claude.json`. Project scope lives in `.mcp.json` at the repository root. On a device you can also run `claude mcp list` ([code.claude.com/docs/en/mcp](https://code.claude.com/docs/en/mcp), checked October 2026).
- **Cursor.** `~/.cursor/mcp.json` globally and `.cursor/mcp.json` per project ([cursor.com/docs/mcp](https://cursor.com/docs/mcp), checked October 2026).
- **VS Code with GitHub Copilot.** `.vscode/mcp.json` with a top-level `servers` key, plus a user-profile `mcp.json`. Both are now labeled deprecated in the Add Server flow, which steers new servers to `.mcp.json` at the project root and `~/.copilot/mcp-config.json`. Sweep both generations ([code.visualstudio.com](https://code.visualstudio.com/docs/agent-customization/mcp-servers), checked October 2026).
- **Devin Desktop**, which was Windsurf. `~/.config/devin/mcp_config.json`, or `%APPDATA%\devin\mcp_config.json` on Windows. Devin's docs still name the older `~/.codeium/windsurf/mcp_config.json`, so check both ([docs.devin.ai](https://docs.devin.ai/desktop/cascade/mcp), checked October 2026).
- **Codex.** `~/.codex/config.toml`, one `[mcp_servers.<name>]` table per server, plus a project `.codex/config.toml` in trusted projects only. One file covers the ChatGPT desktop app, the Codex CLI and the IDE extension ([learn.chatgpt.com](https://learn.chatgpt.com/docs/extend/mcp), checked October 2026).

Desktop extensions in Claude are local servers too, installed from Settings and then Extensions. No vendor page gives their path on disk, so the allowlist is the control for them, not a file sweep.

### Quick reference: paths, policy and log coverage by client

| Client | User/global config | Project config | Policy control | Admin log / audit coverage |
|---|---|---|---|---|
| Claude Desktop | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`; Windows: `%APPDATA%\Claude\claude_desktop_config.json` (`mcpServers` key) | n/a (extensions use an allowlist, not a file) | macOS domain `com.anthropic.claudefordesktop`; Windows `HKLM:\SOFTWARE\Policies\Claude` | Per-server logs at `~/Library/Logs/Claude` or `%APPDATA%\Claude\logs` |
| Claude.ai connectors | n/a, server-side | n/a | Enterprise admin console | Compliance API: `integration_user_connected` / `_disconnected`, `mcp_server_created`, `mcp_tool_policy_updated` |
| Claude Code | `~/.claude.json` | `.mcp.json` at repo root | `allowedMcpServers` / `deniedMcpServers` in managed settings (deny wins) | OpenTelemetry `claude_code.mcp_server_connection`, server names only with `OTEL_LOG_TOOL_DETAILS=1` |
| Cursor | `~/.cursor/mcp.json` | `.cursor/mcp.json` | Enterprise allowlist (does not push servers) | Enterprise audit log: `mcp_server_config`, `mcp_authentication`; `beforeMCPExecution` hook per call |
| VS Code + Copilot | user-profile `mcp.json` (deprecated), now `~/.copilot/mcp-config.json` | `.vscode/mcp.json` (deprecated), now `.mcp.json` at root | `chat.mcp.access` (`all`/`registry`/`none`) + per-server allow/deny (deny wins) | Usage metrics report MCP use for Copilot CLI only |
| Devin Desktop | `~/.config/devin/mcp_config.json`; legacy `~/.codeium/windsurf/mcp_config.json` | n/a | Team admin on/off + allowlist (one allowlisted server blocks all others) | None documented |
| Codex (app/CLI/IDE) | `~/.codex/config.toml` | `.codex/config.toml` (trusted projects only) | `requirements.toml` approved `mcp_servers` list (empty key disables all) | None documented |

## How to collect only the server entries

Parse each file and emit the server objects alone. Never ship the whole file to your inventory.

The reason is concrete. `~/.claude.json` holds the Claude Code sign-in session alongside its `mcpServers` object, so a script that uploads the file uploads a credential. Read the `mcpServers` object out and discard the rest. Do the same for `servers` in VS Code's files and the `[mcp_servers.*]` tables in Codex's TOML. Use a real JSON or TOML parser rather than a regular expression, and when a file will not parse, log the path and move on.

Per entry, record five fields: the device, the user, the client, the server name, and either its `url` or its `command` with arguments. For an `env` block, record the key names and not the values. Those values are often long-lived API tokens, and a sweep that copies them creates a second place they live.

Project files are the part most sweeps miss. `.mcp.json`, `.cursor/mcp.json`, `.vscode/mcp.json` and `.codex/config.toml` sit inside repository checkouts, not in the home directory, so the script needs a path glob across developer working directories. Run the whole sweep twice, a week apart. The delta tells you whether the list is growing, which matters more than the first snapshot.

## Which admin logs show an MCP connection?

Three exist today, and each covers connections made through the vendor's surface rather than files on disk.

Anthropic's Compliance API, on Enterprise, emits `integration_user_connected` and `integration_user_disconnected` with `integration_type` set to `mcp`, carrying the server's id and name. It also carries admin events such as `mcp_server_created` and `mcp_tool_policy_updated`. The Primary Owner enables it and there is no backfill, so turn it on before you need the history ([platform.claude.com](https://platform.claude.com/docs/en/api/compliance/activities), checked October 2026). It covers connectors on the claude.ai side, not local config files.

Claude Code can emit `claude_code.mcp_server_connection` over OpenTelemetry, with server names present only when `OTEL_LOG_TOOL_DETAILS=1` ([code.claude.com](https://code.claude.com/docs/en/monitoring-usage), checked October 2026). Cursor's Enterprise audit log carries `mcp_server_config` and `mcp_authentication` events ([cursor.com/docs/enterprise/compliance-and-monitoring](https://cursor.com/docs/enterprise/compliance-and-monitoring), checked October 2026). Cursor does not say that an edit to a member's own `mcp.json` emits either, so treat it as coverage of connections rather than of files. Cursor also documents MDM-deployed hooks, where `beforeMCPExecution` sees the server name and its URL or command on every call and can allow, deny or ask ([cursor.com/docs/hooks](https://cursor.com/docs/hooks), checked October 2026). GitHub's usage metrics report MCP use for Copilot CLI only ([docs.github.com](https://docs.github.com/en/enterprise-cloud@latest/copilot/reference/copilot-usage-metrics/copilot-usage-metrics), checked October 2026). OpenAI and Devin document nothing comparable, so the file sweep is the whole answer for Codex and Devin Desktop.

## What to ask people directly

Send a short message to the teams most likely to have built something, and make it a question about their work rather than a compliance notice. Scripts miss anything written outside a standard directory. An engineer who wrote a local server to read a staging database will say so if nothing bad happens when they do.

Four questions is enough. Which MCP servers do you have configured, in any client. Which of them reach a company system, as opposed to local files. Which did you write yourself. Which would break your week if it stopped working on Friday.

That last question sorts the list. It separates a tool somebody tried once from a tool that is now part of a process, and the second kind has to be replaced rather than removed. State up front that the goal is to keep the useful ones working. An audit that reads as punishment pushes the next one further out of sight.

## Keep, replace, remove: sorting the inventory

Sort every entry into one of three buckets, and resist inventing a fourth.

**Keep** is a local server that touches nothing but the developer's own machine and holds no company credential. A filesystem reader over a local checkout is the usual case. Record it, scope it so it cannot reach a production environment, and move on.

**Replace** is any server that reaches a company SaaS account: the Jira server with a personal API token in an `env` block, the Slack server somebody pasted a bot token into, the internal API wrapper running on one laptop. These are the entries a leaver takes with them, and they sit outside whatever sign-in rules govern everything else on the device, which is the practical case for [OAuth rather than API keys](/blog/oauth-vs-api-keys-for-ai-agents/).

The replace pattern has the same shape regardless of vendor: one shared endpoint behind OAuth, so no server URL or token lives in a per-user config file; a catalog of maintained connectors, so engineering time isn't spent patching a Jira or Slack MCP server; and a restriction layer, a rule naming which connectors and which individual tools a role or person may reach, enforced at browse, connect, advertise and execute, plus a final check on the fully substituted outbound URL, not only at connect.

Disclosure: Elaichi, which publishes this post, is one implementation of that pattern. It serves 600+ connectors through a single organization-wide endpoint, `POST https://api.elaichi.ai/mcp`, behind OAuth, with no per-user URLs and no embedded tokens. A restriction change takes effect within about two minutes. Elaichi writes one entry per tool-call attempt, succeeded or failed. If you're evaluating this category rather than this vendor, four questions apply to any product in it: single endpoint or per-user URLs, who authors and maintains the catalog, where restrictions are enforced (connect only, or also at execute), and how fast a change propagates.

**Remove** is everything with no owner, plus anything built on a shared long-lived token. Deleting the config entry is not the whole job. Rotate the credential it held, because the entry was only one copy of it.

Write the limit down next to the list. A governed endpoint controls access through itself and nothing upstream of it. It does not govern a server somebody else runs. GitHub's own MCP server, for instance, stays governed by GitHub's policy and your identity provider, regardless of catalog. Removing access through the gateway also ends there: the person's account inside each downstream app still exists and is deprovisioned through that app or your identity provider, which is the harder half of [offboarding when an agent holds access](/blog/offboarding-when-the-agent-holds-access/).

## Which managed policy keeps the list from growing again

Each client ships its own, and each binds only that client. Set them after the sweep, so you know in advance what the policy will break.

Claude Desktop reads device policy from the macOS domain `com.anthropic.claudefordesktop` and from `HKLM:\SOFTWARE\Policies\Claude` on Windows, where `isLocalDevMcpEnabled`, `isDesktopExtensionEnabled` and `isDesktopExtensionDirectoryEnabled` all default to true. The Desktop extension allowlist in Organization settings is off by default, and turning it on removes existing installs ([support.claude.com](https://support.claude.com/en/articles/12622667-enterprise-configuration-for-claude-desktop), checked October 2026). Claude Code uses `allowedMcpServers` and `deniedMcpServers` in managed settings, where the denylist always wins and an unset allowlist allows everything. Anthropic states that matching by `serverName` is not a security control; the reason is that users choose the names, since the name is the key a person types in their own config. Match on `serverUrl` or `serverCommand` instead ([code.claude.com](https://code.claude.com/docs/en/managed-mcp), checked October 2026). A newly blocked server disappears from `/mcp` with no reason shown, so tell people before you apply it.

VS Code has `chat.mcp.access`, set to `all`, `registry` or `none`, plus per-server allow and deny lists where deny beats allow ([code.visualstudio.com](https://code.visualstudio.com/docs/enterprise/manage-ai-settings), checked October 2026). Cursor's allowlist is Enterprise only and does not push servers to anyone ([cursor.com/docs/enterprise/model-and-integration-management](https://cursor.com/docs/enterprise/model-and-integration-management), checked October 2026). Devin Desktop lets a team admin turn MCP off or allowlist servers, and once any server is allowlisted every other server is blocked for the team ([docs.devin.ai](https://docs.devin.ai/desktop/cascade/mcp), checked October 2026). Codex reads an approved `mcp_servers` list from `requirements.toml`, where a key that is present but empty disables every MCP server ([learn.chatgpt.com](https://learn.chatgpt.com/docs/enterprise/managed-configuration), checked October 2026).

## When the inventory is the whole job

If the sweep returns four local servers on two laptops and none of them holds a company credential, you are done. Set the client policies, keep the script on a monthly schedule, and spend the budget elsewhere. A governed endpoint earns its place when people are reaching company SaaS accounts from an assistant, not when two developers are reading local files.

The decision changes when the same list shows a personal token in a config file, or a server nobody can name the owner of, or one app connected from three different clients. At that point the work is consolidation, and [the case for waiting](/blog/when-you-dont-need-an-mcp-gateway/) no longer holds.

For the replacement step in detail, see how to [swap personal servers on laptops for one endpoint](/blog/replace-personal-mcp-servers/). For what the browser-side evidence does and does not prove, read [the shadow AI browser extension log](/blog/shadow-ai-it-admin-browser-extension-log/), and for the layer above it, [what CASB and DLP can see](/blog/casb-dlp-vs-governed-mcp-endpoint/). The architecture behind the single address is covered in [the MCP control plane explainer](/blog/what-is-an-mcp-control-plane/). Check which servers on your list have a maintained equivalent in the [connector catalog](/connectors/).

## FAQ

### Where does each AI client store its MCP server list?

As of October 2026: Claude Desktop uses ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows, under the mcpServers key. Claude Code uses ~/.claude.json for local and user scope and .mcp.json at a repository root for project scope. Cursor uses ~/.cursor/mcp.json and .cursor/mcp.json. VS Code with Copilot uses .vscode/mcp.json and a user-profile mcp.json, both now deprecated in favor of .mcp.json at the project root and ~/.copilot/mcp-config.json. Devin Desktop, formerly Windsurf, uses ~/.config/devin/mcp_config.json or %APPDATA%\devin\mcp_config.json, with the older ~/.codeium/windsurf/mcp_config.json still named in its docs. Codex uses ~/.codex/config.toml with one [mcp_servers.<name>] table per server.

### Can an admin console show which MCP servers employees added on their own laptops?

No client vendor documents an admin inventory of the servers in members' local configuration files. Admin consoles report servers that connect, or are used, through the vendor's own surface. Anthropic's Compliance API emits integration_user_connected events with integration_type mcp, Claude Code can emit claude_code.mcp_server_connection over OpenTelemetry, and Cursor's Enterprise audit log carries mcp_server_config and mcp_authentication events. Finding local servers means reading the configuration files on each device with an MDM or EDR script.

### Is it safe for an inventory script to upload the whole config file?

No. Parse the file and collect only the server entries. Claude Code's ~/.claude.json holds the sign-in session alongside its mcpServers object, so uploading the whole file uploads a credential. For an env block inside a server entry, record the key names and not the values, since those values are often long-lived API tokens.

### Does removing an MCP server revoke the person's access to the third-party app?

No. Removing a server or a connector ends access through the AI client and nothing more. The person's account inside each app still exists, and it is deprovisioned in that app or through your identity provider. Rotate any credential the server config held, because deleting the entry does not invalidate the token it contained.

### What should replace an MCP server that holds a personal API token?

A governed endpoint where the credential is not in the client at all. Elaichi serves every connected SaaS account through one organization-wide MCP endpoint at https://api.elaichi.ai/mcp, behind OAuth, with no per-user URL and no token to paste into a config file. Each member connects once and signs in with their own grant, and revoking that grant, removing the member or suspending them takes effect on the next call.

## Read next

- [Replace personal MCP servers on employee laptops](/blog/replace-personal-mcp-servers/) — Find the MCP servers employees run from each client's config file, then replace personal MCP servers and their tokens with one governed endpoint.
- [Shadow AI browser extension log: what it proves](/blog/shadow-ai-it-admin-browser-extension-log/) — A shadow AI browser extension log proves which AI extensions are installed, where, and which sites they asked to read. It cannot show what anyone pasted.
- [CASB and AI agents: what each layer can see](/blog/casb-dlp-vs-governed-mcp-endpoint/) — CASB and AI agents: a CASB sees who reached which AI service and how much data moved, but not which tool an agent ran or which account it changed.
