DEV Community

Cover image for How npx @latest Was Silently Killing My MCP Servers (and the Doctor I Built for It)
Ayoola Damisile
Ayoola Damisile

Posted on

How npx @latest Was Silently Killing My MCP Servers (and the Doctor I Built for It)

How npx @latest Was Silently Killing My MCP Servers (and the Doctor I Built for It)

If you use AI coding agents — Claude Code, Cursor, opencode, VS Code, Codex — you've probably seen this at least once:

MCP error -32001: Request timed out
Enter fullscreen mode Exit fullscreen mode

No server name. No cause. Just your tools silently missing, or the whole agent hanging at startup while you stare at a spinner.

This is the story of how I found out my own config was the killer, what I learned about how MCP startup actually works, and the open-source CLI I built so nobody else has to lose an afternoon to this again.

How MCP startup actually works

Every time your agent boots, for each server in your MCP config it:

  1. Spawns the process — e.g. npx -y some-package for stdio servers
  2. Opens the transport — stdin/stdout for stdio, HTTP/SSE for remote
  3. Sends a JSON-RPC initialize request and waits for the answer
  4. Calls tools/list to discover what the server can do

If step 3 doesn't complete inside the timeout window (often 30–90s depending on the client), you get -32001: Request timed out. And here's the annoying part: the error doesn't tell you which server failed or why. A config with ten servers gives you one useless line.

The trap: @latest is a boot-time lottery

My configs looked perfectly reasonable:

"context7": {
  "command": "npx",
  "args": ["-y", "@upstash/context7-mcp@latest"]
}
Enter fullscreen mode Exit fullscreen mode

That's the pattern every tutorial copies. But @latest means npm hits the registry on every single agent startup to resolve the newest version before it can even spawn the server.

On a fast network, you never notice. On my network — and a lot of networks in this part of the world — that registry round-trip at boot can take longer than the handshake timeout. Result: every server configured this way dies on every boot. Not sometimes. Every time.

And @latest is only one way to get the same useless error. During my audit I found servers whose commands weren't on PATH, ${VAR} references to env vars I'd never set, an endpoint that had been down for weeks, and the same server name defined in three different agents' configs.

Diagnosing by hand is miserable

The manual loop looks like this: read the config, guess which entry is suspicious, spawn the command yourself in a terminal, watch it hang, kill it, check npm ls -g, repeat. An afternoon gone.

What I actually wanted was a doctor: point it at my machine, have it find every config, and run the same startup conversation my agent runs — then tell me exactly where it breaks.

That tool didn't exist, so I built it.

Enter fixmcp

fixmcp is one command, zero install:

npx fixmcp
Enter fullscreen mode Exit fullscreen mode

It discovers configs from Claude Code (~/.claude.json), Claude Desktop, Cursor, VS Code, opencode (JSONC) and Codex CLI (TOML), then checks every server two ways:

Static checks — command resolution (is it even on PATH?), ${VAR} env references, known footguns like npx without -y (which can hang forever on an interactive prompt), and duplicate server names across agents.

Real handshakes — this is the part that matters. fixmcp doesn't just lint your config. It spawns every stdio server and performs an actual JSON-RPC initialize + tools/list conversation. You get per-server latency and discovered tool counts. When a server dies, you get its stderr tail — so instead of a bare timeout you see ECONNREFUSED 127.0.0.1:5432 or whatever the real cause is.

postgres-mcp (stdio)
  ✗ [handshake] initialize failed after 5012ms
    stderr: ECONNREFUSED 127.0.0.1:5432
filesystem (stdio)
  ✓ [handshake] initialize OK in 412ms, 11 tool(s).
Enter fullscreen mode Exit fullscreen mode

The fix: stop gambling on the registry

Once you know the cause, the fix for @latest is simple: install once, point the config at the installed script.

npx fixmcp --fix automates that. It rewrites fragile npx pkg@latest entries to cached direct-node paths:

"context7": {
  "command": "C:\\Program Files\\nodejs\\node.exe",
  "args": ["C:\\Users\\you\\AppData\\Roaming\\npm\\node_modules\\@upstash\\context7-mcp\\dist\\index.js"]
}
Enter fullscreen mode Exit fullscreen mode

Zero network at startup. Millisecond launches. And it's careful about it:

  • a <file>.fixmcp.bak backup is written before any change
  • only plain-JSON configs are rewritten
  • comment-bearing formats (opencode's JSONC, Codex's TOML) get exact manual instructions instead — a naive rewrite would destroy comments
  • updating a server afterwards means one deliberate npm i -g <pkg>, which beats gambling on the registry every boot anyway

Windows is first-class (because I live there)

Most MCP tooling quietly assumes macOS/Linux. fixmcp handles the Windows realities: .cmd/.bat commands route through cmd.exe (Node ≥ 20 refuses to spawn them directly), PATH lookup honors PATHEXT, and child process trees are cleaned up with taskkill /T. It's tested on Ubuntu, Windows and macOS across Node 20 and 22 in CI.

Built for humans and agents

fixmcp --json outputs machine-readable findings with proper exit codes (0 healthy, 1 errors), so your coding agent can run the doctor itself. Drop this in your project's AGENTS.md:

When the user reports MCP server errors or timeouts, run:
  npx fixmcp --json
Read findings[].message/detail for exact causes. Apply config fixes with:
  npx fixmcp --fix
Then re-run without flags to verify.
Enter fullscreen mode Exit fullscreen mode

Try it

npx fixmcp
Enter fullscreen mode Exit fullscreen mode

If it finds something broken in your setup — or you hit a failure mode it doesn't catch — I genuinely want to hear about it in the comments or the issue tracker. The roadmap is being shaped by real-world failure reports.

And if it saved you a debugging session, a star on the repo helps the next person find it. ⭐

Top comments (0)