Skip to content
All posts
8 min readBrian McManus

How to find every MCP server on your developer laptops

An MCP server inventory means reading each client's config on each machine, then keeping that read current. Here are the paths, the manual sweep, and where the sweep stops working.

mcpshadow-mcpinventoryendpoint

Finding every MCP server on a fleet means reading the configuration file of each MCP client on each machine, because nothing central holds that list. As of November 2026 the clients you are most likely to meet on a developer laptop are Claude Code, Claude Desktop, Cursor, the Codex CLI, VS Code with GitHub Copilot, and the Copilot CLI, and every one of them keeps its servers somewhere different.

Where each client keeps its servers

ClientConfigurationWhat to expect inside
Claude Code~/.claude.json for local and user scope, <repo>/.mcp.json for project scope, managed-mcp.json in the system directory when an administrator deploys a fixed setAn mcpServers object. Local and user scope entries under ~/.claude.json can sit inside a per-project projects map
Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows, ~/.config/Claude/claude_desktop_config.json on LinuxA single mcpServers object
Cursor~/.cursor/mcp.json for global scope, <repo>/.cursor/mcp.json for project scopeA single mcpServers object
Codex CLI~/.codex/config.toml for user scope, <repo>/.codex/config.toml for a trusted project[mcp_servers.<name>] tables in TOML
VS Code and GitHub Copilot<workspace>/.vscode/mcp.json, the user profile mcp.json, or a workspace-root .mcp.jsonservers in the VS Code format, mcpServers in a portable file
Copilot CLI$COPILOT_HOME/mcp-config.json, or ~/.copilot/mcp-config.json when COPILOT_HOME is unsetA portable mcpServers object

Anthropic documents the Claude Code scopes and the claude mcp add commands in Connect Claude Code to tools via MCP. The MCP project documents Claude Desktop's file and the mcpServers shape in Connect to local MCP servers. Cursor documents its two files in Model Context Protocol, OpenAI documents the user and trusted-project config.toml scopes and the codex mcp add command in Codex MCP, and GitHub documents the VS Code and CLI destinations in Add MCP servers to VS Code.

The manual sweep

Start with the file names. From a single machine's home directory:

find ~ -maxdepth 5 \
  \( -name 'claude_desktop_config.json' \
     -o -name '.mcp.json' \
     -o -name 'mcp.json' \
     -o -name 'config.toml' \
     -o -name 'mcp-config.json' \) \
  2>/dev/null

That over-collects. config.toml also turns up Rust and Python project files, so filter for the key you actually care about:

grep -rl --include='*.json' -e '"mcpServers"' -e '"servers"' ~ 2>/dev/null
grep -rn -e '^\[mcp_servers' ~/.codex/config.toml 2>/dev/null
# trusted-project scopes, run from the directory that holds your repositories
grep -rn --include=config.toml -e '^\[mcp_servers' . 2>/dev/null | grep -v node_modules

Read one entry and you can see what you are dealing with:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/ana"],
      "env": { "GITHUB_TOKEN": "ghp_…" }
    }
  }
}

Two facts live in those six lines. The server is stdio, so it runs as a child process of the client. It is launched with npx -y and no version, so which version runs depends on what is already on the machine: npm resolves an unqualified name against the copy installed in the current project when there is one, and otherwise puts the package in the npm cache (npm exec). The -y only suppresses the install prompt.

Why the sweep stops working

The commands above answer the question once, on one machine, on one afternoon. The reasons they do not scale are structural rather than cosmetic.

Stdio servers never cross the network. A stdio MCP server is a pipe between two processes on the same host. Your gateway sees nothing because nothing is routed. Your network logs show nothing because no packet left the machine. The only evidence is on disk, in a config file.

An unpinned npx -y entry is not reproducible. Nothing in the config line names a version, so two machines can run different code from the same file, and a scanner that matches installed packages to a CVE feed has a cache entry, not a pinned dependency it can reason about. Forcing a prompt away with -y says nothing about which version you get. A version in the args is what makes the launch repeatable.

