On this page
Local MCP Server: stdio vs Localhost, Open Ports and Safe Setup (Nov 2026)

A local MCP server is an MCP server that runs on your own computer. Most use stdio: your AI client starts the server and talks to it through a pipe, so nothing listens on the network. Some open a local HTTP port instead. Keep that port on 127.0.0.1, give it a token, and never open it to the internet.

That last part is where people get caught. On r/mcp, one poster scanned the 99 most-starred MCP servers, another listed four MCP security traps, and a third ran a decoy MCP server and watched AI tools connect to it. There's also a scan of 25 public MCP servers for auth gaps, an r/ClaudeAI user who audited the MCP servers connected to their Claude, and an r/ClaudeCode thread on running your own MCP server on a NAS. We link these by title; we haven't quoted them.

This guide is for people running servers, not writing them. If you're building one, the rules for your code are in how to create an MCP server securely.

What is a local MCP server?

It's a program on your machine that gives your AI client tools: read files, query a database, drive a browser. The MCP spec defines two standard ways for the client to reach it, and a local server can use either.

stdio Local HTTP
How it starts Your client launches it You (or a script) start it, and it keeps running
How they talk The process's stdin and stdout HTTP requests to a URL like http://127.0.0.1:8080/mcp
Opens a network port? No Yes
Who can connect Only the client that started it Anything that can reach the port
In your config "command" and "args" "url" (often with "type": "http")

The spec's stdio page says "the client launches the MCP server as a subprocess" and the server "reads JSON-RPC messages from stdin and writes JSON-RPC messages to stdout." With Streamable HTTP, "the server operates as an independent process that can handle multiple client connections." That's the whole difference that matters here: a stdio server has one caller, an HTTP server has a door.

Either way, a local server runs as you, with your files and keys. Limiting that is a separate job, covered in how to sandbox MCP servers.

Local vs remote MCP server

Local MCP server Remote MCP server
Runs on Your machine The vendor's (or your company's) servers
You add it with A command, or a localhost URL An https:// URL
Sign-in Usually an API key in an environment variable Usually OAuth in the browser, sometimes a token
What it can reach Whatever your user account can Only what its token allows on that service
Main risk It runs as you; an open port lets others in A token with too much access

Use a local server when the job is on your machine (files, a local database, a browser you watch) or when no hosted version exists. Use the remote one when the vendor offers it and the job is their service: there's nothing to install, and the access is limited by the token's scopes. The spec agrees on the split: its authorization rules are for HTTP, and stdio servers "SHOULD NOT follow this specification, and instead retrieve credentials from the environment." Sign-in for remote servers is covered in MCP authentication.

Is my MCP server stdio or HTTP?

Look at its entry in your client's config file.

  • It has "command" (npx, uvx, docker, node, a path to a program): it's stdio. Your client starts it when it needs it and stops it after. No port.
  • It has "url" with localhost, 127.0.0.1 or your machine's name: it's a local HTTP server. Something else started it, and it's listening on that port for anyone who can reach it.
  • It has "url" with an https:// address on someone else's domain: it's remote. Nothing runs on your machine.

Watch for a mix: some READMEs tell you to start the server with a flag like --port or --transport http in a terminal, then add the URL. That's a local HTTP server even though you started it with a command. In Claude Code, claude mcp list lists every server you've added; see Claude Code MCP setup.

If you have the choice, pick stdio. The MCP security best practices tell servers meant to run locally to "Use the stdio transport to limit access to just the MCP client."

How to see what's listening on your machine

Run one of these while the server is up. Each lists the ports something is listening on and which program owns them.

macOS or Linux, with lsof:

lsof -nP -iTCP -sTCP:LISTEN

-iTCP -sTCP:LISTEN is the man page's own example for "network files with TCP state LISTEN". -n and -P keep addresses and ports as numbers instead of names.

Linux, with ss:

ss -ltnp

-l shows "only listening sockets", -t TCP, -n numbers instead of service names, and -p the process using each socket.

Windows, with netstat:

netstat -ano -p tcp

-a includes "the TCP and UDP ports on which the computer is listening", -n keeps addresses as numbers, -o adds the process ID, and -p tcp limits it to TCP. Look at the rows whose state is listening, then find the PID in Task Manager.

How to read the address column

