Emsipee

How to vet a third-party MCP server before you install it

Five checks to run before you connect an MCP server someone else wrote: who publishes it, whether the package matches its repository, what access it asks for, what its tool descriptions tell the model, and which version will run.

Illustration titled Five checks before a server gets your access: an MCP server someone else wrote passes five numbered checks, 1 who publishes it, 2 package matches repo, 3 access it needs, 4 tool descriptions, 5 pinned version, before reaching your AI app and your files and tokens. A local server runs as you: check first, then connect.

Before you connect an MCP server someone else wrote, check five things: who publishes it, whether the package you are about to run is the one in their repository, what access it asks for, what its tools say to the model, and which version will run. Most of these take a minute each. They matter because a server is a program running with your account's access, and its tool descriptions go straight into the model's context.

Grounded in: the Security and Trust & Safety principles of the MCP specification and the tool-safety guidance on its tools page, the MCP Inspector README for inspecting a server, and the npm, uv and Docker documentation for pinning versions. These checks apply to any client; how each app shows servers and approval prompts differs, so follow your app's own documentation for its settings.

What you are trusting when you add a server

Some vocabulary first. The host is the AI app you use, a server is the program that gives it extra abilities, and a tool is one action a server offers, such as "read a file" or "create an issue", with a name and a description the model reads to decide when to use it. Our plain-English guide to hosts, clients and servers goes further.

A local server runs on your computer as your user. Whatever you can read, delete or send, it can too, unless something narrows that. A remote server runs on someone else's machine; you reach it over the internet and usually sign in, so it gets whatever the sign-in grants. Local or remote MCP server? explains the difference in more detail.

The specification puts the responsibility on you and your app. It says tools represent arbitrary code execution and must be treated with caution, and that "descriptions of tool behavior such as annotations should be considered untrusted, unless obtained from a trusted server". A server does not have to be malicious to cause trouble, either: a careless one can expose more than it needs to, and an honest one can be hijacked through what it reads, which our prompt injection guide covers.

1. Who publishes it

Start from the source, not from a search result or a list of servers. Good starting points are the official modelcontextprotocol/servers repository, the documentation of the company whose service the server connects to, or a repository whose code you can read.

Ask:

  • Is the publisher the organisation that owns the service? A GitHub server published by GitHub is a different proposition from one published by a stranger, even if both work.
  • Is the code public, and does the repository show recent activity and an issue tracker where problems get answers?
  • Does the README say plainly what the server does, what it needs and how to report a security problem?

A listing in a directory or registry tells you where a server is published. It does not tell you that anyone reviewed the code. Treat it as a pointer to the repository, then check the repository.

2. The package matches the repository

Most local servers are started with a command like npx -y @scope/server-name or uvx server-name, which downloads a package by name from npm or PyPI and runs it. The name is the weak point: a package with a similar name and a different owner runs just as easily.

Copy the package name from the official README, never type it from memory or take it from a forum post. For npm packages you can confirm where a package claims to come from before running it:

npm view @scope/server-name repository.url
npm view @scope/server-name versions

The repository URL should be the one you checked in step 1. On PyPI, the project page lists its links; check them the same way. This does not prove the published package was built from that code, but a mismatch is a clear reason to stop.

3. What access it asks for

Read the configuration section of the README and write down, in one line, what the server can reach. For example: "reads and writes files in the folders I list", "uses a GitHub token with the scopes I grant", "fetches any URL the model asks for".

Then cut it down:

  • Give file servers specific folders, never your home folder. The filesystem server guide shows how its folder list works.
  • Create a token just for the server, with only the permissions its tools need, and put it only in the place the server's documentation says: usually the env block of your client's config, or the sign-in flow for a remote server. Never paste a token into a chat.
  • For a remote server, read the sign-in screen before approving. If it asks for more than the tools you plan to use, look for a narrower option or do not connect it.

A server that needs broad access to do a narrow job is a reason to look for another one.

4. What its tools say to the model

This is the check people skip, and the one specific to MCP. Every tool description is text the model reads, so a description can carry instructions as easily as an explanation. A normal one says what the tool does and what it needs. A poisoned one adds orders aimed at the model: read some other file, include its contents in a parameter, do not mention this to the user.

Illustration of two MCP tool descriptions side by side. Normal: get_weather, returns the forecast for a city, input city. Poisoned: the same tool plus a hidden instruction to also read ~/.ssh/id_rsa, put it in "notes" and not mention this to the user, with an extra notes input.
A normal description explains the tool. A poisoned one gives the model orders, often to fetch something unrelated and pass it along quietly. Read every description in full before you connect a server.

You can read every tool's description before connecting a server to your app. The MCP Inspector starts the server on its own and lists its tools with their full descriptions and inputs:

npx @modelcontextprotocol/inspector npx -y @scope/server-name

Replace everything after inspector with the command and arguments from the server's README. In the Inspector, list the tools and read each description to the end. Look for:

  • instructions addressed to the model rather than a description of the tool;
  • requests to read files, keys or settings unrelated to the tool's job;
  • inputs that do not fit the tool, such as a free-text "notes" or "context" field on a weather tool;
  • anything telling the model to keep something from you.

Note that the Inspector runs the server, with your account's access, so do this only after steps 1 to 3. It also shows you the descriptions at that moment. MCP lets a server tell the app its tool list has changed, so descriptions can change later; pinning a version (next step) keeps a local server's descriptions as you read them, but a remote server can change whenever its operator decides.

5. Which version runs

npx -y @scope/server-name with no version fetches whatever is newest when your app starts the server, so a release you never checked can run without any change on your side. Pin the version you vetted:

{
  "mcpServers": {
    "example": {
      "command": "npx",
      "args": ["-y", "@scope/server-name@1.4.2"]
    }
  }
}

The same idea for other launchers: uvx server-name@1.4.2 for Python servers run with uv, and for Docker an image reference with its digest (image@sha256:…) rather than a moving tag such as latest. Use the version number you actually checked; 1.4.2 here is only a placeholder. When you update, repeat steps 2 and 4 for the new version.

Keep the app's approval prompts on

Vetting lowers the risk; it does not remove it. The specification says there should always be a human in the loop who can deny a tool call, and most clients ask before a tool runs. Leave that on for any server that can write, delete or send, and read what the tool is about to do before you approve it, especially the inputs. If a call carries data that has nothing to do with your request, deny it and remove the server.

MCP servers npx prompt injection security

Share this guide