MCP Server Security: What It Exposes and How to Check
MCP servers often run exposed without authentication. Learn what Model Context Protocol endpoints leak, how to fingerprint them, and how to lock them down.
A Model Context Protocol (MCP) server is an HTTP service that lets an AI assistant call tools — read files, query databases, make API requests. When one of these servers is reachable from the internet without authentication, it is effectively an unauthenticated RPC endpoint that can read your filesystem, call internal APIs, and leak credentials. Most teams deploy MCP servers on localhost during development and never revisit the network binding before shipping to production.
What MCP Is
MCP is an open protocol published by Anthropic in 2024. It defines a JSON-RPC 2.0 wire format over HTTP (or stdin/stdout for local processes). A server registers tools — named functions with JSON Schema input definitions — and exposes them at a single endpoint, typically /mcp or /sse. The AI host (Claude Desktop, a custom agent, n8n) calls tools/list to discover what's available, then calls tools/call to invoke them.
The protocol has no mandatory authentication layer. OAuth 2.1 support was added in the 2025-03-26 spec revision, but most self-hosted implementations still rely on network isolation — an assumption that breaks the moment port 3000 or 8080 is forwarded through a firewall, exposed via ngrok, or deployed to a VPS without a reverse proxy.
What an Exposed MCP Server Leaks
- Tool listing —
tools/listreturns every registered tool name, description, and parameter schema. This alone reveals what internal systems the server can reach. - File read tools — common tools like
read_file,list_directoryaccept paths and return content. Without auth, an attacker calls them directly. - Environment variables — tools that shell out or use SDKs inherit the process environment. A
run_commandtool can dumpenv. - API credentials — tools that proxy to Stripe, GitHub, AWS or databases carry those credentials in the server process. An attacker doesn't need the key — they just call the tool.
- Internal network access — the server runs inside your VPC. Tool calls that make HTTP requests become an SSRF vector into private subnets.
Fingerprinting an MCP Server
MCP servers respond to a JSON-RPC initialize handshake on POST /mcp (HTTP transport) or emit an endpoint SSE event on GET /sse. Both patterns are distinct enough to fingerprint:
# HTTP transport probe
curl -s -X POST http://target:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2024-11-05",
"capabilities":{},
"clientInfo":{"name":"probe","version":"0"}}}'
# SSE transport probe
curl -s http://target:3000/sseA successful initialize response includes serverInfo.name and serverInfo.version, which often identify the framework or application. After initialization, call tools/list:
curl -s -X POST http://target:3000/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
If this returns tool definitions without a 401 or 403, the server is unauthenticated. Tools with names like bash, execute, read_file, http_request, or query_database represent immediate high-severity exposure.
Authentication Gaps
Most MCP frameworks delegate auth entirely to the operator. The reference Python SDK (mcp package) has no built-in middleware for token validation. Common patterns seen in the wild:
- No auth at all — server listens on
0.0.0.0, no reverse proxy, no token check. - Host-header check — easy to bypass by spoofing the
Hostheader. - IP allowlist without SSRF guard — allows
127.0.0.1, but a tool that makes server-side HTTP requests can forward to169.254.169.254(AWS metadata). - Shared secret in URL query param —
/mcp?token=abc— logged by proxies and visible in browser history.
Remediation
- Bind to
127.0.0.1, not0.0.0.0, unless public access is required. - Put the server behind a reverse proxy (nginx, Caddy) and enforce Bearer token or mTLS authentication at that layer.
- Implement the OAuth 2.1 authorization server flow from the 2025-03-26 MCP spec if you need multi-tenant access.
- Restrict tool permissions: a tool that reads files should not also be able to execute shell commands. Separate servers for separate privilege levels.
- Audit tool descriptions — they are part of the attack surface (see MCP tool poisoning).
- Enable request logging at the proxy layer. Every
tools/callinvocation should be auditable.
Common Mistakes
- Assuming
localhostbinding survives Docker's port publish flags —-p 3000:3000binds to0.0.0.0on the host. - Using ngrok for a demo and leaving the tunnel running — ngrok URLs are publicly routable and stable for hours.
- Treating tool descriptions as internal documentation — they are transmitted to the AI host and can be read by anyone who calls
tools/list.
Orb44 Detection
Orb44's outside scan probes common MCP ports and endpoints. If your server responds to an unauthenticated initialize or tools/list request, it surfaces as an exposed internal service finding. Check your exposure at orb44.com.
FAQ
Does MCP have a standard port?
No. Common defaults are 3000, 8000, 8080, and 4000 depending on the framework. Scanners should probe all of these.
Can I tell if my MCP server is exposed without an external scanner?
Run the curl probe above from a machine outside your network. If you get a valid JSON-RPC response, the server is reachable. Cloud shell instances (AWS CloudShell, GCP Cloud Shell) work as external vantage points.
Is SSE transport safer than HTTP transport?
No. SSE transport still accepts tool calls over HTTP POST once the session is established. The transport choice does not affect authentication requirements.