Overview
An MCP server is an opaque subprocess from the client’s perspective. When the agent does something unexpected, the server logs are the primary diagnostic surface. Structured logs per tool call, correlation IDs that span client and server, and latency tracking turn a silent failure into a debuggable event. The MCP protocol also defines a native logging capability: notifications/message lets the server stream log entries to the client during a session. Use both.
Emit one structured JSON log line per tool call
Every tool invocation produces a log entry containing: timestamp, tool name, input arguments, result summary, duration in milliseconds, and error if one occurred. This is the minimum viable log.
{
"ts": "2026-05-14T10:21:00Z",
"tool": "search_issues",
"args": { "q": "is:open label:bug", "limit": 30 },
"result_count": 17,
"duration_ms": 412,
"error": null
}Log at the server boundary, not inside the downstream API client. The boundary log captures the full round-trip time including serialization, network latency to the downstream API, and deserialization. Logs inside the API client miss serialization overhead and do not capture request routing failures.
Never log the full result payload at INFO level. Log a result summary (count, status, first N characters of text). Full payloads belong at DEBUG level and should be off by default. See mcp-security for the redaction rules that apply before any logging.
Assign a correlation ID to every session and propagate it through tool calls
A correlation ID is a random identifier assigned when the session starts. Every log line for that session carries the ID. When an agent runs for 20 minutes and calls 40 tools, the correlation ID lets you filter the log to exactly that session’s activity.
Generate the ID at initialize time and include it in the response metadata. The client attaches it as a header or param on every subsequent call. Log it on every line.
import uuid
session_id = str(uuid.uuid4())
# Include in every log entry:
log_entry["session_id"] = session_idCorrelation IDs also span the MCP boundary. If the server calls a downstream API, pass the correlation ID as a request header (e.g. X-Correlation-ID). When a downstream error appears in that API’s logs, you can trace it back to the specific agent session.
Track tool latency and surface slow tools proactively
Tool latency is a signal. A tool that takes 2 000 ms when it previously took 200 ms indicates a downstream API problem, a caching regression, or a schema change that triggered a full scan. Track latency per tool, and alert or log at WARN level when a call exceeds a threshold.
Use a rolling median and p95. The median shows typical performance; the p95 shows tail latency that the agent experiences on roughly one in twenty calls. A p95 of 5 000 ms on a tool the agent calls 10 times per session adds 5 seconds of wall-clock delay in 10% of sessions.
Store per-tool latency in a structure the agent can query. A get_server_stats tool that returns tool call counts and average latency helps the agent reason about whether to retry or give up. Feed this data to eval pipelines to catch regressions.
Use MCP Inspector for interactive debugging during development
MCP Inspector is the official development tool for MCP servers. It provides a browser-based interface to: connect to a running server, list capabilities, call tools with custom arguments, inspect raw JSON-RPC messages, and view server-sent notifications in real time.
npx @modelcontextprotocol/inspector
# Opens at http://localhost:5173
# Connect to your server via stdio or HTTPUse Inspector before wiring a server into Claude Code. Verify that tools/list returns the expected schema, that tools/call with edge-case inputs returns structured errors rather than stack traces, and that resources/list returns the expected URI set. Issues caught in Inspector cost nothing; issues caught mid-session cost context and time.
Know the five most common MCP failure modes
Each failure mode has a distinct log signature.
-
Server fails to start. The client receives no
initializeresponse. Log: no entries at all, or a process exit event. Fix: check the server command, binary path, and environment variables in settings.json. -
Tool returns an unstructured error. The server throws an exception and returns a JSON-RPC error object with a stack trace in the message field. Log:
errorfield is non-null with a Python/Node traceback. Fix: wrap all tool handlers in try/except and return structured{ "code": ..., "message": ... }errors. -
Auth failure on downstream API. The tool call succeeds at the MCP layer but the downstream API returns 401. Log:
duration_msis low,errorcontains “unauthorized” or “401”. Fix: check environment variable substitution and token expiry. -
Rate limit hit on downstream API. The tool starts returning 429 errors. Log:
errorcontains “rate limit” or “429”,duration_msis near-zero. Fix: add per-session rate limiting at the MCP layer before the downstream call. See mcp-security. -
Schema mismatch causes silent wrong results. The tool returns results but they do not match what the model expected. Log:
result_countis unexpectedly 0, or response fields are missing. Fix: add schema validation on tool outputs and log a warning when the shape deviates from the declared schema.