GPTMap

Debugging MCP in Practice: Inspector, Logging Rules, and the Connection Troubleshooting Chain

The official method for debugging MCP integrations: the Inspector UI as first stop, stdio vs Streamable HTTP logging rules (notifications/message deprecated in spec 2026-07-28), startup root causes, and a five-step connection chain.

TL;DR
MCP debugging starts at the Inspector: an interactive UI that connects to stdio or Streamable HTTP servers and invokes tools. stdio servers log to stderr, never stdout; Streamable HTTP stderr is uncaptured -- aggregate or use OpenTelemetry. notifications/message is deprecated in spec 2026-07-28. Connection chain: logs, process, Inspector, server/discover, _meta, capabilities.
MCP debugging is the workflow for localizing integration failures between MCP servers and clients, using the official three tool layers: the MCP Inspector (interactive, transport-agnostic test UI -- the recommended first stop), server logging (stderr over stdio, OpenTelemetry across all transports), and client developer tools (logs and connection state). Connection failures follow a fixed chain: client logs, process status, standalone Inspector test, protocol version discovery via server/discover, and _meta field verification.

How to

  1. Read the client logs

    Most MCP clients expose logs and connection state (Claude Desktop is one example); read the most recent connection errors first.

  2. Verify the process is alive

    Confirm the server process is actually running: correct command path, required files present, permissions in order -- walk the three startup root-cause classes.

  3. Standalone Inspector test

    Decouple the server from the client: connect and invoke one tool in the Inspector alone to separate 'the server is broken' from 'the integration is broken'.

  4. Check protocol versions

    Call server/discover to list the protocol versions the server supports; a mismatch raises UnsupportedProtocolVersionError (-32022) with the supported versions in its data field.

  5. Verify _meta and capabilities

    Ensure every request carries io.modelcontextprotocol/protocolVersion and clientCapabilities; missing fields reject with -32602, and undeclared capabilities raise MissingRequiredClientCapabilityError (-32021) naming what is missing.

MCP integration failures have an officially defined tool layering: the MCP Inspector (interactive test UI), server logging (stderr / OpenTelemetry), and client developer tools (logs and connection state). Most "won't connect, invocation failed" problems do not require guessing -- walk the chain in this article and every step has a clear signal source. Based on the official MCP debugging guide (verified 2026-09-02), this piece turns it into an executable workflow and folds in the logging changes introduced by the 2026-07-28 spec version.

1. The Tool Landscape: Three Layers, Three Jobs

LayerWhat it answersKey facts
MCP InspectorIs the server itself good?Connects to stdio / Streamable HTTP; invokes tools / prompts / resources; watches the notification stream; officially positioned as your first stop
Server loggingWhat happened at runtimestdio logs to stderr (auto-captured by the host); OpenTelemetry across all transports; in-protocol notifications/message deprecated as of the 2026-07-28 spec
Client developer toolsWhat happened at the integration layerMost MCP clients expose logs and connection state; the official guide uses Claude Desktop as its example

The division of labor: the Inspector answers "is the server itself working", logs answer "what happened at runtime", client tools answer "what happened at the integration". Troubleshoot in that order -- decouple the server first, then the runtime, then the integration.

2. The Inspector: Decoupling the Server from the Integration

The Inspector is an interactive, transport-agnostic testing UI: connect a stdio or Streamable HTTP server, invoke tools, prompts, and resources directly, and watch the notification stream. Launch command (an established fact in our MCP tutorials): npx @modelcontextprotocol/inspector (Node 22.19.0+).

Its core value is variable separation: when the client cannot connect, connect the server standalone in the Inspector first --

# Standalone test of a stdio server (decoupled from any client)
npx @modelcontextprotocol/inspector ./your-server --your-flags

# In the Inspector UI: connect → list tools → invoke one → watch the notification stream
# Works in the Inspector = the problem is in the integration layer
# Fails in the Inspector too = the problem is in the server

3. Server Logging: stderr, OpenTelemetry, and One Deprecation

stdio transport: everything logged to stderr is captured automatically by the host application. Never write to stdout -- stdout is the protocol channel, and ordinary log output interferes with protocol operation. This is the most common self-inflicted stdio failure.

Streamable HTTP transport: stderr is not captured by the client. Use your own log aggregation or OpenTelemetry; inspect individual requests and SSE streams with curl and the browser DevTools Network panel.

The in-protocol logging change: the notifications/message mechanism is deprecated as of the 2026-07-28 spec version (still available during the deprecation window). New code sends logs to stderr or OpenTelemetry. When still using the mechanism, two rules apply:

  1. Logging has 8 RFC 5424 severity levels (debug through emergency);
  2. Clients opt in per request via the io.modelcontextprotocol/logLevel field in the request's _meta; servers must not send notifications/message for requests that omit the field.

The minimal server-side logging shape (matching the official examples):

import logging
from mcp.server import MCPServer

logger = logging.getLogger(__name__)
mcp = MCPServer("reports")

@mcp.tool()
async def fetch_report(report_id: str) -> str:
    logger.info("Fetching report %s", report_id)   # stderr: captured by the host
    return f"Report {report_id} is ready."
// Streamable HTTP / explicit protocol logging (inside the deprecation window):
await server.sendLoggingMessage({
  level: "info",
  data: "Server started successfully",
});

