Skip to content
Back to field notes
AI risksJune 6, 20266 min read

MCP server security exposure: what ships publicly when you deploy an AI agent backend

An MCP server deployed without auth is a public tool endpoint anyone can drive. This note shows how to probe your own endpoint before the URL spreads, and what to lock down.

MCPAI agentsAuthentication

An unauthenticated MCP server is a public tool endpoint

When you deploy an AI agent backend, the part that often ships more openly than you expect is the MCP server. Model Context Protocol is the wire format an agent uses to discover and call tools: list the available tools, then invoke one with arguments. The concrete risk at launch is direct. If that server answers requests on a public URL without authentication, then anyone who finds the URL can ask it which tools it exposes, and in many setups, run them.

Separate what is observable from what is only possible. Observable, with a single request: whether the endpoint responds at all on the public surface, and whether it returns a tool listing to a caller who sent no credentials. That is a fact you can check. What is only possible, and depends on how each tool is wired, is the blast radius once someone calls a tool: a tool that reads a file, queries a database, sends an email, or spends API budget does real work on your behalf. The listing being open is the signal. What the tools actually do is what turns the signal into damage.

This is an authentication gap first. The server is accepting and acting on requests from callers it never identified. Whether a given caller should be allowed to run a given tool is a separate authorization question, and a server with no auth at all has answered neither.

Why AI-built agent backends ship this way

The MCP transport that is easiest to stand up locally is also the one that quietly becomes reachable in production. During development the server runs on localhost, where "no auth" feels fine because nothing else can reach it. The mental model never updates when the same process gets deployed behind a public hostname.

AI-assisted scaffolding makes this faster and quieter. A prompt like "deploy my agent so the frontend can call its tools" produces a server that binds and responds, and the demo works on the first try, which makes it look finished. The step that does not happen automatically is the one where someone decides who is allowed to call the tool endpoint and adds a check for it.

  • Local-first defaults: an HTTP or SSE MCP transport bound to a host that is publicly routable once deployed, with no token check in front of it.
  • Working demo hides the gap: the agent calls its own tools successfully, so the missing auth is invisible from the inside.
  • Fast deploy: the public URL exists before anyone audits whether the tool endpoint answers strangers. This is the same pattern behind unauthenticated API endpoints in AI-built apps; an MCP server is one specific case of it.
  • Prompt-injection makes it worse: if the agent is reachable and also processes untrusted input, a crafted message can try to steer which tools run. See the note on prompt injection as a launch risk.

Probe your own endpoint from the outside

You can confirm this on your own deployment without attacking anything. The goal is a read-only probe: does the public endpoint talk to an unauthenticated caller, and how far does it go.

First find the URL. Open your frontend in the browser, open DevTools, and watch the Network tab while the agent does something. The request that carries a JSON-RPC body with methods like initialize, tools/list, or tools/call is your MCP endpoint. Note the path and whether the browser is attaching any Authorization header or session cookie.

Then send a request from a context that has no credentials, against your own server only. A plain curl with a JSON-RPC tools/list body and no auth header is enough to tell you what an anonymous caller sees.

  • Suspicious result: the endpoint returns a list of tools, their names, and their input schemas to a request that carried no token or cookie. That means tool discovery is open. If a tools/call for a low-risk tool also succeeds unauthenticated, execution is open too.
  • Safer result: the endpoint rejects the unauthenticated request before doing any work, with a 401 or 403 or an equivalent JSON-RPC error, and reveals nothing about the tools. A connection that simply refuses or times out from outside your network is also a good sign, but confirm it is bound privately rather than just slow.
  • Check the transport, not only one path: SSE-based MCP often has a separate event endpoint and a message-posting endpoint. Probe both. An auth check on one and not the other still leaves a hole.
  • Test a write-shaped tool last and carefully, only on your own server with a tool you know is harmless, because tools/call does real work by design.

Put auth in front, and know what auth does not fix

The reliable fix has a binding stage and a check stage, and the binding stage is the safe place to land first.

If the MCP server only ever needs to be reached by your own backend, do not expose it publicly at all. Bind it to localhost or a private network interface, and let your application server be the only thing that talks to it. An endpoint that the internet cannot route to cannot be probed by a stranger. This closes the exposure without depending on getting an auth flow perfect.

If the endpoint genuinely must be public, require authentication on every MCP request, including the transport's secondary endpoints, and reject unauthenticated calls before any tool logic runs. Then add authorization on top: decide which authenticated callers may invoke which tools, because being allowed to connect is not the same as being allowed to run everything.

  • Bind privately by default; expose publicly only when a real client outside your trust boundary needs it.
  • Authenticate the connection, then authorize the specific tool call. These are two checks, not one.
  • Scope the tools themselves: a tool with broad filesystem, shell, or database reach is dangerous even behind auth, so limit what each tool can touch.
  • Do not treat a hard-to-guess URL as a control. An unguessable path is not authentication; it leaks through logs, referrers, and link sharing.

Adding auth in front of the MCP server stops anonymous callers. It does not validate what an authenticated caller asks a tool to do, and it does not sanitize what tools return to the model. A tool that runs an attacker-influenced query, or output that is rendered unsafely downstream, is a separate problem. See the note on insecure output handling in LLM apps for the render-side risk.

Where VibeCodeGuard fits, and what it does not prove

VibeCodeGuard's Launch Check works from the public surface, which is exactly where this exposure lives. It can probe the public URL of an MCP server endpoint and flag when that endpoint returns an unauthenticated tool listing or an unauthenticated execution response. If your tools/list answers a caller with no credentials, that is the signal the scan is built to surface, with a severity and a fix direction. It checks the thing a stranger can actually reach.

What it does not do is map your tool-permission model. Whether each authenticated caller should be allowed to run each tool, and whether a given tool's reach is appropriate, is an authorization question that needs manual review of your code and config. The scan sees that the door answered; it does not read the rules behind it.

Next step: open DevTools, capture your MCP endpoint URL and any secondary transport path, and send one unauthenticated tools/list to each against your own server. If either returns tools, bind the server privately or add an auth check before you share the URL. Then run a focused Launch Check against the public URL so an open endpoint shows up in your scan and not in someone else's.

A clean Launch Check here means an outside probe did not get an open tool listing from the URL it tested. It does not prove every transport path is covered, that your authorization logic is correct, or that the tools are safely scoped. Treat an open listing as a launch blocker, and a clean result as one signal among several.

> launch check

Scan the public surface before launch.

Get severity, evidence, and practical fix guidance for the checks VibeCodeGuard can run from the outside.