Example finding How it works Coverage PENTEST METHODOLOGY DOCS PRICING FAQ MCP CONTACT LOG IN SIGN UP →

MCP server exposed to the internet: how to fix it

FIX / OPEN MCP SERVER / 9 MIN READ

An MCP server that is reachable from the internet without authentication lets any stranger call its tools, so stop the server or close its port now, rotate every secret its tools can read, and then restart it on localhost with a token. The Model Context Protocol (MCP) connects an AI client to tools. Each tool is code that runs on your machine or your server. An open server gives that code to everyone.

This guide shows how an MCP server becomes public, how to confirm it, and how to fix it. It also shows how to check for damage and how to keep the server private.

What does “MCP server exposed to the internet” mean?

It means that a remote computer can send tool calls to your MCP server and the server runs them. The server does not ask who the caller is.

An MCP server offers tools, for example “run a shell command”, “read a file”, “query the database”, or “send an email”. The AI client reads the list of tools and calls them with arguments. The server runs the code and returns the result.

The protocol does not force a login. The MCP authorization specification says that authorization is optional. It also says that servers on HTTP transports should follow it. A tutorial or a coding tool often skips this step, because the demo works without a login.

The result is the same as an open admin panel. The caller does not need a vulnerability. The caller only needs the URL and the knowledge of the protocol, and both are public.

How does an MCP server become public?

Most open servers are not published on purpose. These are the usual ways:

Cause Example
Bind to all interfaces The server starts with host="0.0.0.0" and the machine has a public address
Container port published on all interfaces docker run -p 8000:8000 listens on every interface of the host
Cloud firewall rule that allows all A security group with source 0.0.0.0/0 on the server port
Tunnel for a demo A tunnel tool gives the local server a public URL, and nobody closes it
Deploy of a local server A project that worked on a laptop goes to a hosting platform with the same code
Reverse proxy without auth The proxy forwards /mcp to the server and adds no login

A coding tool can create the first and the fifth cause for you. You ask it to “make the MCP server available to my team”. The tool changes the host to 0.0.0.0 and deploys it, and it does not add a login.

The MCP transport specification gives three rules for the Streamable HTTP transport. Servers must validate the Origin header to prevent DNS rebinding. Local servers should bind only to 127.0.0.1, not to 0.0.0.0. Servers should authenticate all connections.

How do I confirm that my server is open?

Test from outside your network. A test from the same machine proves nothing, because the server accepts local calls by design.

  1. Disconnect your computer from your home or office network. Use a phone hotspot, or use a device that is on a different network.
  2. Open a terminal and send an initialize request to the public URL of the server.
  3. Read the answer. A JSON answer that lists the server name means that the server accepts anonymous callers.
  4. Send a tools/list request. The list shows what a stranger can run.

Use these commands. Replace the URL with the URL of your server:

curl -s -i https://your-host.example/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}'

If the answer has a header named Mcp-Session-Id, add it to the next request:

curl -s https://your-host.example/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Mcp-Session-Id: SESSION_ID_FROM_ABOVE" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

A correct server answers 401 to both requests. Some servers also need a notifications/initialized message before they accept tools/list. If the first request returns 401 or 403, the server is protected.

Warning: Do not call the tools of a server that you do not own. Test only your own servers. A tools/list request changes nothing, but a tools/call request can.

The older HTTP+SSE transport uses a different shape. The client opens a GET request to an SSE path, often /sse. If this path answers with an event stream and no login, the server is open too.

You can also check the listening ports on the host:

# Linux
ss -tlnp | grep -E ':(3000|8000|8080|8931)'

# macOS
lsof -iTCP -sTCP:LISTEN -P -n

An entry with 0.0.0.0:PORT or *:PORT listens on every interface. An entry with 127.0.0.1:PORT listens only on the local machine. The port numbers in the example are common defaults. Use the port of your server.

Also read the MCP server code and the start command. Search for 0.0.0.0, host=, --host, and -p in your Dockerfile and compose file.

What do I do first? Fix it now, in order of urgency

Do the steps in this order. Steps 1 to 3 stop the exposure. The other steps remove the cause.

Caution: If the server runs tools that change data, such as a shell or a database writer, assume that a stranger already used them. Do step 1 before you investigate.

  1. Stop the exposure. Stop the server process, or close the port in the firewall. If you use a tunnel, stop the tunnel. If the server runs in a container, stop the container.
  2. Save the evidence. Before you delete anything, copy the server logs, the proxy logs, and the shell history to a safe place. You need them in the damage check.
  3. Rotate the secrets that the tools can reach. List what the server process can read. Include environment variables, .env files, cloud credentials, SSH keys, and database passwords. Rotate each one. A shell tool can read all of them.
  4. Remove tools that you do not need. Delete the shell tool, the arbitrary file tool, and the raw SQL tool unless a task requires them.
  5. Bind to localhost. If only local clients use the server, set the host to 127.0.0.1. Better, switch to the stdio transport, which opens no port.
  6. Add authentication. If remote clients must connect, require a token. See the code below.
  7. Validate the Origin header. Reject requests from origins that you do not trust.
  8. Restart and test again from outside. Repeat the curl test. The server must answer 401 without a token.

Bind the server to the local machine

Change the start command so that the server listens only on the local interface:

uvicorn server:app --host 127.0.0.1 --port 8000

For Docker, publish the port to the loopback address only:

docker run -p 127.0.0.1:8000:8000 your-mcp-image

Most MCP libraries have a host option. Set it to 127.0.0.1 and do not rely on the default. A local client such as a desktop AI app can still connect.

Add a bearer token and an Origin check

A static token is the minimum for a private server. The specification describes OAuth for public servers, and you should use it when many users connect. This example uses a Starlette middleware for a Python server. Adapt the idea to your framework:

