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, 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, 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.jsonon macOS,%APPDATA%\Claude\claude_desktop_config.jsonon Windows, with servers under themcpServerskey. Anthropic gives no Linux path. Server names also appear in the log directory,~/Library/Logs/Claudeor%APPDATA%\Claude\logs, as onemcp-server-SERVERNAME.logper server (modelcontextprotocol.io, checked October 2026). - Claude Code. Local and user scope live in
~/.claude.json. Project scope lives in.mcp.jsonat the repository root. On a device you can also runclaude mcp list(code.claude.com/docs/en/mcp, checked October 2026). - Cursor.
~/.cursor/mcp.jsonglobally and.cursor/mcp.jsonper project (cursor.com/docs/mcp, checked October 2026). - VS Code with GitHub Copilot.
.vscode/mcp.jsonwith a top-levelserverskey, plus a user-profilemcp.json. Both are now labeled deprecated in the Add Server flow, which steers new servers to.mcp.jsonat the project root and~/.copilot/mcp-config.json. Sweep both generations (code.visualstudio.com, checked October 2026). - Devin Desktop, which was Windsurf.
~/.config/devin/mcp_config.json, or%APPDATA%\devin\mcp_config.jsonon Windows. Devin's docs still name the older~/.codeium/windsurf/mcp_config.json, so check both (docs.devin.ai, checked October 2026). - Codex.
~/.codex/config.toml, one[mcp_servers.<name>]table per server, plus a project.codex/config.tomlin trusted projects only. One file covers the ChatGPT desktop app, the Codex CLI and the IDE extension (learn.chatgpt.com, 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, 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, checked October 2026). Cursor's Enterprise audit log carries mcp_server_config and mcp_authentication events (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, checked October 2026). GitHub's usage metrics report MCP use for Copilot CLI only (docs.github.com, 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.
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.
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, 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, 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, checked October 2026). Cursor's allowlist is Enterprise only and does not push servers to anyone (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, 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, 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 no longer holds.
For the replacement step in detail, see how to swap personal servers on laptops for one endpoint. For what the browser-side evidence does and does not prove, read the shadow AI browser extension log, and for the layer above it, what CASB and DLP can see. The architecture behind the single address is covered in the MCP control plane explainer. Check which servers on your list have a maintained equivalent in the connector catalog.