Debug MCP 404 and connection errors by systematically checking the transport layer, endpoint URL, authentication headers, and server-side logs. MCP servers fail with 404 when the URL path is wrong, the server is not running, or a reverse proxy strips the path prefix. Connection errors (ECONNREFUSED, timeout, ENOTFOUND) point to network or DNS issues. This guide walks through each failure mode in order of likelihood, with concrete commands to test at each step.
Prerequisites
- A running MCP server (local or remote)
- curl or httpie installed for endpoint testing
- Access to the MCP server configuration file
- Python 3.8+ for scripted debugging
Walkthrough
Step 1: Check transport configuration
Verify the MCP client config points to the correct transport type (stdio, SSE, or HTTP) and URL.
import json, os
# Common MCP config locations:
# Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
# .mcp.json in project root
# VS Code settings.json
def check_mcp_config(config_path: str) -> dict:
with open(config_path) as f:
config = json.load(f)
servers = config.get('mcpServers', {})
issues = []
for name, cfg in servers.items():
if 'url' in cfg:
url = cfg['url']
if not url.startswith('http'):
issues.append(f'{name}: URL missing http/https prefix')
if url.endswith('/'):
issues.append(f'{name}: trailing slash may cause 404')
elif 'command' not in cfg:
issues.append(f'{name}: no url or command defined')
print(f'{name}: {cfg.get("url", cfg.get("command", "unknown"))}')
return {'servers': list(servers.keys()), 'issues': issues}
# Check a config file:
# check_mcp_config(os.path.expanduser('~/.mcp.json'))
print('Check your .mcp.json or claude_desktop_config.json for URL accuracy')Step 2: Test the URL with curl
Test the MCP server endpoint directly with curl to isolate whether the issue is client-side or server-side.
# Run these curl commands to test your MCP server:
# Replace URL with your actual MCP server address
import subprocess
def test_endpoint(url: str, api_key: str = '') -> dict:
headers = ['-H', f'x-api-key: {api_key}'] if api_key else []
# Test basic connectivity
try:
result = subprocess.run(
['curl', '-s', '-o', '/dev/null', '-w', '%{http_code}', url] + headers,
capture_output=True, text=True, timeout=10)
status = result.stdout.strip()
print(f'HTTP {status} from {url}')
if status == '404':
print(' -> Check URL path. Common fix: remove or add trailing /sse or /mcp')
elif status == '401' or status == '403':
print(' -> Authentication issue. Check API key or token.')
elif status == '000':
print(' -> Connection refused. Server not running or wrong port.')
return {'url': url, 'status': status}
except subprocess.TimeoutExpired:
print(f'Timeout connecting to {url}')
return {'url': url, 'status': 'timeout'}
test_endpoint('https://api.scavio.dev/api/v1/search')Step 3: Verify authentication
Confirm the API key or token is being sent correctly. MCP 404s sometimes mask auth failures on certain server implementations.
import requests
def verify_auth(url: str, api_key: str) -> dict:
# Test with auth
try:
resp_auth = requests.post(url,
headers={'x-api-key': api_key, 'Content-Type': 'application/json'},
json={'platform': 'google', 'query': 'test'}, timeout=10)
print(f'With auth: HTTP {resp_auth.status_code}')
except Exception as e:
print(f'With auth: {e}')
resp_auth = None
# Test without auth
try:
resp_no_auth = requests.post(url,
json={'platform': 'google', 'query': 'test'}, timeout=10)
print(f'Without auth: HTTP {resp_no_auth.status_code}')
except Exception as e:
print(f'Without auth: {e}')
resp_no_auth = None
return {
'auth_status': resp_auth.status_code if resp_auth else 'error',
'no_auth_status': resp_no_auth.status_code if resp_no_auth else 'error',
}
verify_auth('https://api.scavio.dev/api/v1/search', os.environ.get('SCAVIO_API_KEY', ''))Step 4: Check server logs for root cause
If curl succeeds but MCP client fails, the issue is in the client-server handshake. Check server logs for the actual error.
# Common MCP server log locations:
# Docker: docker logs <container_name>
# systemd: journalctl -u mcp-server
# Local: check stdout/stderr of the server process
def diagnose(test_results: dict) -> str:
status = str(test_results.get('status', ''))
diagnoses = {
'404': 'URL path mismatch. Try: /sse, /mcp, /v1/search, or remove trailing slash.',
'401': 'API key invalid or missing. Regenerate at provider dashboard.',
'403': 'API key valid but lacks permission. Check plan tier.',
'500': 'Server-side error. Check server logs. Retry in 60s.',
'502': 'Reverse proxy cannot reach backend. Check server is running.',
'503': 'Server overloaded. Implement retry with backoff.',
'timeout': 'Server unreachable. Check DNS, firewall, and port.',
'000': 'Connection refused. Server not running on that host:port.',
}
return diagnoses.get(status, f'Unexpected status {status}. Check server logs.')
print(diagnose({'status': '404'}))
print(diagnose({'status': 'timeout'}))Python Example
import requests, os
def debug_mcp(url, api_key):
try:
r = requests.post(url, headers={'x-api-key': api_key},
json={'platform': 'google', 'query': 'test'}, timeout=10)
print(f'Status: {r.status_code}')
if r.status_code == 200:
print('Connection OK')
elif r.status_code == 404:
print('Check URL path: try /sse, /mcp, or /v1/search')
except requests.exceptions.ConnectionError:
print('Connection refused: server not running?')
except requests.exceptions.Timeout:
print('Timeout: check firewall/DNS')
debug_mcp('https://api.scavio.dev/api/v1/search', os.environ.get('SCAVIO_API_KEY', ''))JavaScript Example
async function debugMcp(url, apiKey) {
try {
const r = await fetch(url, {
method: 'POST',
headers: {'x-api-key': apiKey, 'Content-Type': 'application/json'},
body: JSON.stringify({platform: 'google', query: 'test'}),
signal: AbortSignal.timeout(10000)
});
console.log(`Status: ${r.status}`);
if (r.status === 404) console.log('Check URL path');
} catch (e) {
console.log(`Error: ${e.message}`);
}
}
debugMcp('https://api.scavio.dev/api/v1/search', process.env.SCAVIO_API_KEY);Expected Output
A systematic debug checklist that identifies the root cause of MCP 404 and connection errors through transport, URL, auth, and log analysis.