Claude Code MCP Failed to Connect: stdio, ENOENT, Timeouts
The Error Is a Status, Not a Root Cause
You added a local MCP server to Claude Code. The configuration looks reasonable. The Python script works from Terminal. The database is running. Then:
$ claude mcp list
secure-local-database:
✅ Failed to connectFor a local MCP server using stdio, Claude Code is not opening a TCP connection to your Python process. It launches the server as a subprocess and communicates through standard input and standard output. A local MCP server can fail to connect even though no network connection was ever attempted. (MCP Python SDK)
Claude Code can show MCP servers in several states: Connected, Needs authentication, Failed to connect, Pending approval, and Rejected. A server showing Failed to connect means Claude Code could not establish the configured MCP integration. It does not tell you which architectural layer failed. Project-scoped servers can also remain at Pending approval until the workspace and server configuration are explicitly trusted. (Claude Code Docs)
The debugging principle: do not start at the server code. Start at the first boundary Claude Code actually attempted to cross.
Configuration
|
v
Trust / approval
|
v
Process or network transport
|
v
MCP protocol
|
v
Tool discovery
|
v
Tool execution
|
v
Downstream systemProve those layers in order and most MCP failures stop looking mysterious.
Identify the Transport Before Touching Anything
MCP supports different transport models. The two that matter for this article:
| Local stdio MCP | Remote HTTP MCP |
|---|---|
| Client launches a subprocess | Server is already running elsewhere |
| Communication uses stdin/stdout | Communication uses HTTP |
| Executable path matters | URL, DNS and routing matter |
| OS permissions matter | TLS, proxy and firewall matter |
| stdout is protocol traffic | HTTP body/headers carry protocol |
ENOENT is meaningful | Connection refused/HTTP status is meaningful |
| Local environment variables matter | OAuth/HTTP credentials often matter |
The current MCP Python SDK describes stdio as the default transport for local servers and Streamable HTTP as the deployment transport. SSE remains available for compatibility but is not recommended for new servers. (MCP Python SDK)
Before searching for MCP connection refused, ask whether there is actually a network connection here. For a normal local Claude Code stdio server, the answer is no.
Failure Class 0: The Server Is Waiting for Approval
Run claude mcp list. If you see Pending approval, your Python server has not failed. Claude Code has not authorized that project MCP configuration to run.
Project-scoped MCP servers live in the repository's .mcp.json. Claude Code requires approval for those servers in interactive sessions because a repository should not be able to clone itself onto a developer's machine and silently instruct Claude Code to execute an arbitrary local process. A repository cannot simply commit its own approval and bypass an untrusted-workspace decision. (Claude Code Docs)
Open Claude Code interactively with claude, review the workspace trust prompt, then check again. For details: claude mcp get secure-local-database.
This behavior is not developer inconvenience. It is a supply-chain control. A project-level MCP configuration can contain a command that executes software on an employee workstation. Requiring trust before that configuration runs prevents version-controlled configuration from becoming an automatic local code-execution mechanism. An enterprise should preserve that boundary rather than training developers to bypass it.
Failure Class 1: Claude Code Cannot Find the Executable
You can run python3 /opt/company-mcp/server.py from your shell. Claude Code fails. Your interactive shell may initialize PATH modifications, pyenv, conda, uv, nvm, fnm, Volta, Homebrew, or corporate shell scripts before you type a command. The process launching your MCP server may see a different environment.
Anthropic explicitly documents the spawn ... ENOENT failure class when Claude Code cannot find the executable configured for a stdio MCP server, and recommends supplying the full executable path. (Claude Code Docs)
On Linux or macOS: which python3, which uv, which node. On Windows: where.exe python. Then configure the resolved executable:
{
"mcpServers": {
"secure-local-database": {
"type": "stdio",
"command": "/home/me/audit-mcp/.venv/bin/python",
"args": ["/home/me/audit-mcp/server.py"]
}
}
}Do not solve PATH problems by inventing a smaller PATH. A common recommendation is "PATH": "/usr/local/bin:/usr/bin:/bin". Sometimes that works. Sometimes it removes the exact runtime you need. The better production pattern is: pinned runtime, pinned environment, absolute executable, absolute server path. This also makes the MCP deployment reproducible on another developer machine or CI runner.
Failure Class 2: OS Refuses to Run the Executable
If you launch a Python script directly, check ls -l /opt/company-mcp/server.py and make sure the file is executable with a valid interpreter line. A cleaner configuration uses the interpreter as the executable:
{
"command": "/opt/company-mcp/.venv/bin/python",
"args": ["/opt/company-mcp/server.py"]
}Check directory traversal permissions: ls -ld /opt and ls -ld /opt/company-mcp. A file can be readable while a parent directory remains inaccessible. This becomes particularly relevant on hardened Linux workstations, mounted enterprise volumes, WSL, containers, and corporate endpoint-management environments. A permission failure is an operating-system boundary. Changing MCP protocol settings will not fix it.
Failure Class 3: The Process Starts and Crashes Immediately
Claude Code successfully launches Python. Python starts. Then something throws: ModuleNotFoundError, ImportError, KeyError, certificate error, database authentication error. From Claude Code's perspective, the MCP server disappeared. From Python's perspective, it crashed before it ever became a usable server.
The highest-value diagnostic is simple: run exactly the configured command yourself. If .mcp.json says "command": "/home/me/mcp/.venv/bin/python" with "args": ["/home/me/mcp/server.py"], run /home/me/mcp/.venv/bin/python /home/me/mcp/server.py. A healthy stdio server run manually may appear to hang silently because it is waiting for a host to send protocol traffic over stdin. There is no port to open and no server listening banner. (MCP Python SDK)
If your process immediately exits instead, find the startup exception first.
Failure Class 4: stdout Corrupts the stdio Protocol
The server can appear healthy while corrupting the protocol. For a stdio MCP server, stdout is the protocol wire. The MCP transport specification reserves it for MCP messages. Local diagnostic logs should go to stderr.
Use Python logging instead of print:
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("Starting database MCP server")The current Python SDK moves the wire to a private descriptor during serving and diverts flushed stdout to stderr, but output flushed to stdout before serving begins still lands on the wire. A dependency that prints during import can contaminate the transport without your server ever calling print(). (MCP Python SDK)
Do not use sys.stdout.flush() as a protocol fix. If invalid output has already been written, flushing ensures those invalid bytes reach the protocol stream. The fix is to not use stdout for non-protocol output.
Failure Class 5: Missing Environment Configuration
Database MCP servers commonly depend on DATABASE_URL or API_TOKEN. Your shell may have that variable. Claude Code's MCP subprocess may not. Project .mcp.json supports environment-variable expansion using ${VAR} and ${VAR:-default} in commands, arguments, environment values, URLs, and HTTP headers. (Claude Code Docs)
{
"mcpServers": {
"secure-local-database": {
"type": "stdio",
"command": "/absolute/path/to/.venv/bin/python",
"args": ["/absolute/path/to/server.py"],
"env": {
"DATABASE_URL": "${MCP_DATABASE_URL}"
}
}
}
}Then outside version control: export MCP_DATABASE_URL='postgresql://readonly_user:...@127.0.0.1:5432/audit' and run claude.
If ${MCP_DATABASE_URL} is not defined and has no fallback, the configuration does not fail to load. Claude Code reports a missing-variable warning and leaves the literal expression unexpanded. That can create a confusing second-order failure if the downstream library attempts to interpret the placeholder as a real connection value. Treat an unresolved secret placeholder as a configuration failure even if Claude Code technically loads the file.
Do not debug secrets by printing them. Log bool(os.getenv("DATABASE_URL")) instead of the value itself. A troubleshooting log can outlive the incident and turn a short connection problem into a credential exposure. The MCP security guidance consistently emphasizes reducing credential exposure and limiting server privileges. (MCP Security)
Failure Class 6: The Server Is Slow, Not Broken
A server can be correct and still take too long to become available. Current Claude Code gives us several timing concepts that should not be confused.
| Parameter | Default | What It Controls | When to Change |
|---|---|---|---|
MCP_TIMEOUT | 30000 (30s) | Timeout for an individual MCP server startup attempt | Increase only after confirming startup correctness; investigate why startup is slow first |
MCP_CONNECT_TIMEOUT_MS | 5000 (5s) | Blocking connection window when non-blocking is disabled or server uses alwaysLoad | Increase if legitimate servers need more blocking time during startup |
MCP_CONNECTION_NONBLOCKING | 1 (on) | Whether startup waits for MCP servers before first query | Set to 0 to restore blocking 5-second wait |
MCP_DISCOVERY_CACHE | 1 (on) | Cross-process MCP discovery cache for remote servers | Set to 0 to force every server to connect at startup |
For a diagnostic test: MCP_TIMEOUT=60000 claude. But increasing the timeout should not become your permanent architecture without asking why startup is slow. A healthier server does lazy initialization: process starts, MCP becomes available, tool gets invoked, expensive dependency is initialized if needed. (Claude Code Env Vars)
Failure Class 7: Wrong Transport in Configuration
This configuration is incomplete:
{
"mcpServers": {
"company-api": {
"url": "https://example.com/mcp"
}
}
}Claude Code interprets entries without a type as stdio configuration. A URL-only server needs an explicit HTTP type: "type": "http". Claude Code reports a configuration error when a server has a URL but no type and tells the user to specify http, sse, or ws. (Claude Code Docs)
The MCP 2026-07-28 Protocol Change
MCP through the 2025-11-25 generation used an initialization lifecycle built around initialize / initialized and protocol-level sessions with Mcp-Session-Id. MCP 2026-07-28 removed that architecture. The new core protocol removed the handshake exchange and the protocol-level session. Requests are self-contained, and an optional server/discover RPC can be used when a client wants capability information in advance. (MCP Blog)
2025-era mental model:
connect -> initialize -> session -> requests
2026 model:
request carries protocol/client metadata
-> server processes requestThe current Python SDK v2 implements the new protocol while retaining compatibility with older clients and servers. Its client can probe modern discovery and fall back to legacy initialization when speaking to an older server. (MCP Python SDK v2)
For enterprise platform teams, this changes deployment architecture. Modern requests can be handled without requiring a protocol-level sticky session. A request can land on different server instances behind ordinary load balancing because the protocol version, client information, and capabilities travel with the request. (MCP Blog)
Remote MCP can fail at the gateway even when the server is healthy. The current MCP 2026-07-28 HTTP example uses headers including MCP-Protocol-Version, Mcp-Method, and Mcp-Name. If an intermediary strips or mishandles headers, expects obsolete session behavior, or applies the wrong authentication policy, the MCP server can be completely healthy while the client still fails. Capture the request at the server boundary and verify the properties you expect to survive.
Why the 2026 Protocol Change Matters to Infrastructure Teams
For an individual developer, removing the handshake may sound like protocol trivia. For an enterprise platform team, it changes deployment architecture.
Before (2025-era):
Client
|
v
Load Balancer
|
v
Sticky Server A
|
v
Protocol session state
After (2026-07-28):
Client
|
v
Load Balancer
/ | \
A B CApplication-level state can still exist. The protocol simply no longer requires its own implicit server session. For organizations standardizing MCP gateways, this makes ordinary horizontal scaling easier. It also creates a new migration concern: your proxies and gateways need to understand the traffic your new clients are actually sending.
Authentication Failures Are Different from Connection Failures
For remote MCP servers, 401 and 403 are meaningful. Claude Code currently recognizes those responses as authentication-related states and can mark a remote MCP server as needing authentication. It also supports OAuth flows for remote MCP integrations. (Claude Code Docs)
This is different from connection refused and different again from server returned 500. A useful remote diagnostic path is:
DNS resolves?
|
v
TCP/TLS succeeds?
|
v
HTTP endpoint exists?
|
v
Authentication succeeds?
|
v
MCP protocol accepted?
|
v
Tool discovered?
|
v
Tool succeeds?When all of that gets collapsed into "MCP handshake," teams end up adjusting protocol code to solve identity problems.
The Database Can Be Broken While MCP Is Healthy
The most important distinction for secure local database MCP projects: a real database connection refusal can happen at the final boundary, and that is not a local MCP transport error.
Claude Code
|
| stdio healthy
v
MCP server
|
| TCP connection fails
v
PostgreSQLFrom the same environment where the MCP process runs, test PostgreSQL independently: psql "$DATABASE_URL" -c 'select 1;'. Then verify host, port, database name, TLS requirements, CA/client certificates, username, password, database role, Docker network, SSH context, WSL boundary, and firewall rules. Once that works, test the MCP tool. This separation prevents a simple database listener problem from turning into a protocol rewrite.
Cursor Has the Same Architectural Problem
Cursor also supports MCP and can hit many of the same subprocess, environment, and downstream-resource failures. Current Cursor uses agent as the primary CLI entry point; cursor-agent remains available as a backwards-compatible alias. Current CLI releases expose MCP management through /mcp commands. (Cursor CLI Changelog)
The important point is not that Claude Code and Cursor store every setting identically. Their local MCP failure tree is structurally similar: can host resolve command, can process start, can MCP transport work, can tool be discovered, can tool reach target. If a server works in one MCP client but not another, compare the host environment, configuration, and runtime location before rewriting the server.
Remote Development Changes What localhost Means
This is a classic distributed-systems trap. You configure postgresql://127.0.0.1:5432/audit. It works on your laptop. Then you run your MCP server in a remote development environment. Now 127.0.0.1 means the remote machine, not your laptop.
The same confusion can appear with Cursor Remote SSH, dev containers, Codespaces-like environments, WSL, Docker, and remote agents. Before investigating MCP, draw the topology:
Where does the AI client execute?
Where does the MCP process execute?
Where does the database execute?
What machine does "localhost" refer to?That one diagram can save an hour.
Secure Database MCP Architecture
The easiest database MCP tool to expose is query(sql). It is also one of the broadest interfaces you can hand to an autonomous agent. Prefer narrower tools: get_recent_errors(service, since, limit), get_trace(trace_id), get_failed_jobs(since, limit). The tool schema becomes one security boundary.
Claude Code's own current MCP documentation uses PostgreSQL through DBHub as an example and explicitly recommends a read-only database user so Claude's queries cannot modify the database. (Claude Code Docs)
Claude Code
|
v
Approved MCP server
|
v
Narrow tool
|
v
Read-only DB role
|
v
Specific schema / viewsLocal stdio has a smaller remote attack surface than exposing a public HTTP listener. That does not make the server harmless. MCP's security guidance warns that local MCP servers can run with the privileges of the client process and recommends sandboxing and minimal default privileges. (MCP Security)
Claude Code's permission rules apply to tools, including MCP tools, before they execute. Claude Code's built-in OS sandbox applies to Bash commands and their child processes. It does not automatically mean every arbitrary MCP process has been placed inside the same operating-system sandbox. (Claude Code Sandboxing) Those are complementary controls. For a sensitive MCP server, launch it through an isolation mechanism appropriate to the threat model rather than assuming MCP permissions alone provide process containment.
The Enterprise Issue Is Larger Than One Developer Configuration
A developer sees Failed to connect. A CTO should see a new integration surface. MCP can connect AI agents to databases, source control, observability, CI/CD, cloud infrastructure, ticketing, communication systems, internal documents, and customer systems. That creates enormous productivity potential. It also means the AI tooling layer can become a new control plane across systems that were previously isolated from each other.
The enterprise problem is therefore not merely how to get MCP working. It is how to make MCP operable, governable, and auditable at scale.
A CTO-Level MCP Control Model
I would divide enterprise MCP governance into seven controls.
1. Server Provenance
Know who supplied each server. A production inventory should record server name, publisher, repository, version, transport, deployment location, business owner, technical owner, and review status. Installing an MCP package is equivalent to adding executable integration code to the environment. Treat it that way.
2. Capability Scope
Inventory not just servers but exposed tools. A server named database tells you almost nothing. These do: read_orders, create_ticket, deploy_service, delete_branch, run_sql. The security-relevant unit is the capability.
3. Identity and Authorization
Do not reuse application superuser credentials. Prefer agent identity, then MCP-specific service account, then minimum role, then minimum schema/API scope. For remote MCP, integrate authentication with enterprise identity rather than distributing long-lived secrets where possible.
4. Environment Isolation
Determine where MCP actually executes: developer workstation, remote host, container, enterprise gateway, or cloud worker. Then define filesystem, network, and secret boundaries accordingly.
5. Tool Approval Policy
Not every tool should have the same execution policy.
| Capability | Default Posture |
|---|---|
| Read documentation | Auto-allow |
| Read logs | Auto-allow with data policy |
| Query read-only analytics | Auto/limited |
| Create issue | Allow with scope |
| Modify repository | Review depending on branch |
| Trigger deployment | Approval |
| Modify infrastructure | Strong approval |
| Write production database | Exceptional |
| Delete resources | Explicit human approval |
Cursor's current agent controls already apply approval logic to MCP tool calls, and Claude Code similarly supports tool-level permission controls. (Cursor Auto-review)
6. Auditability
For a sensitive MCP tool call, an enterprise should be able to reconstruct: who initiated the session, which agent/model was involved, which MCP server/version handled it, which tool was invoked, with which normalized parameters, which identity executed the downstream action, what resource was affected, what result was returned, and whether a human approval was required. Without that evidence, MCP may improve productivity while reducing accountability. That is a bad trade.
7. Lifecycle Governance
MCP servers change. Dependencies change. Tool schemas change. Permissions expand. Teams forget old integrations. Treat MCP like any other software supply chain: approve, version, monitor, review permissions, patch, retire.
Approve
|
v
Version
|
v
Monitor
|
v
Review permissions
|
v
Patch
|
v
RetireClaude Code itself now supports managed organizational MCP configuration, including a centrally deployed server set and allowed/denied server controls. Cursor has similarly moved toward centrally distributed Team MCPs through organization marketplaces. That is where the enterprise market is heading: MCP is moving from individual developer configuration toward governed organizational infrastructure.
The Connection Error Can Be a Useful Security Signal
There is a tendency in development to treat every permission error as friction. MCP is one place where that instinct can be dangerous.
If the server cannot read a sensitive directory, the right fix is not automatically chmod -R 777. If the database rejects the agent, the right fix is not automatically giving it the application's production-owner credential. If the repository is waiting for MCP approval, the right fix is not automatically disabling workspace trust.
The acceptance criteria should be two things: integration works, and integration still has only the authority it needs. A fix that satisfies the first and breaks the second is not a production fix.
A Minimal MCP Server Is Your Best Diagnostic Tool
When a database MCP server refuses to connect, temporarily remove the database. The current stable MCP Python SDK is v2. Its high-level server class is MCPServer; the former v1 FastMCP name was changed as part of the v2 migration. (MCP Python SDK)
from mcp.server import MCPServer
mcp = MCPServer("transport-test")
@mcp.tool()
def health() -> dict[str, str]:
return {"status": "ok"}
if __name__ == "__main__":
mcp.run()With no transport argument, mcp.run() uses stdio. Prove Claude Code can reach health(). If that works, your transport is not the problem. Then add the next dependency. That turns one opaque failure into a sequence of small proofs.
Use MCP Inspector before blaming Claude Code: uv run mcp dev server.py. The Inspector launches the server and connects over stdio much like a real host would. (MCP Python SDK) If the Inspector cannot talk to the server, changing Claude Code's authentication or workspace settings is unlikely to fix the server implementation.
Production Configuration Pattern
{
"mcpServers": {
"audit-database": {
"type": "stdio",
"command": "/absolute/path/audit-mcp/.venv/bin/python",
"args": ["/absolute/path/audit-mcp/server.py"],
"env": {
"DATABASE_URL": "${MCP_AUDIT_DATABASE_URL}"
}
}
}
}The desirable properties are not the exact filenames. They are: explicit transport, explicit runtime, explicit server path, no password committed to Git, dedicated credential, narrow database privileges, reproducible environment. Those characteristics make troubleshooting easier and reduce security ambiguity at the same time.
What Not to Deploy
Avoid configurations that accumulate years of unexplained workarounds:
{
"env": {
"PATH": "/usr/local/bin:/usr/bin:/bin",
"PYTHONPATH": "/random/dependencies",
"DATABASE_URL": "postgresql://admin:password@prod/db",
"MCP_TRANSPORT_MODE": "stdio",
"SOME_FLAG_FROM_A_GITHUB_ISSUE": "true"
}
}The danger is not only that a setting is wrong. Future engineers cannot distinguish required architecture from historical superstition. A good MCP deployment should be explainable field by field. Do not add MCP_TRANSPORT_MODE=stdio unless your server actually consumes it. There is no universal MCP environment variable with that meaning. Do not add PYTHONPATH unless you can explain why. A reproducible Python server should have its dependencies installed into its environment.
The Troubleshooting Ladder
1. Identify stdio vs remote HTTP
2. Run claude mcp list
3. Resolve Pending approval / Rejected first
4. Confirm configuration type and scope
5. Confirm the executable exists (which / where.exe)
6. Use absolute runtime and server paths
7. Run the configured command manually
8. Confirm the process stays alive
9. Keep non-protocol output off stdout
10. Test a one-tool server (remove database)
11. Test with MCP Inspector (uv run mcp dev)
12. Check environment-variable warnings
13. Never print secrets while debugging
14. Check MCP_TIMEOUT only after startup correctness
15. Confirm MCP protocol/SDK generation
16. Inspect proxy/gateway behavior for remote MCP
17. Test the downstream database/API separately
18. Use a dedicated least-privilege identity
19. Prefer narrow tools over arbitrary privileged actions
20. Test one complete tool call from the real clientSymptom-to-Root-Cause Mapping
| Symptom | Start Here |
|---|---|
Pending approval | Workspace trust / project MCP approval |
Rejected | MCP approval settings |
spawn ... ENOENT | Executable path / PATH |
| OS permission denied | File or directory permissions |
| Process exits instantly | Import/config/runtime startup |
| Works in shell, fails in Claude Code | Environment, executable, cwd, secrets |
| Inspector cannot connect | MCP server/transport implementation |
| Invalid protocol data | stdout contamination |
| Slow server eventually works | Startup initialization / timeout |
| Missing-variable warning | .mcp.json environment expansion |
| URL server treated incorrectly | Missing type: "http" |
| Remote 401 / 403 | Authentication/authorization |
| MCP works, DB fails | Database/network/credential layer |
| Works locally, fails remotely | Runtime topology / meaning of localhost |
The most useful way to think about an MCP connection failure is not Claude Code cannot connect, change MCP settings. It is: did Claude trust the configuration, could it launch or reach the server, did the transport work, did the protocol work, was the tool discovered, did identity and authorization work, did the downstream system work. Once those boundaries are separated, most MCP handshake failures become ordinary engineering problems with much smaller search spaces.
For CTOs, the broader conclusion: MCP is becoming an execution bridge between AI agents and enterprise systems. Connection reliability matters, but the real architectural requirement is controlled connectivity. The right process, using the right identity, reaching the right resource, with the smallest necessary authority and enough evidence to reconstruct what happened afterward.
The Broader Architectural Lesson
Model Context Protocol is often presented as a connector standard. That description is accurate but incomplete. Once an AI agent can use MCP to cross from reasoning into databases, source repositories, cloud accounts, deployment systems, ticketing, observability, and business applications, MCP becomes part of the organization's execution architecture.
That means MCP deserves the same disciplines we already apply to APIs and privileged integration services: identity, authorization, least privilege, change management, observability, supply-chain review, isolation, incident response, and auditability.
The protocol makes integrations easier to create. It does not make their authority less consequential.
Final MCP Connection Checklist
[ ] Identify stdio vs remote HTTP
[ ] Run claude mcp list
[ ] Resolve Pending approval / Rejected first
[ ] Confirm configuration type and scope
[ ] Confirm the executable exists
[ ] Use an absolute runtime path where practical
[ ] Use an absolute server path
[ ] Run the configured command manually
[ ] Confirm the process stays alive
[ ] Keep non-protocol output off stdout
[ ] Test a one-tool server
[ ] Test with MCP Inspector
[ ] Check environment-variable warnings
[ ] Never print secrets while debugging
[ ] Check MCP_TIMEOUT only after startup correctness
[ ] Confirm MCP protocol/SDK generation
[ ] Inspect proxy/gateway behavior for remote MCP
[ ] Test the downstream database/API separately
[ ] Use a dedicated least-privilege identity
[ ] Prefer narrow tools over arbitrary privileged actions
[ ] Test one complete tool call from the real client
[ ] Preserve approval, audit and rollback boundariesIf you are deploying MCP integrations and need an independent systems advisor to audit your agent-to-database control boundaries, schedule a strategic evaluation.
Get new articles in your inbox
Occasional emails when I publish something worth reading. Unsubscribe anytime.
Subodh KC
AI Advisor & AI Systems Architect. Former Sr. Program Manager, HP Inc. Founder of HAIEC - Holistic AI Ethics & Compliance. Builds production AI systems from startups to global enterprise.
