
This fix is part of MCP troubleshooting, within The MCP Guide. Still stuck? Request a free MCP readiness review.
An MCP server in Docker usually fails to connect because the container cannot talk over stdin and stdout. The client launches docker run as a stdio process, so the container must keep standard input open (-i) and must run in the foreground. The fastest fix is to make your args read run -i --rm ... <image>, remove any -d flag, then restart the client and read its MCP log.
Checked against: VS Code's MCP troubleshooting table, the docker run reference, Docker's MCP Toolkit get-started guide and GitHub's MCP server README on 2 November 2026. eSEOspace did not run these steps; follow the vendor page if it differs.
Key Takeaways
- A Dockerized stdio server needs
-iso the container keeps stdin open for the client's messages. - Never use detached mode (
-d). VS Code's docs say the client needs the stdio connection, so the container must stay in the foreground. -e NAMEwith no value copiesNAMEfrom thedockerprocess environment, which is theenvblock of your MCP config.- Docker Desktop must be running before the client starts, or the
dockercommand has nothing to talk to. - Docker says its MCP Toolkit gateway takes about 15 to 25 seconds to start, so short client timeouts can kill it.
What this error means
Most local MCP servers use the stdio transport. The client starts a process and exchanges newline-delimited JSON-RPC messages over that process's stdin and stdout. When the process is docker, Docker sits in the middle and has to pass those streams through to the container.
Three things break that chain. The container may close stdin at once because -i is missing. It may detach and return control to the client, which then sees the process exit. Or it may start, but crash because a required variable never reached it.
The symptom looks different per client. Claude Desktop shows the server as failed. VS Code reports that the server exited. Claude Code lists it as failed in /mcp. The cause is usually the same.
Quick fix
- Start Docker Desktop (or the Docker daemon on Linux) and wait until it reports it is running.
- Run the image by hand in a terminal:
docker run -i --rm <image>. If Docker cannot pull or start it, fix that first. - Open your MCP config and check the
argsarray. It should start with"run", "-i", "--rm". - Delete
-dor--detachif present. Also remove-t, since the docker run reference describes-tas allocating a pseudo-TTY, which a JSON stream does not need. - Pass each secret as
"-e", "NAME"inargsand put the value in the config'senvblock. - Fully quit and reopen the client, then read the MCP log for that server.
GitHub's official README uses this exact shape for its Docker image:
"github": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "your-token"
}
}
The -e GITHUB_PERSONAL_ACCESS_TOKEN line has no value on purpose. The docker run reference shows that --env VAR1 with no value uses the variable already exported in the calling environment. The client sets it from env, and Docker copies it in. If you write the token inside args instead, it can end up in logs and process lists. Our secret leakage guide explains why that matters.
Fix by client / OS
Claude Desktop
Edit claude_desktop_config.json under mcpServers. The file lives at ~/Library/Application Support/Claude/ on macOS, %APPDATA%\Claude\ on Windows and ~/.config/Claude/ on Linux, per the MCP local server guide. Quit Claude Desktop fully after each change. If you use Docker's MCP Toolkit, its guide says to select Connect next to Claude Desktop on the Clients tab, then restart Claude Desktop.
Cursor
Put the same block in .cursor/mcp.json (project) or ~/.cursor/mcp.json (global). Cursor's MCP docs list "docker" as a valid command, as long as it is on your system path or written as a full path.
Claude Code
Use the -- separator so Claude Code does not read Docker's flags as its own:
claude mcp add github --env GITHUB_PERSONAL_ACCESS_TOKEN=your-token -- docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN ghcr.io/github/github-mcp-server
Claude Code's MCP docs say everything after -- is passed to the server command untouched, and --env goes after the server name and before --. Check the result with claude mcp list.
VS Code
In .vscode/mcp.json servers sit under servers, not mcpServers. The VS Code configuration reference warns: "When using Docker with stdio servers, don't use the detach option (-d)." In a remote or dev container window, the command runs in the remote environment, so Docker must be reachable there too.
Docker MCP Toolkit gateway
If you connect through the Toolkit, the client runs docker mcp gateway run --profile <profile>. Docker's get-started guide says the gateway "takes approximately 15-25 seconds to start," and recommends a 60-second startup timeout in one client whose default is 10 seconds. In Claude Code, the docs say you can raise the startup timeout with MCP_TIMEOUT, for example MCP_TIMEOUT=60000 claude. Docker's guide also shows a Windows client config that uses the full path C:/Program Files/Docker/Docker/resources/bin/docker.exe. Our Docker MCP Toolkit setup guide covers profiles in more depth.
Where to find the logs
- Claude Desktop:
mcp.logandmcp-server-SERVERNAME.login~/Library/Logs/Claude(macOS),%APPDATA%\Claude\logs(Windows) or~/.config/Claude/logs(Linux). - Cursor: Output panel (Cmd+Shift+U or Ctrl+Shift+U), then MCP Logs.
- VS Code: run MCP: List Servers, pick the server and choose Show Output.
- Claude Code:
/mcpin a session, orclaude mcp listin a terminal. - Docker itself:
docker ps -ashows containers that started and exited. With--rmthey vanish, so remove--rmbriefly while you debug.
If it still fails
- Image pull errors. Run
docker pull <image>by hand. A login or rate limit problem shows up there. - Wrong platform. An image built for one CPU architecture may not start on another. Docker prints a warning when you run it by hand.
- Server logs to stdout. If the server inside the container prints banners to stdout, the client reads them as broken JSON. Log to stderr only.
- Ports for OAuth. GitHub's README publishes a callback port with
-p 127.0.0.1:8085:8085for its local OAuth flow. Without the port, sign-in cannot finish. - It works by hand but not in the client. Compare the client's
envwith your shell. See env variables not passed.
Look up any JSON-RPC code you see in the log with the MCP error code lookup, or rebuild the block with the MCP config generator.
Next step
Once the container connects, decide what it should be allowed to touch. Containers help with isolation, but only if you limit mounts and network. Our guide to sandboxing local MCP servers covers that. If you want a team to review your setup, eSEOspace offers an MCP readiness audit.
More in the MCP Guide
Free tools: MCP Error Code Lookup · MCP Config Generator
Latest research: State of MCP: October 2026
Frequently asked questions
Why does my MCP container need -i?
The client sends every request on stdin. The docker run reference defines -i as "Keep STDIN open even if not attached." Without it, the server sees an empty input stream and the client gets no replies.
Can I run the container with -d and connect over HTTP instead?
Yes, if the server supports a remote transport such as Streamable HTTP. Then you publish a port and give the client a URL instead of a command. For a stdio server, detached mode will not work.
Do I need Docker Desktop for the MCP Toolkit?
Yes. Docker's guide says you enable it in Docker Desktop under Beta features with Enable Docker MCP Toolkit. Its pages describe the interface in Docker Desktop 4.62 and later.
Why does the server connect in one client and not another?
Each client reads a different file and some use different keys (servers in VS Code, mcpServers elsewhere). Timeouts and the PATH used to find docker also differ. Check the logs of the failing client first.











