How to Evaluate an MCP Server Before You Install It
Before you install an MCP server, check who publishes it, install an exact version instead of latest, and read every tool it offers before your AI app sees them. Then give it the least access it needs: its own credentials, one project folder, and no network if it doesn't need one. No check proves that a server is safe, because nothing you can inspect guarantees what the code will do. These steps limit what a bad server can reach, and a scanner adds one more signal.
Before you start
- Node.js 22.19 or later, which MCP Inspector needs.
- uv if the server you're checking is a Python package.
- The name of the server's package, and the link to its source repository.
Ask whether it needs to run on your machine
A local server runs as your user account. It inherits your environment variables and can read your files and open network connections. The MCP project's local server security guide makes the point that a server which only talks to an online service gains nothing from running on your laptop.
So check for a remote version first. A server offered over Streamable HTTP runs no code on your machine. If the server needs your files, a local tool or some hardware, it has to run locally, and the rest of this guide applies in full. Local vs remote MCP servers explains the difference.
Check the publisher and the package name
Work out who wrote the server and whether the package you're about to install really comes from them.
| Check | Good sign | Warning sign |
|---|---|---|
| Publisher | The package links to a source repository and a named maintainer or organization | No repository, or one that doesn't match the package |
| Activity | Recent releases that fit the project | A quiet project that suddenly publishes a new version under a new owner |
| Name | Matches the name in the vendor's own docs | A name one letter off a known one. The OWASP MCP cheat sheet gives mcp-server-filesytem as a lookalike for mcp-server-filesystem |
| Source | Readable code that builds into the published package | Only a compiled bundle with no source |
The reference filesystem server, for example, is published from the modelcontextprotocol/servers repository, and its npm listing links to it. Install from the name in the official docs and check that the package page points back at the same repository.
Pin the exact version
Install a specific version, not latest. Without a pin, the next release can arrive the next time your app starts the server.
A pin doesn't cover everything. With npx or uvx, the pin fixes only the top-level package. Its dependencies are usually resolved fresh each time, and a compromised dependency can arrive that way. Pinning a container image by digest (image@sha256:...) freezes the whole dependency tree.
Here is a config entry for the reference filesystem server, with the version pinned to 2.0.0. The last argument is the only folder the server is allowed to use:
{
"mcpServers": {
"project-files": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem@2.0.0",
"/Users/you/projects/my-app"
]
}
}
}
The server's npm metadata declares that version 2.0.0 needs Node.js 24 or later. Check that with npm view @modelcontextprotocol/server-filesystem@2.0.0 engines. On an older Node.js, npx prints an EBADENGINE warning. We started the server on Node.js 22.16 with that warning and the tools worked.
A pin also means you update on purpose. When a security advisory comes out for a version you pinned, the pin is what keeps you on the vulnerable version, so check your pinned servers regularly and move them forward when a fix ships.
Read every tool before the model does
Your AI app sends each tool's name, description and parameter schema to the model, and the model reads them as instructions. Installing a server doesn't review that text. Read it yourself.
MCP Inspector can list a server's tools without a model involved:
npx -y @modelcontextprotocol/inspector@2.11.0 --cli npx -y @modelcontextprotocol/server-filesystem@2.0.0 /Users/you/projects/my-app --protocol-era modern --method tools/list
Inspector starts the server to do this, so run the command in a sandbox if the server is unfamiliar. The output is JSON. Here is one tool from the filesystem server, trimmed:
{
"name": "read_file",
"description": "Read the complete contents of a file as text. DEPRECATED: Use read_text_file instead.",
...
"annotations": {
"readOnlyHint": true,
"openWorldHint": false
}
}
Read each description and schema against the server's job. These are the things that should stop you:
- Instructions aimed at the model. A description that tells the model what to do with other tools, asks it to hide something, or says to ignore earlier instructions has nothing to do with the tool's job. The local server guide says to remove the server in that case.
- Hidden characters. Zero-width characters and bidirectional text overrides can hide instructions from a person reading the description. The mcp-guard rule set flags them as a tool-poisoning technique.
- Free-form input that reaches a shell, a database query or a URL. The OWASP cheat sheet says never to pass raw shell commands or unsanitized file paths to a server, and to treat URLs from the model as untrusted.
- Descriptions that reach into another server. A tool description can steer how the model uses tools from a different, trusted server. The OWASP cheat sheet calls this tool shadowing, and it's why each server's list is worth reading alongside the others.
- Annotations you take at face value. Annotations such as
readOnlyHintare hints the server sets about itself. The OWASP cheat sheet says clients should treat them as untrusted unless they come from a trusted server.
Work out what access it gets
Most of the damage a bad server can do depends on what it can reach. Go through each of these before you connect it:
| Access | What to check |
|---|---|
| Environment variables | Pass each server only the variables it needs, in its own config entry. Don't rely on whatever your shell profile exports, because a local server inherits it |
| Credentials | Give the server its own token with the smallest scope that works. Don't reuse a broad personal token across servers |
| Files | Grant the narrowest folder, read-only if the job allows. Directories such as ~/.ssh and ~/.aws hold credentials too |
| Network | A server that doesn't need the network should run with none |
| Commands | Find out whether the server starts other programs, and what they are |
The filesystem server's last argument is an allowed-directory guardrail. We tried it: asking for a file outside the allowed folder returned an error from the server.
Access denied - path outside allowed directories: /etc/hosts not in /tmp/eval-mcp-demo, ...
That refusal comes from the server's own code. It stops an honest server from wandering, but a malicious server can ignore it. The real boundary has to come from outside the server, such as a container that mounts only that folder. Run local MCP servers in a sandbox covers how to set that up.
Scan it, and know what the scanner checks
Scanners look for patterns they know about. A clean report means the server didn't match those patterns, and it doesn't clear the server. Two open-source tools cover different parts of the job.
| snyk-agent-scan | mcp-guard | |
|---|---|---|
| What it reads | The MCP servers in your AI app configs, plus their tool descriptions | The source code of a server and an optional tool list |
| Does it run the server? | Yes. It starts stdio servers to read their tools. An interactive scan asks you to consent to each one, and --dangerously-run-mcp-servers skips that prompt | No. It reads files and never executes the code it scans |
| Network | Needs a Snyk API token, and its analysis runs against the endpoint set by --analysis-url | Offline |
| Checks named in its docs | Prompt injection, tool poisoning, tool shadowing, toxic flows | Tool poisoning, hidden Unicode, command injection, hardcoded secrets, path traversal, excessive permissions |
| License | Apache-2.0 | MIT |
snyk-agent-scan was previously published as mcp-scan. The PyPI page for mcp-scan now says the package was renamed to snyk-agent-scan and redirects to it, so older posts and the OWASP cheat sheet may still use the old name.
Run snyk-agent-scan against a config file with uvx:
uvx snyk-agent-scan@latest ~/.vscode/mcp.json
Its README warns that scanning a config can start the servers in it and make outbound requests, and it recommends running scans in a sandbox when the config comes from someone else.
mcp-guard works on a server's source code, so clone the repository first and point it at the folder:
npx @m8t-jacob/mcp-guard scan .
It's a small project, so check its recent activity before you rely on it in a pipeline. Use it as one input next to your own reading of the code.
Watch for changes after you install
A server can change its tool definitions after you approve it. The OWASP cheat sheet calls this a rug pull, and a one-time review doesn't catch it. Save the tool list when you approve the server, then compare it after every update:
npx -y @modelcontextprotocol/inspector@2.11.0 --cli npx -y @modelcontextprotocol/server-filesystem@2.0.0 /Users/you/projects/my-app --protocol-era modern --method tools/list > approved-tools.json
Run the same command after an update, saving to new-tools.json, and then compare the two files with diff approved-tools.json new-tools.json. A difference in a description, a schema or an annotation means the server changed what it offers. Re-read the change before you keep using it.
Hash the whole definition, not just the description. The OWASP cheat sheet says a changed readOnlyHint or destructiveHint can change how a client approves a call without any change to the text. Some clients ask you again when a tool changes, and that's a useful thing to pick for.
Decide, and write down what you decided
Before you connect a server, you should be able to answer each of these:
- Does it need to run on my machine, or is there a remote version?
- Can I name the publisher and the source repository?
- Am I installing a pinned version, and do I know its Node.js or Python requirement?
- Have I read every tool's description, schema and annotations?
- Does it get only its own credentials, one folder and the network it needs?
- Did I run it in a sandbox if I didn't trust it yet?
- Did I save the tool list so I can compare it after an update?
If you can't answer one of them, leave the server out until you can.
FAQ
Is an MCP server safe to install?
It depends on who wrote it and what it can reach. No check proves what a server will do, but you can identify the publisher, pin the version, read the tool list and give the server only the access it needs. Those steps limit the damage a bad server can do.
Can a local MCP server read my files?
Yes. A local server runs as your user account, so it can read whatever that account can read. Passing a folder in its arguments is a guardrail for honest servers. A container that mounts only that folder is the boundary that holds against a malicious one.
Are there open-source MCP scanners?
Yes. snyk-agent-scan, which is Apache-2.0 licensed, reads the tool descriptions of the servers in your AI app configs. mcp-guard, which is MIT licensed, checks a server's source code without running it. Neither replaces reading the tool list yourself.
Should I use a remote MCP server instead of a local one?
If the server only calls an online service and does not need your files or local tools, its remote version is usually the lower-risk choice. It runs no code on your machine, so there is less to trust and less to update.
- MCP security best practicesThe threat model for users and builders, with a checklist for each.
- Tool poisoning and rug pullsHow malicious tool descriptions and changed definitions work, and how to defend against them.
- Sandbox local MCP serversRun a local server with only the files and network it needs.
- Local vs remote MCP serversWhen a server should run on your machine and when it should run remotely.
- Find MCP serversWhere to look for official and vendor servers before you evaluate them.