Debugging MCP Servers with Client Logs
Where Claude Desktop, Cursor, and Cline write MCP server logs, what to look for when a server fails to connect, and how to add stderr instrumentation to custom servers.
Detailed Explanation
Where the Logs Live
Each client writes its own MCP debug log separately from the main app log:
- Claude Desktop (macOS):
~/Library/Logs/Claude/mcp.logplus one file per server, e.g.mcp-server-filesystem.log. - Claude Desktop (Windows):
%APPDATA%\Claude\logs\mcp.log. - Cursor: View → Output panel → select MCP from the dropdown.
- Cline: Output → Cline dropdown → search for "MCP".
What to look for
A healthy server logs an "Initialized" line shortly after the client launches. Common failure modes:
[error] Failed to spawn server "github": ENOENT
The command value couldn't be resolved. For npx/uvx this usually means PATH doesn't include the binary in the GUI app's environment — see troubleshooting common errors.
[error] Server "postgres" exited with code 1: connection refused
Postgres isn't running on the host:port in the connection string. Start the database or fix the URI.
[error] Invalid env var GITHUB_PERSONAL_ACCESS_TOKEN: 401 Bad credentials
The token is wrong or expired. Generate a new one — see github with token.
Instrumenting your own server
Custom servers (see custom stdio server) should write structured logs to stderr:
console.error(JSON.stringify({
level: "info",
msg: "tool called",
tool: name,
ts: new Date().toISOString(),
}));
stderr is captured by the client into the per-server log file, so anything you write there is visible without affecting MCP's stdio JSON-RPC frames.
Live tailing
For Claude Desktop on macOS:
tail -F ~/Library/Logs/Claude/mcp*.log
Open this in a terminal before restarting Claude — you'll see exactly when each server connects (or fails) during launch.
Making sense of warnings
A noisy "deprecated" warning from @modelcontextprotocol/server-... packages is usually safe to ignore short-term, but check the package's CHANGELOG before updating in case the env var names changed.
Use Case
Diagnosing why a config that 'worked yesterday' suddenly stopped — token expired, postgres restarted, npx cache corrupt, or PATH missing in the GUI's environment.