How to Vet an MCP Server Before You Install It: 7 Checks
Seven checks to run on an MCP server before it goes into your client: who published it, how it runs, whether it answers, what its tools can do, and how to pin it. Each with a command and a measured figure.
Vetting an MCP server means answering seven questions before it goes into your client: who published it, how it runs, whether it answers, what its tools say they do, how well they describe themselves, how it signs you in, and whether it can change after you install it. Each one below comes with a command you can run in a minute and a figure from servers we called ourselves.
A star count answers none of them. A popular server can still be a package that runs with your permissions and a tool list that never says which calls delete things.
TL;DR:
- Check the publisher in the official registry. A name like
io.github.someone/serveris tied to a verified GitHub account or domain [1]. - Know how it runs. A package runs on your machine with your permissions; 10,630 of 23,828 registry servers (44.6%) ship only that way (census).
- Call it once. 139 of 1,100 registry addresses (12.6%) did not answer as an MCP server when we called them on 20 August 2026.
- Read the tool list, not the README. Only 49 of 262 annotated tools in our sample said whether they are destructive.
- Pin the version. A server can change its tools after you approve them.
These checks tell you what a server is and what it claims. They are not a security review: for that, read its source.
1. Who published it?
The MCP Registry names every server in reverse-DNS form, and only the owner of that GitHub account or domain can publish under it [1]. That makes the name the quickest publisher check there is. io.github.acme/crm was published by whoever controls the acme GitHub account; com.acme/crm by whoever controls acme.com.
Look a server up by name and print who published it, which version, and where its source lives:
curl -sS "https://registry.modelcontextprotocol.io/v0.1/servers?search=postgres&version=latest&limit=20" \
| jq -r '.servers[].server | "\(.name)\t\(.version)\t\(.repository.url // "no repository listed")"'
What to look for:
- The namespace matches the company you think you are installing. A server for a well-known product published under an unrelated personal account is somebody's wrapper, which may be fine, but you should know.
- A repository is listed. On 14 October 2026 the first five results for
postgresall printedno repository listed. That is allowed, and it means the next steps carry more weight.
The registry checks ownership of the name. It leaves security scanning to the package registries and to the directories built on top of it [1].
2. How does it run?
Every registry entry says how it is delivered. A remotes entry is an address your client connects to: the code runs on someone else's machine and sees only what you send it. A packages entry is an npm, PyPI, NuGet or Docker package your client starts on your machine, with your files, your environment variables and your network.
Remote is not the norm. When our registry census opened, 10,630 of 23,828 servers (44.6%) shipped only as a package. Those deserve the closer read: check the publisher, the last release and the source link before you let npx or uvx run it. For an npm package, one command prints all of it:
npm view @modelcontextprotocol/server-filesystem version time.modified repository.url deprecated
A deprecated line is the one to catch. On 14 October 2026, @modelcontextprotocol/server-postgres printed Package no longer supported and no source link.
3. Does it answer?
A listing records that someone published an entry. It does not record that anything answered. For a remote server, call it once before you configure anything.
Paste the address into our MCP connection test. It runs the same handshake your client would, then reports the protocol revision, the tools that came back and how long the answer took, or the exact reason it failed. To do it by hand, the curl guide sends the same requests.
On 20 August 2026 we called 1,100 remote addresses from the registry, once each. 139 of them (12.6%) did not answer as an MCP server: HTTP 404s, payment walls, server errors and refused connections (census). MCP server errors explains what each failure means.
4. What can its tools do?
The tool list is the server's own statement of what your model will be allowed to call. Save the tools/list answer to a file (the curl guide shows the request) and print each tool's two safety hints:
jq -r '.result.tools[] | [.name, (.annotations.readOnlyHint | tostring), (.annotations.destructiveHint | tostring)] | @tsv' tools.json
Run against our own server on 14 October 2026, it printed:
search_mcp_servers true null
get_mcp_server true null
weekly_leaderboard true null
check_mcp_server null null
get_check_result true null
mcp_ecosystem_stats true null
null means the server did not say. For destructiveHint that matters, because the specification's default is "may be destructive": a client that reads the hint treats an unannotated tool as one that might delete or overwrite something. Tool annotations has all four hints and their defaults.
Unset is the usual case. In the 288 tools that 14 public servers returned in our sample, 262 carried annotations, and only 49 of those said whether they were destructive. 5 tools in the whole sample declared themselves destructive outright.
Two things to do with that list:
- Count the tools that write. Anything named
delete_,send_,update_,execute_ortransfer_acts on your behalf. Decide whether your client should ask before each call. - Do not trust the hints as a guarantee. The specification says clients "MUST consider tool annotations to be untrusted unless they come from trusted servers" [2]. A hint is a claim by the server about itself.
5. Do the descriptions say what each tool does?
Your model chooses tools by reading their descriptions, so a vague description is a server that will be called at the wrong moment. Print each description's length beside its tool:
jq -r '.result.tools[] | "\(.description | length)\t\(.name)"' tools.json | sort -n
Read the shortest ones in full. In our sample, 17 of 288 descriptions were under 40 characters and 30 were over 1,000. A short one rarely says when to use the tool; a very long one spends your model's context on every request. MCP tool descriptions shows what good ones look like, with real examples.
While you are reading, look for instructions aimed at the model rather than at you: "always call this first", "do not tell the user", or anything about other servers' tools. A description is text your model reads as context, which is the opening that tool-poisoning attacks use [3].
6. How does it sign you in?
A 401 from a remote server is a good sign, not a failure: it is a server refusing to answer a stranger. 263 of the 1,100 registry addresses we called (23.9%) asked for credentials. What matters is how:
- OAuth (your client opens a sign-in page) lets you see and limit what you grant. MCP OAuth covers what a well-behaved server publishes.
- An API key in a header or environment variable gives the server whatever that key can do. Create a key with the narrowest scope the provider offers, used for this server only, so you can revoke it alone.
For a package server, read which environment variables it asks for. A server that wants a broad token for a job that needs read access is asking for more than it uses.
7. Can it change after you install it?
You approve a server once, and the server keeps running after that. Two things can change under you:
- The package. An unpinned
npx -y some-serverfetches whatever version is newest each time your client starts. Pin it to the version you checked:npx -y [email protected]. - The tool list. A server that declares
"listChanged": truein its capabilities is telling you its tools can change while you are connected [2]. A server that changes its tools after you approved it is the pattern known as a rug pull [3].
Re-run step 4 after an update and compare the two files. A new tool that writes, or a description that grew a paragraph, is worth a second look before you keep going.
What these checks do not cover
All seven read what a server says about itself and how it answers. None of them reads the code behind it or watches what a tool does with your data once you call it. For a package server with access to anything that matters, that means reading the source, or running it in a container with no access to your home directory and only the network it needs.
The same bound applies to every listing on MCP Hunter: a listing that carries a connection record says what the server returned when we called it, on the date printed, and nothing more.