Every local MCP server on a developer laptop is a program that an AI client starts with the developer's own permissions, often fetched from a package registry at launch and handed API tokens in plain text, and most security teams cannot say how many there are. This guide is a hands-on audit of MCP servers on developer machines: where each common client keeps its configuration on macOS, Windows and Linux, which fields to collect, how to spot secrets and risky launch commands, how to rate what you find, and how to remove or replace servers without breaking a developer's workflow. It is for endpoint, application security and platform teams, and for developers checking their own machine. The principles behind the controls are in the Local MCP Server Security Guide; this page is the procedure.
Key takeaways
- Configuration files are the inventory: local servers are declared in JSON files with a
command,argsandenvper server, in known paths per client and per repository. Collect those files and you have most of the picture. - Scope matters as much as the server: Claude Code alone has local, project and user scopes, with project servers in a committed
.mcp.json. A server in a repository reaches every developer who clones it. - Look for secrets in
envandheaders: client documentation shows API keys passed in theenvblock. Record variable names, never values, and replace literals with references. - Rate by what the launch command does: the MCP specification lists malicious startup commands, malicious packages and DNS rebinding of local servers as the main local attacks. Unpinned package runners, shell chains and listeners on localhost rate highest.
- Remove through the client, then enforce centrally: back up the file, remove the entry, confirm the client no longer starts it, then use managed allowlists and denylists where the client supports them so the server does not come back.
When you need this
- First visibility: before you can approve or block anything, you need to know what is installed. The audit feeds the MCP Server Inventory Template.
- After a malicious server or package report: when a server such as the one in the postmark-mcp malicious MCP server case is reported, you must find every laptop that has it configured within hours.
- Repository review: committed MCP configuration is code that runs on every developer's machine. The Amazon Q MCP config vulnerability showed how opening a malicious repository could put a developer's AWS credentials at risk.
- Developer offboarding or incident response: MCP configuration holds tokens that outlive the person. See the Compromised Developer Workstation Playbook.
Before you start
- Agree the rules with developers: the audit reads files in home directories and repositories. Publish what is collected (server names, commands, arguments, environment variable names) and what is not (values, source code).
- Collection channel: an MDM or EDR script that reads files and returns parsed fields, or a self-service script developers run. Do not copy whole configuration files to a central share: they contain tokens.
- Scope: managed laptops on macOS, Windows and Linux, development virtual machines, and dev containers. Unmanaged personal devices are out of reach of this method; note them as a gap.
- An approved list, even a short one: you will rate findings against it. The MCP Server Trust Assessment covers how servers get onto it.
Step-by-step
1. Know where each client keeps its servers
| Client | File and location | Scope | Top-level key |
|---|---|---|---|
| Claude Desktop | claude_desktop_config.json in ~/Library/Application Support/Claude/ (macOS), %APPDATA%\Claude\ (Windows), ~/.config/Claude/ (Linux) | User | mcpServers |
| Claude Code | ~/.claude.json (local scope, stored under each project's path, and user scope); .mcp.json at the project root (project scope, version-controlled) | Local, user, project | mcpServers |
| GitHub Copilot in VS Code | ~/.copilot/mcp-config.json (or under $COPILOT_HOME); .mcp.json at the workspace root; .vscode/mcp.json in the workspace; mcp.json in the user profile | User, workspace | mcpServers in portable files, servers in the VS Code format |
| Cursor | ~/.cursor/mcp.json (global); .cursor/mcp.json in the project | User, project | mcpServers |
| Other clients | Check each vendor's documentation; for example Amazon Q uses .amazonq/mcp.json | Varies | Varies |
Two gaps to remember. Cursor documents an extension API (vscode.cursor.mcp.registerServer()) that registers servers at runtime without editing mcp.json, so file collection misses those; process telemetry (step 5) catches them. And file paths move between client versions, so recheck the vendor documentation each quarter.
2. Collect the fields (read-only)
- Search each user profile for the files in step 1, and search code directories for project files (
.mcp.json,.vscode/mcp.json,.cursor/mcp.json,.amazonq/mcp.json). - Parse each file as JSON and, for every server entry, record: device, user, file path, scope, server name, transport (
commandfor local stdio servers,urlortypefor HTTP servers),command, fullargs, the names ofenvandheaderskeys, and the file's modification time. - Replace every value in
envandheaderswith a classification before it leaves the device: literal (a value written in the file), reference (for example${API_KEY}, which Claude Code expands from the environment), or empty. Never ship the value. - For a developer auditing their own machine, the client can list what it loads:
claude mcp listshows every configured server with a health status such as Connected or Pending approval, andclaude mcp get <name>shows one server's details. - Pull the same fields from repositories centrally with code search on the project file names. A committed file is one finding that affects every clone.
3. Resolve what actually loads
The same server name can appear in several scopes. Claude Code documents that it uses the highest-precedence entry in full without merging fields: local, then project, then user, then plugin-provided servers, then claude.ai connectors. A harmless user-scope entry can therefore be overridden by a project file in a repository. Record the effective entry per project, not only the list of files.
Also record approval state. Claude Code prompts before using project-scoped servers in interactive sessions, but documents that in claude -p runs and Agent SDK sessions the prompt cannot be shown and project servers load without asking. Scripts and CI jobs that run a coding agent in a cloned repository are therefore the highest-risk place for a committed .mcp.json.
4. Rate each server
| Signal in the entry | Why it matters | Rating |
|---|---|---|
Command chains (&&, ;, pipes), curl or wget, sudo, rm -rf, shell interpreters with inline scripts | The MCP specification gives exactly these as examples of malicious startup commands | Critical: investigate the device |
Package runner (npx -y, uvx) with no version pinned | Fetches and runs whatever the registry serves at each launch | High |
| Package name not on the approved list, or a near-miss of an approved name | Typosquatting and lookalike packages | High |
Literal secrets in env or headers | Plain-text credentials in a home directory file (ATT&CK T1552.001) | High: rotate the credential |
Broad filesystem arguments (home directory, /, C:\) | The server runs with the user's permissions and can reach SSH keys and cloud credentials | High |
| Local server using HTTP on a localhost port without a token | The specification warns that insecure local servers can be reached through DNS rebinding; it recommends stdio, or an authorisation token or restricted IPC for HTTP | Medium to high |
| Project-scope server in a repository | Runs for every developer who opens the repository and approves it, and without prompts in headless runs | Medium; high for public or forked repositories |
| Pinned, approved server with references for secrets and narrow arguments | Expected state | Low |
5. Confirm with process and network telemetry
- Process lineage: in EDR, find child processes of AI desktop apps and IDEs that are package runners (
npx,uvx), interpreters (node,python) ordocker. Compare with the file inventory; anything running without a matching entry was registered another way. - Sensitive file access: alert when a process started by an MCP client reads
~/.ssh,~/.awsor browser profile paths. The MCP specification's own example exfiltrates~/.ssh/id_rsa. - Listeners: list processes listening on loopback ports whose parent is an AI client or IDE. These are the HTTP local servers to check for tokens.
- Client logs: Claude Desktop writes
mcp.logand per-servermcp-server-SERVERNAME.logfiles (in~/Library/Logs/Claudeon macOS,%APPDATA%\Claude\logson Windows,~/.config/Claude/logson Linux). They show which servers connected and when.
The Unsanctioned AI Agents and MCP Servers Guide covers the wider discovery sources (identity provider, CASB, billing) that find remote servers and agents this laptop audit does not.
6. Remediate with the developer (change-controlled)
- Critical findings first: treat the device as possibly compromised. Isolate through EDR, preserve the configuration file and client logs, and follow the MCP Incident Response Playbook.
- Rotate exposed secrets: any credential found as a literal in a configuration file is rotated at the issuing service, not only removed from the file. Replace it with a reference to an environment variable or the client's secret input mechanism, using a token scoped to what the server needs.
- Back up, then remove: copy the configuration file, then remove the entry with the client (
claude mcp remove <name>for Claude Code) or by editing the file. Restart the client and confirm the server no longer appears in its list and no child process starts. - Fix rather than remove where the server is needed: pin the package version (or a container image digest), narrow filesystem arguments to the project directory, and move HTTP local servers to stdio or add a token.
- Repository files: raise a pull request removing or correcting the committed
.mcp.json, and add a code owner rule so future changes need security review. For headless agent runs, Claude Code documentsdisabledMcpjsonServersand the--strict-mcp-configflag to keep project servers out. - Enforce centrally: where the client supports managed settings, deploy them. Claude Code documents managed MCP configuration (
managed-mcp.json,managedMcpServers) andallowedMcpServersanddeniedMcpServersentries matched byserverNameorserverUrl. For GitHub Copilot, VS Code points to the enterprise allowlist policy in GitHub's documentation.
Rollback: restore the backed-up file and restart the client. Keep backups on the device for one review cycle. If a denylist entry blocks a server the business needs, remove that entry in the managed settings and redeploy, rather than letting developers edit around it.
Decision guidance
| Situation | Approach |
|---|---|
| Small team (under 50 developers), one or two clients | Self-audit with a shared script and claude mcp list style commands, results in a shared register, monthly repeat |
| Large estate, several clients | MDM or EDR collection of parsed fields, central code search for project files, managed allowlists in clients that support them |
| Regulated data on laptops | Default-deny through managed settings; local servers only from an internal registry, pinned and run in containers |
| Headless agents in CI or scripts | No project servers by default (strict configuration), explicit per-job server list, tokens from the CI secret store |
Verify it worked
The targets below are NHI Mgmt Group recommendations, not vendor or regulatory requirements.
| Metric | Source | Target |
|---|---|---|
| Managed developer devices reporting MCP inventory | MDM or EDR collection | Above 95%, gaps named |
| Configured servers not on the approved list | Inventory versus register | Falling to zero |
| Literal secrets in MCP configuration files | Classification in step 2 | Zero |
| Unpinned package-runner servers | Parsed args | Zero for approved servers |
| Repositories with unreviewed project MCP files | Code search | Zero |
| Time to find every device with a named server | Inventory query | Under one hour |
Common failure patterns
- Collecting values: shipping whole configuration files to a central store creates a new collection of developer tokens. Parse on the device and send names only.
- Checking one client: developers often run two or three AI clients, each with its own files and scopes.
- Ignoring repositories: a single committed project file reaches every clone and every headless agent run.
- Removing the entry but not the credential: a token that sat in plain text in a home directory must be rotated.
- Trusting approval prompts: prompts do not appear in headless runs, and approved servers can change behaviour when an unpinned package updates.
- Auditing once: new servers appear weekly. Repeat the collection on a schedule and diff the results.
Where identity fits
Each local MCP server acts with two identities: the developer's operating system account, which decides what files and processes it can reach, and whatever tokens it is given in env or headers, which decide what it can do in SaaS and cloud services. Both are non-human use of credentials with no separate owner. Scope tokens to the server's task, issue them per server rather than reusing a developer's personal token, and record them in your non-human identity inventory. See the MCP Security Guide for the wider model.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST CSF 2.0 | ID.AM-02, PR.PS-02, PR.AA-05, DE.CM-03 | Software inventory; software removed by risk; least privilege; monitoring |
| NIST SP 800-53 Rev 5 | CM-7(5), CM-8, CM-11, IA-5 | Allow-by-exception software, component inventory, user-installed software, authenticator management |
| CIS Controls v8.1 | Controls 2 and 16 | Software asset inventory and allowlisting; application software security |
| OWASP Top 10 for LLM Applications 2025 | LLM03 Supply Chain; LLM06 Excessive Agency | Third-party servers and packages; tools with more reach than the task needs |
| MITRE ATT&CK v20 | T1195.002 Compromise Software Supply Chain; T1552.001 Credentials In Files; T1059 Command and Scripting Interpreter; T1005 Data from Local System | Malicious packages, plain-text tokens, startup commands, local data theft |
Practitioner checklist
- List the configuration paths for every AI client your developers use, per operating system.
- Collect server name, scope, command, arguments and secret key names on the device, never values.
- Search repositories for committed project MCP files and resolve which entry actually loads.
- Rate each server on its launch command, pinning, package source, secrets, arguments and transport.
- Confirm with EDR process lineage, loopback listeners and client logs.
- Rotate any literal secret found, then back up and remove or fix the entry with the developer.
- Disable project servers in headless agent runs and deploy managed allowlists where supported.
- Repeat monthly and diff the results against the approved register.
Standards and references
- Model Context Protocol: Security Best Practices
- Model Context Protocol: Connect to local MCP servers
- Anthropic: Connect Claude Code to tools via MCP
- Visual Studio Code: Add and manage MCP servers
- Cursor: Model Context Protocol
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls
Related NHI Mgmt Group resources: MCP Server Security Checklist, Top 10 MCP Security Risks, MCP Gateway and MCP Server Governance Guide, Sentry MCP Agentjacking 2026.