MCP server security research
What We Found Scanning 1,480 MCP Servers
A scan of the official MCP registry, and what it says about trusting a URL in a config file.
MCP servers are being adopted the way libraries are adopted: one line in a config file, no review, no procurement. What actually gets connected is closer to a browser extension with a credential. It runs inside the agent's loop, it holds a token for whatever system it fronts, and it updates itself without asking.
We wrote a security policy for evaluating one before connecting it, consolidating existing guidance and best practices from the MCP specification, the OWASP MCP Top 10, NSA's MCP guidance and Anthropic's Zero Trust work into numbered, checkable requirements. Then we built a scanner that measures a server against it, without ever installing or executing the server being assessed. It reports two things separately: a trust grade for how the server is built and operated, and a capability rating for what the server can do.
This is what 1,480 servers from the official MCP registry looked like, scanned in July 2026.
| Population | Count |
|---|---|
| Registry entries scanned | 1,480 |
| Hosted (remote) servers | 998 |
| Local (npm/PyPI/GitHub) servers | 166 |
| Declaring neither a package nor an endpoint | 316 |
| Tool list obtained | 654 |
via a live tools/list | 481 |
| via a downloaded package | 173 |
| Tool descriptions and instructions readable | 590 |
| Package available for advisory lookup | 198 |
The registry lists 18,615 servers, so this covers roughly 8% of it. Servers were taken in registry list order rather than sampled randomly, which skews the set toward earlier publications.
What we found
A quarter of the servers we could inspect rate High or Critical capability. Of the 654 whose tool lists we could read, 8% rate Critical and 17% High. These capabilities are usually the reason for connecting the server in the first place. The point is that connecting one grants the ability to change data, not only to read it.
For most servers there is no repository to review. Our scanner could not identify a public repository for 52%, meaning none was declared in the registry entry or in package metadata; we do not go looking for source that was never pointed at. Another 14% name a repository that does not exist or is private. That leaves 25% with a repository that is public, active and carries a license. For roughly two thirds of these servers there is no repository to inspect. For a small number of those a published package can still be read, but that is the exception. For those, trust in the maintainer of a local server, or the operator of a hosted one, is the whole of the assessment.
Being listed on the registry is not a safety signal. Every server here comes from the official MCP registry, which describes itself as "like an app store for MCP servers." Official means canonical, not vetted: it is a Linux Foundation project run by a community working group, still in preview. It verifies that a publisher owns the namespace, that packages carry ownership metadata and come from approved registries, and that the submission matches the schema. There is no security review of submitted servers. It does not even check that the repository URL resolves, which is how 14% of listed servers name a repository that is dead or private, and how one entry here is called REPLACE_WITH_ORG/REPLACE_WITH_REPO. Namespace verification is real anti-typosquatting protection, but it answers who published a server, not whether it is safe.
Most servers are hosted services, not local installs. 67% are remote endpoints rather than something installed and run locally, against 11% installed and run locally. The remaining 21% declare neither a package nor an endpoint, so there is nothing to connect to at all. That changes the question from what the code does on a machine to what a company does with data. A hosted server fronting a CRM receives every customer record the agent queries. One fronting a codebase receives the source it searches. One fronting email receives the threads it reads.
Most hosted servers that answer anonymously expose only read-only tools. 417 of 998 hosted servers answer an unauthenticated request, and 323 hand over a complete tool list. Of those 323, 300 rate Low capability, exposing read-only tools: web_search, get_faq, search_merchants, documentation and directory lookups. The specification permits public servers and these are behaving as intended. The cost is reconnaissance, since a tool list tells anyone who finds the URL exactly what a server can do.
Twenty-three hand a High or Critical capability tool list to anonymous callers. Among them: a 147-tool payments server exposing escrow_create and exec_sandwich, a 133-tool hiring server exposing candidate creation, update and deletion, a 423-tool platform exposing smartSend and createAgent, and others exposing delete_account, create_key, set_webhook and send_outbound_mail. Whether those tools can actually be invoked without credentials was not tested, since doing so would mean calling someone else's production service uninvited. Listing them to strangers is not the same as leaving them open. But it publishes an exact inventory of what is worth attacking, and it is the same code path that would have to be secured separately at invocation.
Authorization discovery is thin, and where it exists it is usually coarse. Surveying 1,311 hosted origins directly, 769 publish no OAuth discovery metadata at all, a year after the June 2025 specification made these servers OAuth 2.1 resource servers. Missing metadata is not the same as missing authorization: many that publish none still demand credentials, just without publishing where to get them. Of the 392 that do declare scopes, 134 offer a single all-or-nothing scope like mcp, 111 offer two or three coarse ones, and 61 declare only identity scopes such as openid. Eighty separate reading from writing, the form that lets a client be granted one without the other, and 22 do it per resource. The mechanism for narrow authorization exists and a small minority of operators use it. Transport is fine: every hosted endpoint used HTTPS.
Known vulnerabilities are rarely observable in this category. Only 13% of servers publish a package that can be queried against advisory databases, and exactly one carried advisories. That reflects what can be seen, not what is there. The check covers the server's own package, not its dependency tree, and for the 67% that are hosted there is no package at all: their dependencies are their operator's problem, invisible from outside and not something a consumer could patch.
Being well built does not predict low capability. 95 servers rate A or B on trust while carrying High or Critical capability. This is why the scan reports two separate ratings rather than one overall score: a single number would average these servers into the middle of the pack, when they are precisely the ones worth the most scrutiny.
We observed two clear cases of prompt injection. Across the 590 servers whose tool descriptions and initialize instructions could be read, eight were flagged and two survived hand review. One tells the model never to name competing products, and has a tool instructing it to generate a report and not show it to the user. The other, a banking server, instructs the model twice to do something and not tell the user. Both are manipulation of what the user sees rather than credential theft. A further 78 servers carried weaker indicators, mostly oversized descriptions, which are worth reading but are not evidence of intent.
What to watch out for
The finding underneath the numbers is that connecting an MCP server is not a one time code review. It is an ongoing relationship with whoever maintains it.
Remote servers can change server-side at any time, and there is no way to confirm that deployed code matches any published repository. Local servers are usually launched with npx -y, which fetches whatever was published most recently on every start. In both cases the software reviewed on Monday is not necessarily the software running on Friday. This is the browser extension problem: approval happens once, updates continue indefinitely.
That makes trust the decision rather than verification, and for most of the registry there is nothing else to go on. With no repository, there is no way to assess the code, the maintainer's track record, or whether anyone is still maintaining it at all.
Local servers are a partial exception. Where a package exists it can be downloaded and read, but what arrives is usually bundled output, dist/ rather than src/. Readable in principle, hostile to review in practice, and the published package need not match the repository anyway.
Capability matters separately because it sets the cost of being wrong. The trust grade measures observable practice: provenance, maintenance, whether the server demands authentication, whether the text it publishes is honest. Those are evidence for a judgment about the people behind it, not a substitute for one. Capability asks what happens if that judgment turns out to be wrong.
Because that data is sent deliberately, the risk is not interception. It is what happens to it afterwards, and most of the failure modes are mundane rather than malicious:
- Logged in plaintext, including credentials passed as tool arguments
- Retained indefinitely, with no stated policy
- Used to train a model
- Stored in a jurisdiction the data is not permitted to leave
- Passed to subprocessors nobody disclosed
- Exposed in a breach of an operator whose security posture is invisible from outside
- Inherited by whoever acquires the company, or orphaned when it shuts down
Deliberate misuse belongs there too, though it is the least likely entry: most operators are not hostile, and nearly all of them keep logs. None of it is observable from outside.
Any other service holding the same data would come with a contract covering exactly those questions. A SaaS vendor with access to customer records goes through procurement, signs a data processing agreement, names its subprocessors, and commits to breach notification. An MCP server fronting the same records arrives as a URL in a config file. The data exposure is comparable and the contractual apparatus is absent.
Existing supply-chain controls do not close that gap either. A dependency scan of an MCP server returns clean for 87% of them because there is nothing to scan, and clean is indistinguishable from unscanned in the output. A server can pass the control that was meant to catch problems without the control having examined anything.
Recommendations
Treat a remote MCP server as a vendor, not a dependency. Data goes to their infrastructure by design, so the questions are the usual ones: who runs it, what is retained, what happens on a breach. A scanner does not answer any of them.
Pay attention to which identity authorizes the server, because that is the permission it inherits. With OAuth there is no separate key to scope down. The server acts with the authority of whoever consented, so an admin authorizing a server grants it admin reach. For a personal resource such as email or calendar, consenting as the user is the right model and the scopes are meant to do the narrowing. For a shared or organizational system, a purpose-limited account is usually better than a privileged one. Where a server takes a locally configured API key instead, that is one of the few cases where the credential can genuinely be scoped and rotated on its own.
Where the client supports it, require approval before tools that write, send or execute are called. The agent invokes tools on its own, so anything irreversible is otherwise reachable unattended. How much control is available over which tools need confirmation depends on the client.
Pin versions rather than relying on npx -y. Unpinned installs remove the version gate that normally protects a dependency.
Read the tool list before connecting, and read it against actual need. The median server exposes 5 tools; the largest exposes 542. The whole surface is accepted, not the part in use.
Where the tooling allows it, keep visibility into what each server is asked to do. Client support for this varies, and the practical options differ by deployment: a local server runs as a process on your own machine and can be constrained accordingly, while a remote one is someone else's service where the only thing observable is that a call was made. Neither shows what an operator does with data after it arrives.
For maintainers: verify that authorization is enforced at invocation, not only at listing. Those are separate code paths and it is easy to secure one and leave the other open. Any server that returns a tool list to an anonymous caller should be checked today.
Where manual review is required
Two of the checks depend on reading a server's tool list: the capability rating, and the scan for hidden instructions in the text a server supplies to the model. A server that requires credentials and publishes no source cannot be assessed on either, which describes a substantial part of the registry. Provenance, transport and authorization checks still resolve for nearly all servers.
Where an automated capability rating is unavailable, manual review fills it. For a server under consideration, that means authenticating with a real account and pulling the tool list and instructions directly. In an organization this belongs in vendor review alongside the questions about data handling. Individually it is a few minutes of work before connecting. The appendix below describes how.
Caveats
The registry self-selects for maintainers organized enough to publish there at all, so the unregistered long tail is likely worse rather than better.
Some servers appear less inspectable than they are. Our scanner reads npm and PyPI packages only, so container images and MCP bundles look unreadable when they are not. The same limit applies to the advisory check: a server whose package we cannot read counts as having nothing to query.
Individual grades are provisional. Rescanning the same server has produced different results, mostly from upstream timeouts. Treat a grade as a reason to evaluate further.
The policy is at v0.2 and published under CC-BY-SA, so it can be adopted and adjusted. The parts that are wrong for a given context are the parts worth arguing about.
Appendix: checking a server manually
The official MCP Inspector is the fastest route:
npx @modelcontextprotocol/inspector
Select Transport Type: Streamable HTTP, enter the server URL, and click Connect. This shows the server's configuration, including its instructions field and its full tools/list.
Two things are worth reading carefully. The instructions field is text the server asks the client to place in the model's system prompt, written by whoever operates the server. And every tool description is model-facing text that the model tends to act on. Both should be read with the suspicion applied to any third-party input, because that is what they are.
For a server that requires authentication, connect with a real account. That is the only way to answer the capability question for the servers this scan could not reach.
Check a server yourself
The scanner is free and the scoring model is open. Paste an npm package, a GitHub repository, a registry name or a remote server URL at mcprisk.org and you get the same two ratings used throughout this report, with the evidence behind each check.
The policy those checks measure against is published under CC-BY-SA at github.com/bluemotionlabs/mcprisk. It is version 0.2 and open to argument.