import os
import secrets

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.responses import PlainTextResponse

ALLOWED_ORIGINS = {"https://your-app.example"}


class Guard(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        origin = request.headers.get("origin")
        if origin and origin not in ALLOWED_ORIGINS:
            return PlainTextResponse("Forbidden", status_code=403)

        sent = request.headers.get("authorization", "").encode()
        expected = f"Bearer {os.environ['MCP_TOKEN']}".encode()
        if not secrets.compare_digest(sent, expected):
            return PlainTextResponse(
                "Unauthorized",
                status_code=401,
                headers={"WWW-Authenticate": "Bearer"},
            )
        return await call_next(request)

Generate the token with openssl rand -hex 32 and store it as a server secret. Put the same token in the settings of your MCP client. Never put the token in the URL. The specification forbids tokens in the query string.

A client that is not a browser does not send an Origin header, so the check above lets it pass and the token check decides. A web page that calls your server sends an Origin header, and the check rejects any origin that is not on your list.

Put the server behind a proxy that enforces the login

If you cannot change the server code, put a reverse proxy in front of it. The proxy checks the token, and the server listens only on 127.0.0.1. Do not publish the server port directly. Test that the direct port is closed and that the proxy returns 401 without a token.

How do I check the damage after an exposure?

Read the evidence from the time window in which the server was public. If you do not know when it became public, use the time of the first deploy or the first tunnel start.

  1. Read the access logs. Look at the proxy logs and the server logs. Find requests to the MCP path from addresses that you do not know. Look for tools/call methods and for tool names such as run_command or read_file.
  2. Read the tool logs. If the server logs each tool call with its arguments, read them. Look for commands that download files, open a network connection, or read secrets.
  3. Check the shell history. Read the history files of the user that runs the server.
  4. Check for new persistence. Look for new cron jobs, new systemd units, new SSH keys in authorized_keys, and new user accounts.
  5. Check the file system. Look for files that changed in the window. Check the home directory and the project folder.
  6. Check outbound connections. Read the firewall logs or the cloud flow logs. Look for connections from the server to unknown hosts.
  7. Check the cloud audit log. If the server held cloud credentials, read the audit log of the cloud account for calls that you did not make.
  8. Check the database logs. If a tool can reach the database, read the query logs for statements that your app does not send.

If the server had a shell tool and the logs are missing, treat the machine as compromised. Build a new machine from a clean image and move the data you need. Do not trust the old machine.

Also check what the tools could change. A tool with a database write ability can delete or alter rows. Compare the current data with a backup. If data is missing, restore it from a backup.

How do I stop the next exposure?

Use these controls so that no MCP server is public by mistake.

  1. Use stdio for local tools. The client starts the server as a child process. No port opens, so nobody can reach it from the network.
  2. Bind to 127.0.0.1 by default. Change the bind address only when you have a reason, and add authentication in the same change.
  3. Require a token or OAuth on every HTTP server. Check the token on every request, not only at the start of a session.
  4. Give each tool the least access. Use a database role with read-only rights for a query tool. Limit file tools to one folder. Do not give the process the keys of your whole cloud account.
  5. Run the server as a low-rights user in a container. Mount only the folders that the tools need. Do not mount the home directory.
  6. Ask for human approval for risky tools. A tool that deletes data or sends money must wait for a person to approve it.
  7. Keep secrets out of the server environment. Give the process only the secrets that its tools use.
  8. Review each deploy of an MCP server. Check the bind address, the published ports, the firewall rules, and the login. Ask your coding tool to list them.
  9. Scan from outside after each release. Test the public address of your app and your hosts. See MCP servers without auth for the attack patterns, and hosting panels and internal surface for other tools that people leave open.

The server is not the only part of the risk. A malicious tool description can also tell the model to do harmful things. Read the same pattern page for tool-spec injection. If you use an AI coding agent that connects to MCP servers, read the Claude Code security guide to limit what the agent can run.

You can use the MCP Inspector to test your server after the fix. It can connect to a remote HTTP server and call tools/list, so you can see what a client sees.

When should I get help?

Get help in these cases:

  • The logs show tool calls from addresses that you do not know. Keep the logs and ask a security engineer to review them.
  • The server had a shell tool, and you cannot prove that nobody used it. Rebuild the machine and rotate all secrets.
  • The server held credentials for a cloud account or a payment provider. Rotate them, then read the audit logs of those services.
  • The server could read customer data. Ask a lawyer whether you must tell the affected users.
  • You cannot find which host runs the server. Scan your domains and your cloud accounts for open ports.

After the fix, repeat the outside test. The initialize request must return 401 without the token and a normal answer with it.

Common questions

Is a Model Context Protocol server safe on localhost?
It is safer, but not fully safe. A web page in your browser can send requests to localhost. Validate the Origin header and add authentication, even for a local server.
Does the MCP specification require authentication?
No. The specification says that authorization is optional. A server that you put on the internet must still have it. You are responsible for the control.
Should I use stdio or HTTP?
Use stdio for tools that run on your own machine. The client starts the server as a child process, so no network port opens. Use HTTP only when the server must serve remote clients.
Is a secret URL enough to protect the server?
No. URLs appear in logs, browser history, and screenshots. Use a token in the Authorization header and keep it out of the URL.
What is the biggest risk of an open MCP server?
The tools. A caller can run every tool that the server offers, with the permissions of the server process. A shell tool or a database tool gives the caller the same access.

Find open mcp endpoints

A VibeEval scan probes your deployed app and its hosts for MCP endpoints that answer a tool list request without a login.

14-day free trial · No credit card · Cancel anytime

SCAN YOUR APP →