MCP error -32000: Connection closed
Something on the other end of an MCP connection went away, and the transport reported it. This failure happens entirely between your client and an MCP server — a local process or a remote endpoint you configured — so it is not an API error, not your API key, not a quota, and not the model. No request reached the model provider and nothing was billed. Your prompt is irrelevant here: the tools that server was supposed to provide simply are not available.
Data as of 2026-09. Vendor limits and defaults change; check the official docs for current values before acting on any number below.
The number is a category, not a cause
-32000 sits in the block JSON-RPC 2.0 reserves for implementation-defined
server errors. It is the client saying “the channel is gone” in the only
vocabulary the protocol gives it. It is emitted by the transport layer, which
knows the pipe closed and knows nothing else — not the exit code, not the
stack trace, not whether the server printed a reason on its way out.
That has one consequence worth internalizing before you touch any config: the message is phase-ambiguous, and recovering the phase is the entire job. The identical string appears when a server dies during startup, when it exits three seconds later, and when a server that worked fine for twenty minutes gets killed and the next tool call finds a dead pipe. Those are three different problems with three different fixes, and the error text discriminates between none of them.
This is the practical difference between this message and
the Codex handshake failure,
which names the phase in the string itself. When a message says the connection
closed while waiting for the initialize response, you know the server never
became usable. -32000 promises you nothing of the kind.
Recovering the phase
Three observations, in the order that costs you the least time:
- Did the server ever come up in this session?
/mcpandclaude mcp listshow a health status per server:✔ Connected,! Needs authentication,✘ Failed to connect,⏸ Pending approval,✘ Connection error, or✘ Rejected. A server showing✘ Failed to connectnever started. One that you watched connect and that is now failing died mid-session. - Ask for the failure detail instead of guessing it. On
✘ Failed to connect,claude mcp listappends the detail andclaude mcp get <name>prints it on anIssue:line — the HTTP status or error code plus the server’s own error text, with credential-like text redacted. Note the asymmetry: no detail is appended to✘ Connection error. If that is the status you have, the CLI has nothing more to give you and the next section is where you go. - Time the failure. A server that dies in well under a second is crashing on its own startup path — a missing runtime, a bad argument, an import error. A server that takes noticeably longer and then reports closed is more likely failing on something it does at startup: reading a credential, resolving a host, opening a database.
Is retrying useful?
No — and unlike most retry questions, this one has already been answered for you automatically before the message reached your screen.
The reconnection behavior is not something you can improve by hitting the up
arrow. On a first connection over HTTP or SSE, a transient error (5xx,
connection refused, timeout) is retried up to 3 times before the server is
marked failed. When a remote server drops mid-session, the client reconnects
with exponential backoff, up to 5 attempts, starting at a 1-second delay and
doubling, showing the server as pending while it tries. By the time you read
-32000, that budget is spent. Re-sending your prompt re-runs a decision that
has already been made.
For stdio servers the answer is even more final: stdio servers are local processes and are never auto-reconnected. Once that process is gone, the session holds a dead handle for the rest of its life. No amount of retrying inside the session brings it back — you restart the server’s configuration and start a session that will spawn it again.
The useful version of “try again” is therefore a different action: fix something, then start a new session. If the second session fails identically, you are looking at a deterministic configuration problem, which is what the rest of this page is about.
Where the real reason is actually written
A stdio server is a child process, and a child process that dies has usually said why — on stderr, not through the protocol. That output does not appear in the error string, which is why this error reads as informationless while the explanation sits a few lines away in a log.
Run the exact command from your configuration by hand in a terminal, with the
same arguments and the same environment. Most startup crashes reproduce in one
second at a shell prompt and print the reason in plain text. When the command
works by hand but not from the client, you have learned something specific: the
difference is the environment, not the server. A client launched from a desktop
icon does not necessarily inherit the PATH your shell profile builds, so
npx, uv, or a version-managed runtime can be missing from exactly one of the
two contexts.
For the client’s own view of the same event, enable debug logging and read
~/.claude/debug/<session-id>.txt. On Windows that is
%USERPROFILE%\.claude\debug\.
Fix by what you observed
- Never connected, dies instantly — the command is wrong or the runtime is missing. Fix it in the config entry and verify by running it by hand first. Absolute paths to the interpreter remove an entire class of PATH problems.
- Never connected, dies after doing some work — it is failing on a credential or a network dependency it needs at startup. Check that the environment variables the server documents are actually reaching it.
- Connected, then died — something killed the process: an OOM kill, a crash under a specific tool call, a laptop suspend, or a supervisor. If it dies reproducibly on one tool, that tool is the trigger and the server’s own logs will show it.
- Remote server, drops repeatedly — you are on a network path with an intermediary that closes idle connections. If the failure instead names a status rather than a closed connection, you want the SSE stream 404 page instead, because a server that answers with a status is a routing problem, not a dead one.
- The entry has a
urlbut notype— that one is reported explicitly (MCP server "<name>" has a "url" but no "type"; add "type": "http" (or "sse" / "ws") to this entry), so if you are reading-32000it is not your problem.
A note on the credential half of this, because MCP servers run with your
privileges and usually hold tokens. Project-scoped servers live in .mcp.json
at the project root, which is a file people commit. Keep secrets out of it —
reference an environment variable rather than pasting a value, and when you
share a config in an issue or a chat, mask it (ghp_****). A server that fails
to start is a bad reason to paste a working token into a shared file.
Confirming it is really fixed
✔ Connected in /mcp is necessary and not sufficient — it means the handshake
finished, not that the server survives use. Start a fresh session, confirm the
status, then call one of that server’s tools twice, and prefer a call that
does real work over a trivial one. A server that connects and dies on first use
passes the status check and fails the only test that matters. If the server is
stdio, do this in a session started after your change, since the old session can
never respawn the process.
Related failures in the same area
If the server connects and the failure arrives after a successful tool call, you are in a different part of the pipeline: an oversized result gets rejected with MCPContentTooLargeError rather than a closed connection, and that is a budget problem, not a transport one. If several servers do connect and requests then fail before the model runs, check whether two of them collide on a tool name — that error blocks requests while every server reports healthy, which is a confusing combination if you are not expecting it. For the general question of which layer a given message comes from, the AI coding error triage tool sorts client-side transport failures from API-side ones, which is the first fork on this page.