MCP Server Connection Refused Localhost: Quick Fix
Connection refused is the most frustrating error in the MCP ecosystem because it tells you almost nothing. The server isn't responding on the expected address — but why? The process could be dead, the port could be wrong, the transport could be mismatched, or a firewall could be silently dropping packets.
This guide covers the four checks that resolve mcp server connection refused localhost errors in order of likelihood. Most issues resolve at Check 1 or 2.
Why MCP Servers Refuse Connections on Localhost
A "connection refused" error means the client (Claude Code, OpenClaw, or your custom agent) tried to open a TCP connection to localhost on a specific port, and nothing was listening there. The operating system itself rejected the connection — no process ever saw the request.
This is different from a timeout (where something is listening but not responding) or a broken pipe (where the connection was established then dropped). Connection refused is binary: either something is listening on that port or it isn't.
The four most common reasons nothing is listening:
- The server process isn't running at all
- The server is running but bound to a different port or interface
- You're trying HTTP to a stdio server (or vice versa)
- A firewall or container network is blocking the connection
Check 1: Verify the Server Process Is Actually Running
Before debugging anything else, confirm the server exists as a running process.
For npx-based servers
ps aux | grep mcp
Look for a node process running your MCP server package. If nothing shows up, the server never started or crashed immediately after launch.
Test the launch command manually:
npx -y tavily-mcp@latest
If this exits immediately, check the output for errors. Common causes: missing API key, Node.js version too old, or the package name is wrong. Our Tavily MCP server setup guide has a working config you can test against.
For custom or self-hosted servers
lsof -i :3000
Replace 3000 with whatever port your server should be binding to. If nothing appears, the server isn't listening on that port. Check the server's logs for startup errors.
The most common cause
The server crashed on startup due to a missing environment variable or invalid config, and Claude Code doesn't surface the error. The process spawned, failed, and exited — all silently. Always test the launch command in your terminal first where you can see stderr output.
Check 2: Port Conflicts and Binding Issues
The server is running, but your client still gets connection refused. The likely cause: a mcp server port binding error where the server bound to a different port or interface than expected.
Port mismatch
Check what port the server is actually listening on:
lsof -i -P | grep node
This shows all ports that node processes are bound to. If your server is on port 8080 but your config says 3000, that's your answer.
Interface mismatch: 127.0.0.1 vs 0.0.0.0
A server bound to 0.0.0.0:3000 accepts connections from any interface. A server bound to 127.0.0.1:3000 only accepts connections from localhost. These are usually equivalent on the same machine — but not always.
If your server binds to 0.0.0.0 and your client connects to localhost, this should work. But on some systems, localhost resolves to ::1 (IPv6 loopback) while the server only bound to 127.0.0.1 (IPv4). Test explicitly:
curl http://127.0.0.1:3000/health
curl http://[::1]:3000/health
If one works and the other doesn't, force your client to use the working address.
Port already in use
If another process grabbed the port first, your MCP server may have failed to start or bound to a fallback port:
lsof -i :3000
Kill the conflicting process or configure your server to use a different port.
Check 3: Transport Mismatch — stdio vs HTTP
This is the sneaky one. Most MCP servers configured in Claude Code use stdio transport — they communicate through standard input/output pipes, not HTTP. These servers don't listen on any port at all.
If your config looks like this:
{
"mcpServers": {
"tavily": {
"command": "npx",
"args": ["-y", "tavily-mcp@latest"],
"env": { "TAVILY_API_KEY": "tvly-xxx" }
}
}
}
This is a stdio server. There's no port, no HTTP, no localhost. Claude Code spawns it as a child process and communicates via stdin/stdout. A "connection refused" error on localhost means something in the chain is incorrectly trying to reach the server over HTTP when it should be using pipes.
When you actually need HTTP transport
If your MCP server runs as a separate service (in Docker, on another machine, or as a long-running daemon), you need streamable HTTP transport. The config looks different:
{
"mcpServers": {
"my-server": {
"url": "http://localhost:3000/mcp"
}
}
}
The url field instead of command tells Claude Code to connect via HTTP rather than spawning a child process. If you're getting connection refused with this config, the HTTP server genuinely isn't running on that address — go back to Checks 1 and 2.
For more on when to use which transport, see our MCP broken pipe troubleshooting guide.
Check 4: Firewall, Proxy, and Docker Networking
Local firewall
On macOS, the application firewall can block incoming connections to node processes. Check System Settings → Network → Firewall and look for blocked entries. On Linux:
sudo iptables -L -n | grep 3000
Docker networking
If your MCP server runs inside a Docker container, localhost inside the container is the container's loopback — not the host machine's. The fix depends on direction:
Client on host, server in container:
docker run -p 3000:3000 my-mcp-server
The -p flag maps the container port to the host. Connect to localhost:3000 on the host.
Client in container, server on host:
Use host.docker.internal instead of localhost:
{
"url": "http://host.docker.internal:3000/mcp"
}
Both in containers: Use Docker Compose networking and connect via service name:
services:
agent:
depends_on: [mcp-server]
mcp-server:
ports: ["3000:3000"]
Corporate proxy
If you're behind a corporate proxy, HTTP connections to localhost may be routed through the proxy and rejected. Set the NO_PROXY environment variable:
export NO_PROXY=localhost,127.0.0.1
Preventing Connection Refused Errors in Production
Once you've fixed the immediate issue, prevent it from recurring:
Health checks. Add a /health endpoint to HTTP-based MCP servers. Monitor it with a simple cron job or uptime checker. If the health check fails, restart the server automatically.
Process supervision. Run production MCP servers under a process manager (systemd, pm2, Docker restart policies) that automatically restarts crashed processes.
Startup verification. In your agent workflow, add a startup check that verifies all MCP tools are available before proceeding. If a tool is missing, log which server failed and retry the connection.
Prefer stdio for local servers. If the MCP server runs on the same machine as your agent, stdio transport eliminates the entire class of networking issues. No ports, no firewalls, no binding conflicts. Reserve HTTP transport for remote or containerized servers.
For a comprehensive walkthrough of all MCP connection issues in Claude Code, see our full troubleshooting guide.
Frequently Asked Questions
Why does my MCP server refuse connections on localhost?
The most common cause is that the server process isn't actually running. It either failed to start (bad config, missing API key, wrong Node.js version) or crashed immediately after launch. Claude Code doesn't display MCP server startup errors, so failures are silent. Test your server's launch command directly in the terminal to see error output. Other causes include port conflicts, transport mismatches (trying HTTP on a stdio server), and Docker networking issues where localhost resolves differently inside containers.
How do I check if my MCP server is listening on the right port?
Run lsof -i :PORT (replacing PORT with the expected port number) to see if any process is bound there. If nothing appears, the server isn't listening. Run lsof -i -P | grep node to see all ports that node processes are using — your server may have bound to a different port than expected. For stdio-based MCP servers (the default in Claude Code configs), there is no port — the server communicates via stdin/stdout pipes, not TCP.
What's the difference between connection refused and connection timeout?
Connection refused means the operating system actively rejected the connection — nothing is listening on that port. It's an immediate failure. Connection timeout means something may be listening but isn't responding within the expected time. Refused is typically a configuration or process issue (wrong port, server not running). Timeout is typically a performance or firewall issue (server overloaded, packets being silently dropped). The fixes are different: refused requires checking process state and ports; timeout requires checking firewalls and server performance.
How do I fix MCP connection refused in Docker?
Docker containers have their own network namespace, so localhost inside a container points to the container itself, not the host machine. If your MCP server runs in a container and the client is on the host, expose the port with -p 3000:3000. If the client is in a container and the server is on the host, use host.docker.internal instead of localhost. If both are in containers, use Docker Compose service names for addressing and ensure they're on the same Docker network.