MCP debugging is most effective when you debug the protocol session before debugging the model experience. A broken server can fail during process startup, initialize negotiation, capability discovery, schema validation, authorization, backend execution, transport framing, or host rendering. Each layer has different evidence.
The MCP Inspector is the fastest way to isolate server behavior because it lets you connect to a server, inspect advertised capabilities, call tools, read resources, fetch prompts, and observe protocol-level failures without the noise of a full host application.
This page teaches a repeatable troubleshooting workflow for local stdio servers, remote HTTP servers, host integrations, and production deployments. The goal is to move from “the MCP server is broken” to a precise diagnosis such as “the host is running an old build,” “tools/list does not include the expected capability,” “stdout is polluted before initialize,” or “the remote server rejects the token scope during invocation.”
Debug MCP from the outside edge inward: can the process or endpoint start, can initialize complete, can capabilities be discovered, can a minimal valid request execute, can invalid and unauthorized requests fail safely, and only then does the host UX become the main suspect?
Run the Inspector against the same command, build output, environment variables, and remote URL that your real host uses. Otherwise you may prove a different server works.
# Local stdio server from built JavaScript
npx @modelcontextprotocol/inspector node dist/index.js
# Local TypeScript server during development
npx @modelcontextprotocol/inspector npx tsx src/index.ts
# Pass environment variables required by the server
npx @modelcontextprotocol/inspector -e API_BASE_URL=https://internal.example.com -e LOG_LEVEL=debug node dist/index.js
# Rebuild when the host points at dist/
npx tsc
npx @modelcontextprotocol/inspector node dist/index.js
| Layer | Typical Symptom | Best Evidence |
|---|---|---|
| Startup | Server never appears or exits immediately. | Manual command run, exit code, stderr, host process logs. |
| Initialize | Client connects but session fails before tools/resources/prompts appear. | Initialize request/response, protocol version, capability metadata. |
| Discovery | Expected tool, resource, or prompt is missing. | tools/list, resources/list, prompts/list output and permission filters. |
| Validation | Tool rejects input that looks correct from the model conversation. | Input schema, parsed arguments, field-level validation errors. |
| Authorization | Capability exists but call is denied or hidden for one user. | Token scopes, tenant, object-level policy decision, audit logs. |
| Backend execution | MCP call starts but external API, database, filesystem, or queue fails. | Dependency logs, timeout metrics, retry behavior, sanitized exception class. |
| Transport | Works locally but not remotely, or messages are truncated/interleaved. | HTTP status, headers, session IDs, proxies, stdout/stderr discipline. |
| Host rendering | Inspector result is correct but the host displays or selects it poorly. | Host logs, capability descriptions, response shape, UX constraints. |
Start with the narrowest reproduction that still shows the failure. If a server cannot start from the same command the host uses, do not debug model behavior. If it starts but cannot initialize in Inspector, focus on protocol setup. If initialize and discovery work in Inspector but the host fails, compare host configuration and runtime environment.
This order saves hours because MCP failures often look mysterious from the chat surface while being obvious at the protocol layer. A missing build, wrong absolute path, polluted stdout stream, invalid schema, unsupported protocol version, or missing token scope can all appear to the user as “the assistant cannot use the tool.”
A good workflow produces evidence at every step. You should be able to say which layer passed, which layer failed, and what exact request, response, log line, or status code proves it.
Inspector is not just a convenience UI. It is a protocol isolation tool. It removes the model, conversation state, host tool-selection heuristics, and product UI from the debugging loop so you can test the server contract directly.
Use Inspector to prove the server advertises what you think it advertises. Tool names, descriptions, input schemas, resource URIs, prompt arguments, and capability metadata are not implementation details; they are the contract the host sees. If that contract is wrong, the model will make bad choices even if the handler code is perfect.
Local MCP servers usually run over stdio. That means the host starts a child process and speaks JSON-RPC over stdin and stdout. The most important debugging rule is simple: stdout is protocol traffic. Do not print banners, debug text, stack traces, progress messages, or ordinary logs to stdout.
Many “server not found” bugs are not protocol bugs at all. They are process bugs: wrong executable path, missing dependency, stale compiled file, incorrect working directory, missing environment variable, permission problem, or a command that works in your terminal but not in the host environment.
| Symptom | Likely Cause | First Check |
|---|---|---|
| Server exits immediately. | Unhandled startup exception, missing env var, bad import, missing dependency. | Run the exact command manually and inspect stderr plus exit code. |
| Inspector hangs during connect. | Process started but never entered MCP server loop. | Add startup diagnostics to stderr and confirm server transport is connected. |
| Initialize fails with parse errors. | Non-protocol text printed to stdout. | Search for console.log, print, echo, or framework startup banners. |
| Works with tsx but not host. | Host points at stale dist build. | Run the configured dist command and rebuild with tsc. |
| Works in terminal but not host. | Different PATH, cwd, env vars, or permissions. | Use absolute paths and log safe environment diagnostics to stderr. |
Initialize is the first real protocol checkpoint. During initialize, the client and server establish that they can speak the protocol and exchange capability metadata. If initialize fails, tool handlers, resource readers, and prompts are irrelevant because the session never reached normal operation.
Version or capability mismatches should be handled explicitly. A robust server does not assume every client supports every optional behavior, and a robust client does not assume a server exposes tools, resources, prompts, roots, logging, or subscriptions unless discovery confirms them.
Discovery is where the host learns what the server can do. If discovery output is unclear, missing, overbroad, or permission-blind, the host may hide capabilities, select the wrong tool, or show a confusing user experience.
Treat discovery responses as a primary debugging artifact. The tool name, description, input schema, resource URI, prompt arguments, and metadata are what the host and model actually see. The code you intended to write matters less than the contract that was advertised.
| Discovery Problem | Consequence | Fix |
|---|---|---|
| Tool name is vague. | Model chooses it in the wrong situations. | Rename with action plus domain noun, such as search_runbooks or create_incident_update. |
| Description omits constraints. | Host cannot explain safe usage clearly. | State what the tool does, what it does not do, and important permission constraints. |
| Schema accepts generic strings or objects. | Model sends ambiguous or unsafe arguments. | Use enums, formats, minimums, maximums, and explicit required fields. |
| Resources list inaccessible objects. | User learns data exists even when reads are denied. | Filter discovery by identity and object-level permissions. |
| Prompt variables are unclear. | Reusable workflow becomes brittle. | Use named arguments with domain-specific descriptions and examples. |
Tool debugging should separate input parsing, schema validation, authorization, backend execution, result formatting, and host rendering. If these are collapsed into one catch-all error, every failure looks the same and every incident takes longer to diagnose.
Run three cases for every important tool: a minimal valid request, an invalid request that should fail before backend access, and an unauthorized request that should fail before sensitive work. These tests prove the server contract, not just the happy path.
Resources fail differently from tools. A resource may be discoverable but unreadable, readable but too large, correctly addressed but stale, or returned with the wrong MIME type. Debug resource URI construction and permission checks as carefully as tool schemas.
Prompts fail when their template contract is unclear. Missing variables, weak descriptions, hard-coded context, or hidden assumptions make prompts look reusable while behaving inconsistently across hosts and users.
| Capability | Failure Mode | Debugging Move |
|---|---|---|
| Resource | URI cannot be resolved. | Log parsed URI parts and verify template variables. |
| Resource | Content is too large for useful model context. | Add summaries, pagination, section reads, or range selection. |
| Resource | User can list but cannot read. | Align discovery filtering with read authorization. |
| Prompt | Required variable is missing or ambiguous. | Inspect prompts/list metadata and prompts/get arguments. |
| Prompt | Template depends on hidden server state. | Expose required inputs explicitly or pair the prompt with resources/tools. |
Remote MCP debugging adds HTTP, identity, session, proxy, and deployment concerns. A server may be correct locally but fail remotely because the request lacks a bearer token, the token has the wrong audience, the reverse proxy strips headers, CORS policy blocks a web host, or session state is not preserved.
For enterprise deployments, debug authorization at discovery and invocation. A capability that is hidden during discovery may be a permission issue, not a registration issue. A tool that appears but fails during call may be hitting object-level authorization or scope checks.
Logging should tell an operator what happened without leaking what the model saw or what the user owns. Log identifiers, capability names, decisions, durations, counts, and sanitized error classes. Avoid raw prompts, tokens, secrets, full resource bodies, and large customer records unless you have an explicit controlled audit requirement.
For stdio servers, send logs to stderr. For remote services, use normal structured service logging plus metrics and traces. MCP logging notifications can be useful for client-visible diagnostics, but they should not replace durable server-side logs.
After Inspector passes, move to the host. Host failures usually involve configuration, stale builds, restart behavior, environment variables, connector permissions, or the host deciding not to surface a capability. The server may be healthy while the host is using a different command, an old binary, or a filtered capability list.
For Claude Desktop-style local integrations, server status and host logs are often enough to find startup and config issues. For custom clients or web hosts, inspect client-side MCP session handling, auth token flow, transport implementation, and how the host maps discovered capabilities into model instructions.
Production debugging should not begin with asking a user to reproduce the issue in chat. A production MCP server needs enough observability to identify failing capabilities, affected tenants, dependency failures, latency spikes, auth denials, payload truncation, and protocol errors from telemetry.
Define incident runbooks around failure layers. If tools/list latency spikes, investigate discovery dependencies and permission filters. If one tool has a high validation error rate, inspect schema descriptions and host prompts. If authorization denials jump after a deploy, compare scope mapping and policy changes.
| Metric or Signal | Why It Matters |
|---|---|
| Initialize success rate | Detects broken deployments and version/capability negotiation failures. |
| Discovery latency and capability counts | Finds slow permission filters and accidental capability exposure/removal. |
| Tool call rate by tool and status | Shows which workflows are active and which are failing. |
| Validation error rate | Reveals schema or description problems causing bad model arguments. |
| Authorization denial rate | Highlights policy issues, missing scopes, or attempted misuse. |
| Backend dependency latency | Separates MCP server health from downstream system health. |
| Response size and truncation count | Catches unbounded outputs before they degrade model interactions. |
Start with process startup. Run the exact configured command manually, use absolute paths, inspect stderr, check missing environment variables, and confirm the process stays alive before debugging protocol behavior.
Usually no. Log identifiers, capability names, counts, durations, sanitized error classes, and correlation IDs instead. Raw prompts, tokens, secrets, and resource bodies should be redacted unless a controlled audit policy explicitly allows them.
The server contract is probably healthy, so compare host configuration: command path, args, working directory, environment variables, build output, restart behavior, host logs, and any cached discovery state.
In stdio transport, stdout carries MCP protocol messages. Ordinary debug output on stdout can make the client parse non-protocol text as JSON-RPC and fail before your capabilities are even discovered.
Track initialize success, discovery latency, capability counts, tool calls by status, validation errors, authorization denials, backend dependency latency, response sizes, truncation, and audit events for sensitive operations.
Explore 500+ free tutorials across 20+ languages and frameworks.