Understanding MCP Connection Refused Errors
A "connection refused" error means your MCP client attempted to connect to a server over the network but the connection was actively rejected. This is fundamentally different from a timeout (no response at all) or an ENOENT error (executable not found). Connection refused specifically means the TCP stack on the target machine received the connection request and sent back a RST (reset) packet, either because nothing is listening on that port or because a firewall explicitly rejected it.
The typical error output varies by client and transport type. Here are the exact messages you will encounter:
# stdio transport (rare for connection refused - usually means the server crashed)
Error: Server process exited unexpectedly with code 1
Connection to MCP server lost
# Streamable HTTP transport
Error: connect ECONNREFUSED 127.0.0.1:3000
MCP server connection failed: Connection refused
# SSE transport (legacy)
Failed to connect to MCP server at http://localhost:3000/sse
Error: fetch failed - ECONNREFUSED
# Remote server
Error: connect ECONNREFUSED 192.168.1.50:8080
ETIMEDOUT or ECONNREFUSED depending on firewall behavior
This guide covers every cause and fix for Claude Desktop, Cursor, VS Code, and other MCP clients. We will walk through systematic diagnosis so you can identify and fix the problem quickly.
Cause 1: Server Not Running
The most obvious cause - the server process has not started, has crashed during initialization, or was never configured to start automatically. For HTTP/SSE-based MCP servers, the server must be running before the client tries to connect. Unlike stdio transport where the client spawns the server process directly, HTTP transport requires you to start the server independently.
Port Detection Commands by OS
Use these commands to check whether anything is listening on the expected port:
| OS | Check Port | Check Process | Kill Process |
|---|---|---|---|
| macOS | lsof -i :3000 |
ps aux | grep mcp |
kill -9 PID |
| Windows | netstat -ano | findstr :3000 |
tasklist | findstr node |
taskkill /PID PID /F |
| Linux | ss -tlnp | grep 3000 |
ps aux | grep mcp |
kill -9 PID |
Quick Connectivity Test
Before debugging further, test basic connectivity to rule out the server process itself:
# Test if the port is reachable
curl -v http://localhost:3000/mcp
# If connection refused: nothing is listening
# If connection reset: something is listening but rejected the request
# If timeout: firewall is blocking
# For Windows (PowerShell)
Test-NetConnection -ComputerName localhost -Port 3000
# TcpTestSucceeded: True means something is listening
Fix
Start the server manually first to verify it works and check for errors in its output:
# For Node.js HTTP servers
node my-mcp-server.js
# Should print: Server listening on port 3000
# For Python servers
python3 my-mcp-server.py
# Should print: Started MCP server on http://localhost:3000
If the server crashes immediately, check its output for errors. Common issues include missing dependencies, incorrect configuration, or another process already using the port. Pay attention to the exact error message - it will point you to the specific cause.
Cause 2: Port Conflicts
Another process is already using the port your MCP server needs. This is especially common with development servers on popular ports like 3000, 8000, 8080, or 5173. When the MCP server tries to bind to an occupied port, it either crashes (giving you connection refused because the server never started) or the wrong process responds to your client's requests.
Finding and Resolving Port Conflicts
# macOS - find what's using port 3000
lsof -i :3000
# Output:
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# node 12345 user 22u IPv4 ... TCP *:3000 (LISTEN)
# Linux - find what's using port 3000
ss -tlnp | grep 3000
# Output: LISTEN 0 128 *:3000 *:* users:(("node",pid=12345,fd=22))
# Windows - find what's using port 3000
netstat -ano | findstr :3000
# Output: TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 12345
# Then find the process name:
tasklist | findstr 12345
# Kill the conflicting process
kill 12345 # macOS/Linux
taskkill /PID 12345 /F # Windows
A better long-term solution is to assign explicit ports to your MCP servers to avoid conflicts with development tools. Choose ports in the 3100-3199 range or 8100-8199 range to stay out of the way of common development defaults:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["server.js", "--port", "3100"]
}
}
}
Cause 3: Wrong Transport Configuration
MCP supports two transport types: stdio (local process communication) and Streamable HTTP (network-based communication). Mixing them up is a common cause of connection refused errors because you end up trying to make an HTTP connection to something that is not an HTTP server.
stdio vs Streamable HTTP
Most MCP servers use stdio transport - the client spawns the server as a child process and communicates via stdin/stdout. No network port is involved. If you configure a stdio server with an HTTP URL, you will get connection refused because no HTTP server is running.
// WRONG - trying to connect to a stdio server via HTTP
{
"mcpServers": {
"filesystem": {
"url": "http://localhost:3000/mcp"
}
}
}
// CORRECT - stdio transport (client spawns the process)
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
}
}
// CORRECT - Streamable HTTP (for servers that actually serve HTTP)
{
"mcpServers": {
"remote-server": {
"url": "http://localhost:3100/mcp"
}
}
}
How to tell which transport a server uses: check its documentation or README. If setup instructions show a "command" field, it is a stdio server. If they show a "url" field, it is an HTTP server. The official MCP servers (@modelcontextprotocol/server-*) all use stdio transport.
Cause 4: Firewall Blocking Connections
Firewalls can block localhost connections, especially on corporate machines, VPN-connected systems, or when running servers in containers. Even connections to 127.0.0.1 can be blocked by overly aggressive security software.
macOS Firewall Rules
# Check if macOS application firewall is enabled
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
# Output: Firewall is enabled. (State = 1)
# Check if a specific app is blocked
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --listapps
# Temporarily disable to test (re-enable after!)
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
# Better: add your server process to the allowed list
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /usr/local/bin/node
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp /usr/local/bin/node
Windows Firewall Rules
# Check all firewall profiles
netsh advfirewall show allprofiles
# Add inbound rule for a specific port
netsh advfirewall firewall add rule name="MCP Server 3000" dir=in action=allow protocol=TCP localport=3000
# Add outbound rule if needed
netsh advfirewall firewall add rule name="MCP Client" dir=out action=allow protocol=TCP remoteport=3000
# List all rules to find conflicts
netsh advfirewall firewall show rule name=all | findstr "MCP"
# PowerShell alternative (more readable)
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*MCP*" } | Format-Table
Linux Firewall Rules
# Check iptables rules
sudo iptables -L -n --line-numbers
# Check if ufw is active (Ubuntu/Debian)
sudo ufw status verbose
# Allow localhost (usually already allowed by default)
sudo iptables -A INPUT -s 127.0.0.1 -j ACCEPT
sudo iptables -A INPUT -i lo -j ACCEPT
# If using ufw
sudo ufw allow from 127.0.0.1
# Check if firewalld is active (Fedora/RHEL)
sudo firewall-cmd --state
sudo firewall-cmd --list-all
# For SELinux issues (RHEL/CentOS)
sudo setsebool -P httpd_can_network_connect 1
Cause 5: Docker Networking Issues
Running MCP servers in Docker introduces networking complexity. There are several distinct failure modes depending on your Docker configuration.
Problem: Server Binds to 127.0.0.1 Inside Container
The most common mistake is that the server inside the container binds to 127.0.0.1 (loopback), which means it only accepts connections from within the container itself. Port mapping with -p forwards traffic to the container's network interface, not to its loopback.
# WRONG - server inside binds to 127.0.0.1
docker run -p 3000:3000 my-mcp-server
# The -p flag maps host:3000 to container:3000
# But if the server listens on 127.0.0.1:3000 inside the container,
# external connections (including from the host via port mapping) are refused
# FIX - tell the server to bind to 0.0.0.0
docker run -p 3000:3000 my-mcp-server --host 0.0.0.0
# Or set via environment variable (depends on server)
docker run -p 3000:3000 -e HOST=0.0.0.0 my-mcp-server
Problem: Docker Compose Service Discovery
In Docker Compose, containers communicate using service names, not localhost. If your MCP client runs on the host and the server runs in a container, use localhost with the mapped port. If both run in containers, use the service name.
# docker-compose.yml
services:
mcp-server:
image: my-mcp-server
ports:
- "3100:3000" # host:3100 -> container:3000
command: ["--host", "0.0.0.0"]
# Client config (running on host)
{
"mcpServers": {
"my-server": {
"url": "http://localhost:3100/mcp"
}
}
}
Problem: Docker Desktop Network Differences
Docker Desktop on macOS and Windows runs containers inside a Linux VM, adding another layer of networking. The host.docker.internal hostname resolves to the host machine from inside a container. On native Linux Docker, use --network host to share the host network stack directly.
Cause 6: SSL/TLS Certificate Issues
If your MCP server uses HTTPS and the certificate is self-signed, expired, or has a hostname mismatch, the client will refuse the connection. The error messages vary but always point to a certificate problem:
# Self-signed certificate
Error: self-signed certificate in certificate chain
UNABLE_TO_VERIFY_LEAF_SIGNATURE
# Expired certificate
Error: certificate has expired
CERT_HAS_EXPIRED
# Hostname mismatch
Error: Hostname/IP does not match certificate's altnames
ERR_TLS_CERT_ALTNAME_INVALID
Debugging SSL Certificates
# Check the certificate details
openssl s_client -connect localhost:3000 -servername localhost
# Shows certificate chain, expiry date, and issuer
# Check certificate expiry
echo | openssl s_client -connect localhost:3000 2>/dev/null | openssl x509 -noout -dates
# Output: notBefore=... notAfter=...
# Test with curl (verbose SSL output)
curl -vvv https://localhost:3000/mcp 2>&1 | grep -A5 "SSL"
# Skip certificate verification for testing ONLY
curl -k https://localhost:3000/mcp
NODE_TLS_REJECT_UNAUTHORIZED=0 node test-connection.js
For local development, use HTTP instead of HTTPS. For production remote MCP servers, use certificates from a trusted CA like Let's Encrypt. If you must use self-signed certificates, configure the client to trust your CA certificate rather than disabling verification entirely.
Some clients support environment variables to handle certificate issues. In your MCP server config, you can add:
{
"mcpServers": {
"remote-server": {
"url": "https://internal.company.com:3000/mcp",
"env": {
"NODE_EXTRA_CA_CERTS": "/path/to/company-ca.pem"
}
}
}
}
Client-Specific Fixes
Claude Desktop
After fixing the connection issue, restart Claude Desktop completely - close all windows and quit the app via Cmd+Q (macOS) or Exit from the system tray (Windows). Check logs for detailed error output:
- macOS:
~/Library/Logs/Claude/mcp*.log - Windows:
%APPDATA%\Claude\logs\mcp*.log - Linux:
~/.config/Claude/logs/mcp*.log
Claude Desktop retries server connections a few times on startup but does not retry indefinitely. If the server was not ready during the retry window, you must restart Claude Desktop after starting the server. See the Claude Desktop troubleshooting guide for complete restart procedures.
Cursor
In Cursor, open Settings and navigate to MCP to see server connection status. Cursor shows a red indicator for connection failures. Click the server name to see the error details. After fixing the issue, you can restart individual servers without restarting Cursor - click the restart button next to the server, or reload the window with Cmd/Ctrl + Shift + P and select "Developer: Reload Window." See the Cursor MCP error guide for more details.
VS Code
In VS Code, open the Output panel (View, Output) and select the MCP channel from the dropdown. The connection error with full details will be logged there. You may need to reload the window (Cmd/Ctrl + Shift + P, then "Developer: Reload Window") after fixing the issue.
Step-by-Step Diagnosis Flowchart
- Identify the transport - Is it stdio or HTTP? Check your config file. If you see a
"command"field, it is stdio. If you see a"url"field, it is HTTP. - For stdio - Connection refused is rare with stdio. The error is more likely spawn ENOENT or a server crash. Verify the command works manually in your terminal.
- For HTTP - Check whether the server is running with the port detection commands above.
- Server running? - If yes, test connectivity with
curl http://localhost:PORT/mcp. If curl also gets connection refused, the server may be binding to the wrong interface. - Server not running? - Start it manually and look for error output. Fix any startup issues first.
- Curl works but client fails? - Check for client-specific issues: wrong URL in config, firewall treating the client app differently, or the client using HTTPS when the server serves HTTP.
- Check firewall - Temporarily disable firewalls to rule them out. Re-enable and add specific rules after confirming.
- Check Docker - If using containers, verify port mapping, 0.0.0.0 binding, and network mode.
- Check VPN - Disconnect VPN to test. Some VPNs redirect or block localhost traffic.
- Restart client - Fully restart your MCP client after fixing the issue. Most clients do not auto-reconnect to previously failed servers.
Preventing Connection Refused Errors
For production MCP server deployments, implement these practices to minimize connection issues:
- Health check endpoints: Add a
/healthendpoint to your HTTP MCP server that returns a 200 OK. Monitoring tools and load balancers can use this to verify the server is alive. - Startup scripts: Use process managers like PM2 (Node.js) or supervisord (Python) to automatically restart crashed servers.
- Explicit port binding: Always specify the host and port explicitly in your server code rather than relying on defaults. Use
0.0.0.0for containerized deployments and127.0.0.1for local-only access. - Port documentation: Maintain a port registry documenting which ports each MCP server uses to prevent conflicts when adding new servers.
Security Considerations
When fixing connection refused errors, be careful not to over-expose your MCP servers. Binding to 0.0.0.0 makes the server accessible from any network interface, not just localhost. This means other devices on your network can reach the server. For development, this is usually fine. For production, use a reverse proxy with authentication or bind to 127.0.0.1 and tunnel connections through SSH. For comprehensive security best practices, see our MCP Server Security Guide.