The formats differ. JSON here, TOML there. mcpServers in a portable file, servers in the VS Code format, [mcp_servers] in Codex. A parser written for one client returns nothing on the next.

Configuration is scoped and layered. A server can be defined at project scope in a repository you do not have checked out, at user scope in a home directory, or pushed by an administrator through a managed file. Claude Code alone reads four settings scopes plus managed sources, as the settings reference lists.

The file changes between the sweep and the report. A developer adds one stanza to reach a staging database in under a minute. Nothing records the change, because there is no change process for a five-line edit to a JSON file.

A gateway only inventories what routes through it. A gateway that fronts your model calls and remote MCP traffic can list servers registered with it. The filesystem entry above is not registered with it and will never appear.

What the inventory has to record

A list of server names is not enough to act on. For each endpoint, record the client and its version, the config path and the scope it came from, the server name, the transport (stdio, HTTP, SSE, or WebSocket), the launch command or URL that will actually run, whether the version is pinned, and the first and last time the server was seen. With those fields you can answer the questions that reach a security team: which endpoints run unpinned servers, which servers nobody approved, and what changed since last week.

That record only becomes governance when something can act on it. A pinning rule needs to attach to a server identity. An allowlist needs to know which client would launch the server. A block has to run before the process starts, not after the call is logged.

Where Alyria Beacon fits

Alyria Beacon is a signed, user-space daemon that runs on macOS, Linux, and Windows with no kernel driver. It inventories the AI tooling on each endpoint and the MCP server configuration each client holds, including stdio servers that never touch the network, and it keeps that inventory current on a schedule rather than at the moment someone remembers to run a search.

From that inventory it flags the hygiene problems a manual sweep finds and cannot maintain: unpinned servers, down-level AI tools, credentials sitting in plaintext in a config file, and agents running outside sanctioned accounts. The same inventory is the input to policy. Lyra policy is evaluated locally on every MCP call, at the point the call is made, so a rule about an unbrokered server applies to a stdio server with no network path as readily as to a remote one. Enforcement is opt-in and starts in monitor mode, so you can see what your real usage would have triggered before anything is blocked. Every decision lands in one signed audit chain rather than in per-client log files.

To see the inventory before committing to any policy, start with the Agent Exposure Report, which lists every agent, version, and MCP connection on a pilot cohort. The module page is /platform/beacon and the operational detail, including the three enforcement planes, is in the Alyria Beacon docs.

A day-one baseline

You do not need a policy framework to begin. Five rules, run in monitor mode, cover most of the exposure an inventory turns up:

  1. Pin MCP server versions. No latest, and no unreviewed npx -y entry.
  2. Keep a sanctioned-server list. Start by flagging off-list servers rather than blocking them, and let the flags build the list.
  3. Remove plaintext credentials from client configs and issue them from a broker instead.
  4. Require sanctioned accounts. An agent should run under an identity your IdP knows.
  5. Monitor for two weeks before enforcing. Enforce per rule and per cohort once the false-positive rate is uninteresting.

Each rule is dependency, secret, and account hygiene you already apply elsewhere, applied to the one layer that arrived without an onboarding process.

Frequently asked questions

Do I need an agent on every laptop to inventory MCP servers? To inventory them continuously and act on the result, yes. Client configuration is local by design, and stdio servers leave no network trace, so a network or gateway position cannot see them.

Can I read MCP configuration from my MDM instead? MDM can read files on managed devices, but it will not parse four client formats, resolve which scope won, or connect the result to enforcement. It is a transport for the file, not an inventory of the servers.

Is a server that only reads files still worth inventorying? Yes. The filesystem example above was granted the home directory of the user who runs it, which includes .ssh, .aws, and every .env file in every project on that machine.

How is your team using AI agents?

Pick your function and answer 20 questions on agent workflows and controls. Your result appears on the page.

Governed AI agents for the work your teams do.