What to log? The official list names five event classes: startup steps, resource access, tool execution, error conditions, performance metrics.

4. Startup Failures: Three Root-Cause Classes and Two Frequent Sub-Causes

The official grouping: path issues (wrong executable path, missing required files, permission problems), configuration errors (invalid JSON syntax, missing required fields, type mismatches), and environment problems (missing variables, incorrect values, permission restrictions). Two sub-causes outnumber the rest:

Sub-cause one: the working directory is undefined. When a client launches a stdio server, the working directory may be undefined (like / on macOS) -- so use absolute paths everywhere in configuration and .env files. The official do-example (Claude Desktop's claude_desktop_config.json; the same principles apply to any stdio client):

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/data"
      ]
    }
  }
}

Do not write relative paths like ./data -- where they point depends on where the client was started from, which you do not control.

Sub-cause two: limited environment inheritance. Servers launched over stdio inherit only a limited, platform-dependent subset of environment variables. Provide or override them with an env key in the configuration:

{
  "mcpServers": {
    "myserver": {
      "command": "mcp-server-myapp",
      "env": {
        "MYAPP_API_KEY": "some_key"
      }
    }
  }
}

5. Connection Failures: The Five-Step Chain

When a server fails to connect, walk the official chain -- each step has a clear signal:

  1. Check client logs -- most clients expose logs and connection state.
  2. Verify the process is running -- path, missing files, permissions; walk the three startup classes.
  3. Standalone Inspector test -- separate "the server is broken" from "the integration is broken".
  4. Verify protocol compatibility -- call server/discover to see which protocol versions the server supports; a mismatch raises UnsupportedProtocolVersionError (-32022) listing the supported versions in its data field.
  5. Check _meta and capability declarations -- every request must carry io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities (clients should also include io.modelcontextprotocol/clientInfo); a missing required field is rejected with -32602 (Invalid params -- the same code returned for many other malformed inputs, so read it in context), and a request needing an undeclared capability (such as elicitation) raises MissingRequiredClientCapabilityError (-32021) naming the missing capabilities.

A practical trick for step five: put the request _meta next to the server/discover response and confirm both sides declared what you assume they declared.

6. Common Mistakes and Troubleshooting

  • Logging to stdout: fatal over stdio -- stdout is the protocol channel. Logs go to stderr, always.
  • Relative paths: an undefined working directory makes "works from the command line, fails in the client" the default outcome. Absolute paths in config and .env.
  • New code still using notifications/message: deprecated in the 2026-07-28 spec; usable in the window, but new code goes to stderr / OpenTelemetry with the logLevel subscription rules.
  • Editing client config before testing in the Inspector: decouple first -- prove the server works standalone, then touch the integration.
  • Reading -32602 as "wrong business parameters": it can also come from _meta missing protocolVersion / clientCapabilities -- check _meta before business params.
  • Logging that answers nothing: "request received" alone is worthless; instrument the official five event classes (startup, resource access, tool execution, errors, performance).

7. Next Steps

Key points

  • The MCP Inspector is the officially designated first stop: interactive and transport-agnostic, connecting to stdio / Streamable HTTP servers, invoking tools / prompts / resources, and watching the notification stream
  • stdio servers log to stderr (captured automatically by the host) and must never log to stdout -- it interferes with protocol operation; Streamable HTTP stderr is not captured, so use your own aggregation or OpenTelemetry, with curl / browser DevTools Network panel for requests and SSE streams
  • In-protocol logging notifications/message is deprecated as of the 2026-07-28 spec (still available in the deprecation window); logging has 8 RFC 5424 severity levels (debug through emergency), subscribed per request via io.modelcontextprotocol/logLevel in _meta -- servers must not send for requests that omit the field
  • stdio servers inherit only a limited, platform-dependent subset of environment variables; inject custom ones via the env key in config; the working directory may be undefined (like / on macOS) -- use absolute paths in config and .env files
  • Startup failures have three root-cause classes: path (wrong executable path, missing files, permissions), configuration (JSON syntax, missing required fields, type mismatches), and environment (missing/incorrect variables, permission restrictions)
  • The connection troubleshooting chain: client logs → process alive → standalone Inspector test → server/discover for protocol versions (UnsupportedProtocolVersionError -32022) → required _meta fields io.modelcontextprotocol/protocolVersion and clientCapabilities (missing → -32602 Invalid params); undeclared capabilities → MissingRequiredClientCapabilityError (-32021)
  • Five event classes worth logging: startup steps, resource access, tool execution, error conditions, performance metrics

Frequently asked questions

The official debugging guide is explicit: the MCP Inspector -- an interactive, transport-agnostic testing UI. It connects to stdio or Streamable HTTP servers, invokes tools, prompts, and resources directly, and watches the notification stream; officially, it should be your first stop. Prove the server works standalone in the Inspector before connecting it back to the client -- this separates 'the server is broken' from 'the client integration is broken'.

Official references

Related articles

Subscribe to GPTMap Weekly

One email every Monday: curated OpenAI updates, deep dives, and best practices. No ads, unsubscribe anytime.

GPTMap EditorialPublished 2026-09-02 6 min read
Test environment (EEAT)
Last tested: 2026-09-02
Model used: gpt-5.6