You see It means
127.0.0.1:8080 or [::1]:8080 Only programs on this machine can connect. Good.
0.0.0.0:8080, [::]:8080 or *:8080 Every network interface: your Wi-Fi, your LAN, a VPN. Anyone who can reach your machine can connect.
192.168.1.20:8080 (your LAN address) Anyone on that network can connect.

If a port belongs to node, python, uv or docker and you don't know why, match the port to your MCP config before you assume it's harmless. A stdio server shouldn't show up in this list at all.

MCP server on localhost: bind to 127.0.0.1, not 0.0.0.0

127.0.0.1 (and localhost) is the loopback address: traffic never leaves your machine. 0.0.0.0 tells the server to accept connections on every address the machine has. The spec's Streamable HTTP rules say that "When running locally, servers SHOULD bind only to localhost (127.0.0.1) rather than all network interfaces (0.0.0.0)."

Not every server does that by default. From servers we've graded:

  • DBHub: --host defaults to 0.0.0.0. Set --host 127.0.0.1.
  • SerpApi's MCP server, self-hosted: listens on 0.0.0.0:8000 by default. Set MCP_HOST=127.0.0.1.
  • Playwright MCP: --host defaults to localhost, but the README's long-running Docker example binds 0.0.0.0.

So check the README for a host flag or variable before you start a server in HTTP mode, then confirm with the commands above.

Docker is the usual surprise. Inside a container, a server often has to listen on 0.0.0.0 so Docker can reach it. The limit then goes on the publish flag. Docker's port publishing docs say "Publishing container ports is insecure by default": -p 8080:8080 publishes to "all host addresses (0.0.0.0 and [::])". Publish to loopback instead:

docker run -p 127.0.0.1:8080:8080 <image>

With that, "only the Docker host can access the published container port."

MCP DNS rebinding: why localhost alone isn't enough

127.0.0.1 keeps other machines out, but your browser runs on your machine. In a DNS rebinding attack, you open a web page on a domain the attacker controls; the attacker then switches that domain's DNS to 127.0.0.1. The browser still thinks the page is talking to its own site, so the page's script can send requests to your local MCP server's port and call its tools. The spec's Streamable HTTP page is blunt about it: "attackers could use DNS rebinding to interact with local MCP servers from remote websites." Its defense is on the server: servers "MUST validate the Origin header on all incoming connections" and answer a bad one with "HTTP 403 Forbidden". Real servers have shipped fixes for this (Playwright MCP's CVE-2025-9611, DBHub's CVE-2026-61742), so your part is to keep HTTP-mode servers updated, leave their host or origin checks (--allowed-hosts and the like) at the defaults, and add a token, which a web page can't guess.

How to add a token to a local MCP server

The spec's security page tells servers meant to run locally that use HTTP to "Require an authorization token". Many servers have an option for it, but check whether it's on: DBHub's, for one, is off by default.

1. Check the README for a token option. DBHub, for example, has --auth-token; with it set, clients must send Authorization: Bearer <token>. If the server has no option, see the last step.

2. Make a long random token, and keep it in an environment variable, not in a file you commit:

openssl rand -hex 32

3. Start the server with it, using the flag or variable its README names.

4. Send it from your client. In Claude Code:

claude mcp add --transport http my-local-server http://127.0.0.1:8080/mcp \
  --header "Authorization: Bearer $MY_MCP_TOKEN"

Or in a .mcp.json, where Claude Code expands ${VAR} in headers:

{
  "mcpServers": {
    "my-local-server": {
      "type": "http",
      "url": "http://127.0.0.1:8080/mcp",
      "headers": {
        "Authorization": "Bearer ${MY_MCP_TOKEN}"
      }
    }
  }
}

The spec's rule for real OAuth tokens is the same idea: they go in the Authorization header and "MUST NOT be included in the URI query string". Don't use a server that only accepts a token in the URL; URLs end up in logs and browser history. More on keeping the value out of config in MCP API keys and config.

5. No token option? Then don't open it beyond 127.0.0.1. Run it over stdio if it supports that, or put something in front that checks who's calling: an MCP gateway or the Cloudflare Access setup below.

A shared token is a door key, not a sign-in. Everyone who has it gets the same access, and the server can't tell callers apart. Fine for you and your own tools; not for a team.

Use a tunnel instead of an open port

Sometimes you do need a local server from somewhere else: a laptop away from home, or a server on a NAS. Don't forward a port on your router to it. Use a tunnel, where the connection goes out from your machine and no inbound port is opened.

