MCP Inspector Alternatives: Where to Debug a Live Server
An inspector is a debugging client: it connects to a server, lists the tools, and lets you call them while it shows the traffic. It is the first stop for a failure you can reproduce. When the call never arrived, the counter stopped moving, or the server booted in a mode you did not choose, the answer is in server logs, in an audit row, or in a health check instead.
Short answer: An inspector is a debugging client: it connects to a server, lists the tools, and lets you call them while it shows the traffic. It is the first stop for a failure you can reproduce. When the call never arrived, the counter stopped moving, or the server booted in a mode you did not choose, the answer is in server logs, in an audit row, or in a health check instead.
Key takeaways
- One package, three clients. The reference inspector ships a browser UI, a scriptable CLI and a terminal UI behind one binary, sharing transports and stored OAuth state.
- A session trace is not an audit trail. The inspector shows the connection you drove; the audit row is what the server recorded for the calls you did not drive.
- Protocol logging is deprecated as of the 2026-07-28 revision. Logs belong on stderr for stdio servers and in OpenTelemetry for everything else.
- A missing audit row and a failed call are different bugs. The row is written on success, so absence points at the request path, not at the tool.
- Ask the third question early. A readiness check that reports the storage mode a server resolved at boot removes a whole class of "the counter stopped" tickets.
- Next: reproduce one failing call in the CLI client, then reconcile it against the audit row for that same call — the gap between the two is the bug.
The four questions a debugging tool has to answer
Every MCP incident is one of four questions, and no single tool answers all four. Did the call arrive at the server? What did the server do with it? Who sent it, and at whose expense? Is the state the server depends on healthy right now?
A browser inspector answers the second question best, because you are the caller and you can read the response as it comes back. Server logs answer the first and second for calls you never drove. Audit rows answer the third, and they are the only one of the four that outlives the session. A readiness check answers the fourth before a user notices anything.
The mistake is to treat an inspector as the whole toolkit. It is the fastest window into one
session and the slowest way to learn what happened to a key you do not own — and it is silent
about retention, because it stores nothing beyond the tab you have open. The vocabulary is
searched often enough to be worth separating: the head phrase carries roughly 4,400 US
searches a month, while the phrases engineers use for the neighbouring jobs are smaller and
more specific — mcp client at 1,600, mcp cli at 480, mcp logging at 210, mcp server testing at 140, python mcp client at 110, mcp observability at 50 and mcp monitoring at
20, all measured in this page's own research pass.
What the reference inspector covers, and where it stops
The MCP Inspector is the protocol's own developer tool, published as a single package and described as the reference tool for testing and debugging servers (Inspector). Three clients sit behind one binary: a web client, a scriptable CLI, and a terminal UI. All three are built on the same core, so a connection behaves identically across them — the same transports, the same configuration files, the same stored authorization state, and the same protocol-era negotiation between the legacy handshake and the modern revision.
Two properties follow from that design, and both decide when to reach for something else.
First, it is interactive: the session exists because you started it, so it cannot describe
traffic from clients you do not own. Second, the CLI client is the half that fits automation —
a machine-readable call such as a tools/list against a remote HTTP endpoint is a one-line
check you can put in a deploy pipeline, while the browser client is the half you use when you
want to see a call happen.
The protocol's debugging guide draws the same line from the other side (Debugging). It names the inspector as the first stop, then splits logging by transport: a stdio server's messages on stderr are captured by the host automatically, while a server on Streamable HTTP has its stderr captured by nobody, so its logs need an aggregation path or an OpenTelemetry exporter. The in-protocol notification channel is deprecated as of the 2026-07-28 revision and remains available only through the deprecation window; clients opt in per request, and a server must not send those notifications to a request that did not ask. That deprecation is the reason this page treats "inspector" as one instrument among several rather than the default: the mechanism a server used to expose its own diagnostics over the wire is on its way out, and the replacements are process logs, traces and audit records. If you are still deciding what the server under debugging should look like at all, start from MCP server, which covers the endpoint, the registry and the session question.
mcp server testing: read the annotations before the first call
A test plan starts with what the server says about each tool, and the annotation is the cheapest signal available before you call anything:
# backend/smartgate/api/mcp_tool_docs.py — source lines 63–67 (tool_annotations)
def tool_annotations(name: str) -> ToolAnnotations:
return ToolAnnotations(
title=TOOL_TITLES.get(name),
readOnlyHint=name in READ_ONLY_TOOLS,
)
Three lines, and they carry a governance decision. The title comes from a shared table keyed by tool name, so the model-facing label cannot drift away from the documentation, and the read-only hint is derived from membership in a declared set rather than typed next to each tool. For a tester that distinction is operational: a tool the server marks read-only can be called against production data without asking permission, and a tool it does not mark that way needs a scratch key and a reversible input.
The annotation is a hint, not a control. It shapes what a host shows the user and what an agent chooses to do; it does not stop a call. The thing that stops a call is policy in front of the server, which is why the split between hints and enforcement is worth testing explicitly: call every read-only tool with a key that has no write scope, then call the same tools with a key that does, and compare. A registry whose annotations and enforcement disagree is invisible until one of those two runs. The annotation fields themselves are catalogued in the MCP tools reference.
mcp observability: which calls actually become audit rows
An audit row is not a log line. It is a record written at a specific moment, under specific conditions, and knowing the conditions is what makes absence informative:
# backend/smartgate/core/record_usage.py — source lines 79–97 (maybe_record_from_tool)
async def maybe_record_from_tool(
team_id: str,
tool: str,
*,
success: bool,
token_used: int = 0,
data: Any = None,
params: dict | None = None,
) -> None:
"""Record billable tool usage when successful and tokens are known or inferable."""
if not success or not team_id or tool in _SKIP_RECORD_TOOLS:
return
if tool not in _BILLABLE_TOOLS and token_used <= 0:
return
amount = token_used
if amount <= 0:
amount = infer_token_usage(tool, data, params)
if amount > 0:
await record_usage(team_id, amount)
Read the early exits carefully, because each one is a legitimate way to end up with no row. A call that failed returns before recording — that is the first exit, and it means a failed call with no row is normal rather than suspicious. A call with no team identity returns as well, which is what a request that resolved to no key looks like from the inside. Tools on the internal skip list never record, and a tool that is neither billable nor reporting a token count is skipped too. Only after those gates does the function take the reported token count, and when the count is missing it infers one from the tool, the response and the parameters before writing usage.
That ordering turns the missing-row question into a short checklist rather than a search through logs. If the client saw a successful response and there is no row, ask whether the request carried a resolvable identity, and whether the tool is one this server counts. If the client saw an error, no row is expected — the failure is the tool's, not the audit path's. The one case that looks like a bug and is not: a call whose tokens were reported as zero and whose tool is not in the billable set. Nothing is recorded because nothing measurable happened, which is the honest behaviour for a counter that must not drift. The wider retention and trail design is the subject of the logging and observability page.
mcp monitoring: from one audit row to a daily series
A single row answers "what happened". A trend answers "is this normal", and the trend is built by folding timestamps into buckets:
# lib/analytics/aggregate-entries.ts — source lines 4–9 (parseAuditDate)
function parseAuditDate(iso: string): string {
const trimmed = iso.trim();
if (!trimmed) return "";
if (trimmed.includes("T")) return trimmed.slice(0, 10);
return trimmed.split(" ")[0] ?? trimmed.slice(0, 10);
}
Six lines that exist because timestamps arrive in more than one shape. The stored value is
trimmed first, and an empty string stays empty rather than becoming a date, so a row with no
timestamp is visibly missing instead of silently landing on the first day of the series. A
full ISO timestamp contains the letter T, and the first ten characters of it are the date; a
timestamp written with a space separator is split instead. The two branches are the difference
between a chart that matches its own rows and one that quietly drops them.
The debugging consequence is worth stating plainly: when a day bucket looks empty, the first hypothesis should be a timestamp shape, not a traffic outage. A series built over mixed shapes produces a bucket that is short by exactly the number of rows the parser could not read, and the aggregate looks like a load drop. That is also why the fold belongs on the server side of the audit view rather than in a client: one implementation, one answer, and a shape change that fails loudly in one place. Cross-check the result against the raw records in the logs view before you escalate a "traffic stopped" ticket.
What no inspector can see: the storage mode a server resolves at boot
The third question — is the state healthy — is answered by the server, not by a session, and it usually has a small, readable answer:
# lib/redis/config.ts — source lines 38–42 (resolveRedisMode)
function resolveRedisMode(): RedisMode {
if (isUpstashRestConfigured()) return "upstash";
if (isTcpRedisConfigured()) return "tcp";
return "none";
}
Three lines in priority order. An Upstash-style REST configuration wins, a TCP URL comes
second, and anything else resolves to none. The first two carry shared counters across
instances; the third is the mode in which every limiter has to fail open, because there is no
store to count against. None of that is visible in an interactive session, because a session
that works with a degraded store still works — until the second instance starts and both of
them believe they are the only one counting.
A readiness check is the cheapest instrument for this and it does not need a protocol client at all: report the resolved mode, the number of tools the registry exposes, and the result of one read-only call. Those three values separate a boot problem from a data problem from a policy problem. They also make a deployment review possible without reading a single log line, which matters at the moment the store disappears and every "the counter stopped moving" symptom has the same explanation. The same resolution runs on the client side of the protocol's own transports, which the protocol versions and transports walks through version by version.
A session trace, a log line and an audit row are three instruments
The instruments differ in more than location: they differ in what they can be asked and how long the answer survives.
- A session trace answers "what did this exchange look like". It is the right tool when you can reproduce the failure, and it disappears with the tab.
- A server log line answers "what did the process do". On stdio it is captured by the host for free; on an HTTP transport it is only as good as the pipeline you ship it to.
- An audit row answers "who called, and what did it cost". It is the only one of the three that a third party can be shown — a finance reviewer, a security reviewer, or a teammate looking at a team's spend.
- A health check answers "is the state the server depends on the state I think it is". It is read before the incident, not after.
Order them by reproducibility, not by preference. A failure you can trigger goes to a session trace first, because a trace shows the request and the response together and costs minutes. A failure that arrives from a client you do not own goes to the audit row, because the row is the only record that exists. A failure that repeats at the same time every day goes to the log pipeline, because repetition is a property of the process rather than of one exchange.
None of the three instruments is specific to one transport: a model context protocol exchange and a plain HTTP call produce rows of the same shape, so the choice is about the failure you are chasing, not about the protocol.
Choosing between them: five questions worth asking
Pick the instrument and the client with the same discipline you would apply to any other dependency, and answer these five before you adopt one.
- Does it speak your transport? A debugging client that handles local stdio and not Streamable HTTP cannot inspect the deployment you actually run, however good its panels are.
- Can it run headless? If the only client is a graphical one, a failing check cannot be part of a deploy or a nightly job, and someone will be asked to click through a UI at an inconvenient hour.
- Which protocol era does it negotiate? A client written against an older revision may connect and then decline the capabilities a newer server advertises; the failure presents as "connected but no tools".
- What does it do with credentials? A tool that stores a key on disk in a developer's home directory is a real answer for local work and a bad one for a shared machine.
- What is retained, and where? A trace kept for the length of a tab is not a retention policy. If the answer has to survive the session, the audit row is the record and the client is only the reader.
A team that answers those five has also decided its retention story, because the fourth and fifth answers are the ones an auditor asks for. The gateway side of that split — which calls are counted, capped and recorded — is in MCP gateway.
How the options compare
Each row describes the instrument as its own documentation describes it; the links are sources, not claims from this page.
| Instrument | What it shows | What it cannot show | Where it lives |
|---|---|---|---|
| Inspector web client (docs) | Tools, prompts, resources and notifications for a session you start | Calls from clients you do not own; anything after the tab closes | A browser tab against a local or remote server |
| Inspector CLI client (docs) | The same protocol traffic, formatted for a shell or a pipeline | Nothing interactive; it answers the method you asked for | A command line, including a deploy job |
| Server logs on stderr (docs) | What the process logged, at eight severity levels | Anything the process did not log; stderr of an HTTP server is captured by nobody | The host's log files, or your aggregation pipeline |
| OpenTelemetry export (semconv) | Traces and metrics across every transport | The protocol's own notifications, which are deprecated | Your observability backend |
| Audit rows | Which key called which tool, when, and at what measured cost | A call that never reached the server | A retention window measured in days, by plan |
| A readiness check | The storage mode the server resolved, the tool count, one read-only call | Anything about a specific past request | Any HTTP client; it is not a protocol session |
The table is also a cost ordering. A readiness check is the cheapest thing to add and the cheapest to keep running, and it prevents the tickets the other four instruments are used to diagnose. An exporter is the most work and pays off across every service you run. The audit row sits between them: it is configuration rather than code, and it is the only row in the table that another department can be shown without giving them a shell.
How to get started
- Reproduce one call in the CLI client. Against a remote endpoint that is a single command with a method name; against a local server it is the command that launches the server. Record what the response says before you change anything.
- Reconcile it against the audit row for that call. Open the logs view and find the same call. A row that disagrees with the response is a server-side bug; a missing row with a successful response is an identity or billable-set question.
- Add a readiness check to the deployment. Report the resolved storage mode, the tool count and one read-only call. It costs one handler and it answers the third question above without a session.
- Point a client at the hosted endpoint and compare. The per-host configuration blocks are generated on the connect page, so the endpoint, the transport and the auth header are written for you rather than copied from a screenshot.
- Read the two neighbours before you decide your split. Retention and trail design are in MCP logging and observability, and the registry the annotations come from is in MCP tools reference.
Start on the free tier — 2M tokens a month, all seven tools, 120 MCP requests a minute per key and seven days of logs — then compare retention windows and per-key limits on the pricing page.
Frequently Asked Questions
Limitations and what this does not do
- No single instrument is complete. The page separates four of them and says which question each answers; it does not claim that any one of them will find a given bug.
- The interactive clients move faster than this page. Subcommands, flags and protocol-era behaviour are defined in the tool's own documentation, which is the version to trust.
- This is not a retention policy. Storage windows are plan-level and are described on the pricing page; nothing here substitutes for a compliance decision your team has to make.
- Deprecation windows are time-bound. Protocol-level log notifications remain available through the documented window only, so a server that relies on them needs a plan rather than a note.
- The code excerpts are windows, not whole files. Each block is one function from a larger module; the branches around it are described rather than quoted.
- It does not instrument your model calls. This page is about seeing what an MCP server and its clients do, not about tracing the model behind them.
Sources
- Model Context Protocol — MCP Inspector, the reference developer tool: https://modelcontextprotocol.io/docs/tools/inspector
- Model Context Protocol — debugging guide, logging per transport and common issues: https://modelcontextprotocol.io/docs/tools/debugging
- Model Context Protocol — specification (2026-07-28): https://modelcontextprotocol.io/specification/2026-07-28
- Model Context Protocol — transports, including stateless Streamable HTTP: https://modelcontextprotocol.io/specification/2026-07-28/basic/transports
- Model Context Protocol — server tools (
tools/list,tools/call): https://modelcontextprotocol.io/specification/2026-07-28/server/tools - OpenTelemetry — generative-AI semantic conventions: https://opentelemetry.io/docs/specs/semconv/gen-ai/
- Anthropic — introducing the Model Context Protocol: https://www.anthropic.com/news/model-context-protocol
- SmartGate — docs, logs, connect and pricing: https://smartgate.network/docs · https://smartgate.network/docs/logs · https://smartgate.network/docs/connect · https://smartgate.network/pricing
Method note
The code in this article is not transcribed. Each block was cut directly out of the slice body returned by the SmartGate slice API and re-asserted byte-for-byte as a substring of that body before publication; the first line inside every fence records the file and the exact source lines. Symbols were pinned by whole-name containment (rule A level 2) and confirmed by the service's slot-proof endpoint before any prose was written. All four excerpts are whole slice bodies, so nothing is presented as a fragment of something longer.
Demand figures come from this project's own keyword run, recorded in research_brief.md and
search_volume.json: the head phrase mcp inspector at 4,400 US searches a month, the phrases
that pinned these sections — mcp client 1,600, mcp cli 480, mcp logging 210, mcp server testing 140, python mcp client 110, mcp observability 50, mcp monitoring 20 — and the
smaller tail (mcp client list 30, typescript mcp client 20, mcp client configuration 10,
mcp client library 10, mcp client setup 10). Only four of the twelve planned sections
pinned a slice: 1:1 (mcp logging) returned no candidate, and seven sections returned several
equally plausible symbols and were recorded as abstentions. Those sections are written from
the protocol's own public documentation and quote no code, which is the deliberate fallback
rather than a gap.
Slice provenance
| # | SERP keyword | Symbol | File | Source lines | How it was pinned | sha256(12) |
|---|---|---|---|---|---|---|
| 1 | mcp server testing | tool_annotations |
backend/smartgate/api/mcp_tool_docs.py |
63–67 | rule A L2 → slot-proof | a822944005fe |
| 2 | mcp observability | maybe_record_from_tool |
backend/smartgate/core/record_usage.py |
79–97 | rule A L2 → slot-proof | 8a77a10cb0a5 |
| 3 | mcp monitoring | parseAuditDate |
lib/analytics/aggregate-entries.ts |
4–9 | rule A L2 → slot-proof | 16902c35b5dc |
| 4 | mcp cli | resolveRedisMode |
lib/redis/config.ts |
38–42 | rule A L2 → slot-proof | 3a9dd8e9daf4 |
Every fenced block above was cut from the slice body and re-asserted against it byte-for-byte before publication. 4 of 12 sections pinned, 7 abstentions, 1 misses.