Back
AI & vibecoding12 Sept 2026 · 5 min read

21,000 MCP servers exposed on the internet: why authentication is "optional"

by detektd team

Unplugged orange network cables dangling in front of a dark patch rack
Photo: rawpixel · CC0 · rawpixel

In a year, the Model Context Protocol (MCP) became the de facto standard for wiring tools into an AI assistant: a database, a Git repo, a CRM, a shell. An MCP server exposes tools; a client (Claude, Cursor, a homegrown agent) discovers and calls them. The protocol was designed to run locally first. It now runs, massively, on the internet.

In late April 2026, Censys identified 12,520 internet-reachable MCP services across 8,758 IP addresses; by early May, the count passed 21,000. On July 31, a study published on arXiv, "Exposed by Design", measured that fleet dynamically: 640 confirmed production servers, 414 audited, 68 reportable vulnerabilities. And above all, 91.8% of audited servers had no OAuth authentication in place.

0,0%

of audited production MCP servers had no OAuth authentication (arXiv 2608.00150, July 2026)

How MCP works

Under the hood, MCP is JSON-RPC 2.0. A session starts with an initialize exchange, where client and server announce their protocol version and capabilities. The client then calls tools/list to get the list of tools, each described by a name, a natural-language description and a JSON schema for its arguments. Then tools/call to run one. The server can also expose "resources" (addressable data) and "prompts" (interaction templates).

A tool as tools/list describes it
{
  "name": "query_customers",
  "description": "Run a read query against the customers table",
  "inputSchema": {
    "type": "object",
    "properties": { "sql": { "type": "string" } },
    "required": ["sql"]
  }
}

The protocol defines two transports. The first, stdio, runs the server as a subprocess of the client: no network port, no possible exposure, and the specification asks clients to prefer it. The second, Streamable HTTP, turns the server into an independent HTTP service, with a single endpoint accepting POST and GET that can stream responses (Server-Sent Events). In March 2025 it replaced the older HTTP+SSE transport from version 2024-11-05, still present on many deployments.

"OPTIONAL"

MCP's authorization specification opens with a sentence that explains much of the numbers: authorization is OPTIONAL, in capitals, in the RFC sense. When implemented, it's solid: OAuth 2.1 with mandatory PKCE, authorization server discovery through protected resource metadata (RFC 9728), tokens bound to their audience through resource indicators (RFC 8707), and an explicit ban on passing a received token straight through to another API. But nothing forces anyone to implement it.

That choice makes sense for stdio, where the server inherits the local user's rights. It makes far less sense for an HTTP service. The HTTP transport's security section acknowledges as much: servers MUST validate the Origin header of every connection to prevent DNS rebinding attacks, SHOULD bind only to 127.0.0.1 when running locally, and SHOULD authenticate all connections.

How a local server ends up on the internet

The typical scenario is mundane. An MCP server starts locally, over stdio. To share it with the team, it's switched to the HTTP transport and put in a container. Then it's deployed to a cloud instance. At no step did anyone decide to expose it publicly; at every step, it became a bit more true. Censys notes that 61.5% of the services found answered directly on a bare IP address, with no domain name, a sign of quick deployments rather than services designed to be public.

Docker adds a classic trap. Publishing a port with -p 8080:8080 opens it on every interface of the machine, and Docker writes its own iptables rules, which take effect before those of a firewall like ufw. An administrator who thinks the port is closed in ufw may have left it open to the world.

Publish a port on localhost only
# exposed on every interface, and past ufw
docker run -p 8080:8080 my-mcp-server

# reachable from this machine only
docker run -p 127.0.0.1:8080:8080 my-mcp-server
stdio server, local
Switched to HTTP, containerized
Deployed on a public IP
Tools callable by anyone

What's exposed

Censys's numbers, obtained without ever calling a single tool (only from what servers announce during initialization), show the scale of the risk. 88.3% of servers expose at least one tool. About 90 advertise command execution tools, and 1,056 direct query interfaces to SQL, NoSQL or vector databases. The most represented categories are data (databases, knowledge bases) and infrastructure (system control, cloud management).

The arXiv study went further, with a dedicated testing framework covering ten MCP-specific vulnerability classes. Among the 68 reported flaws: SQL injection through tool arguments, SSRF to cloud metadata services, prompt template injection, path traversal. One detail makes the problem hard to track: 41.6% of confirmed servers had vanished between measurement runs, in under three days. Ephemeral servers, spun up for a trial and sometimes forgotten.

Authentication isn't enough

Censys makes the point: an authenticated MCP server is still exposed to prompt injection, since a legitimate client, the agent, calls it with arguments derived from untrusted content. Authentication closes the door to strangers; it doesn't protect against a manipulated agent.

Deploying an MCP server properly

  1. Stay on stdio as long as the server doesn't need to be shared. No port, no surface.
  2. Over local HTTP: bind to 127.0.0.1 and validate the Origin header, as the specification requires; otherwise a web page the developer visits can talk to the server through DNS rebinding.
  3. Over remote HTTP: implement the specification's OAuth 2.1 authorization, or at minimum put the server behind an authenticated gateway. Never a bare public IP.
  4. Reduce every tool to the strict minimum: a read-only database user rather than a free-form SQL tool, file paths restricted to one directory, no access to cloud metadata.
  5. Keep an inventory: a server started "just to test" and never stopped is exactly the one that ends up in the statistics.

When detektd finds an MCP endpoint on the scanned domain, it proceeds like Censys: it reads the list of announced tools, without ever calling one. Severity depends on what they allow: a server exposing a shell, file access, SQL queries or arbitrary HTTP calls without authentication is rated high.