Cloudflare Tunnel works that way: a small program, cloudflared, "initiates an outbound connection through your firewall" to Cloudflare, so your machine doesn't need "a publicly routable IP address" and can keep blocking inbound traffic. Two ways to use it:

A quick tunnel, for a five-minute test only. Cloudflare's Quick Tunnels page gives:

cloudflared tunnel --url http://localhost:8080

That prints a random trycloudflare.com address, with no account needed. Read the warnings before you use it for MCP: "Anyone with the URL can access your local service", Quick Tunnels "are for testing and development", and they "do not support Server-Sent Events (SSE)". MCP's HTTP transport can answer with SSE streams, so some calls may fail. Keep the server's token on and stop cloudflared when you're done.

A named tunnel with Cloudflare Access, for anything you keep. Create the tunnel on your own domain (dashboard steps), then put a Cloudflare Access policy in front of it. A browser sign-in won't help an MCP client, so use a service token: set the policy action to Service Auth, and have the client send the CF-Access-Client-Id and CF-Access-Client-Secret headers (the same --header flag or headers block as above). Keep the server's own token too, so a mistake in one layer doesn't open the other.

Whatever is in front, the server still runs on your machine with your rights. Trim what it can reach first (sandbox it), then decide who can reach it.

Lock down a local MCP server in 5 steps

Tick these off for each local server you run.

1. Use stdio when the server offers it

No port, one caller. Only switch to HTTP when you need a server that stays running or several clients sharing it.

2. Check what's listening

Run lsof -nP -iTCP -sTCP:LISTEN, ss -ltnp or netstat -ano -p tcp and match every port to a server you meant to run.

3. Bind to 127.0.0.1

Set the server's host flag to 127.0.0.1, and publish Docker ports as 127.0.0.1:PORT:PORT. Re-check the address column after.

4. Turn on a token and keep the server updated

A long random token in an environment variable, sent as Authorization: Bearer. Update HTTP-mode servers so their DNS rebinding checks are current.

5. Reach it from elsewhere through a tunnel, not an open port

A Cloudflare Tunnel with an Access service token, never a router port forward, and a quick tunnel only for a short test.

Before you add a server at all, run it through our MCP security checklist. If your team needs MCP servers that several people can reach safely, with real sign-in and a log of every call, that's the kind of setup we build. Tell us what you need to connect.

Frequently asked questions

What is a local MCP server?

An MCP server that runs on your own computer instead of a vendor's. Your AI client either starts it and talks over stdio, or connects to it on a localhost HTTP port.

Is a local MCP server safe?

It's as safe as the code and how you run it. It runs with your user's rights, so only install servers you've checked. Over stdio, nothing else can connect to it. Over HTTP, keep it on 127.0.0.1 with a token.

What port does a local MCP server use?

There's no standard port. A stdio server uses none. An HTTP server uses whatever its README or your flag sets: 8000 for a self-hosted SerpApi server, 8080 in DBHub's Docker example, 8931 in Playwright MCP's. Check with lsof, ss or netstat.

Should a local MCP server listen on 0.0.0.0?

Not on your own machine. 0.0.0.0 accepts connections from every network you're on. The spec says local servers should bind to 127.0.0.1. Inside a Docker container, 0.0.0.0 is often needed, so limit it on the host side with -p 127.0.0.1:PORT:PORT.

What is DNS rebinding in MCP?

A trick that lets a web page you visit send requests to a server on your localhost, by switching its domain's DNS to 127.0.0.1. MCP servers must check the Origin header to stop it; you add a token and keep the server updated.

What's the difference between a local and a remote MCP server?

Where it runs and how it signs in. A local server runs on your machine with your rights and usually reads an API key from the environment. A remote server runs on the vendor's side, you connect by URL, and it usually signs you in with OAuth.

How do I connect to an MCP server on localhost?

Add its URL to your client, for example http://127.0.0.1:8080/mcp. In Claude Code that's claude mcp add --transport http my-local-server http://127.0.0.1:8080/mcp, plus --header "Authorization: Bearer $MY_MCP_TOKEN" if the server has a token. A stdio server needs no URL: you add its command and the client starts it.

How do I use my local MCP server from another computer?

Through a tunnel, not an open port. A Cloudflare Tunnel connects outward, so nothing on your machine is opened to the internet. Put a Cloudflare Access service token in front, and keep the server's own token on.