> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heymcp.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The inspector

> Reading the timeline: attempts, not frames.

The inspector is the live timeline at
[app.heymcp.dev](https://app.heymcp.dev). Every session has one, linked from
the CLI as a short `…/s/x7k2` URL.

## It's a list of attempts, not frames

On the wire, a tool call is two messages: a request going one way and a result
coming back the other. The inspector folds them into **one row**:

```
14:02:31   Tried place_order · hmm, that broke        ✗   120ms
14:02:34   Tried place_order                          ✓    62ms
```

That's what you actually want to see — one attempt, with its outcome and how
long it took — rather than two rows you have to mentally staple together.

A response that arrives without a matching request still gets its own row.
Hiding an orphan would hide a real problem.

<Note>
  The database keeps the two halves separate, which is why
  [`heymcp export`](/cli/export) is lossless. The folding is a reading
  convenience, not a storage decision.
</Note>

## Latency is measured at the edge

The number on each row is the round trip as seen from the edge, because that's
the only place both halves of an exchange are visible. It includes your local
server's work plus the hop to your machine, which is the number that matters
when you're deciding whether a tool feels slow to an agent.

## What the rows say

The inspector writes plain English, not protocol labels. The same events show up
in the CLI ticker as a terse glyph column instead.

| What happened                   | The inspector row reads                                | CLI ticker |
| ------------------------------- | ------------------------------------------------------ | ---------- |
| `initialize`                    | *Claude.ai plugged in*                                 | `INIT`     |
| its response                    | *Your server said hello back*                          | `INIT←`    |
| `tools/list`                    | *Claude.ai asked what's available*                     | `LIST`     |
| its response                    | *Found `3 tools`* · looked around                      | `LIST←`    |
| `tools/call`                    | *Claude.ai tried `place_order`*                        | `CALL`     |
| its result                      | *It worked* · worked · `62ms`                          | `CALL←`    |
| a failed result                 | *That call came back with a problem* · hmm, that broke | `CALL←`    |
| a notification                  | *Claude.ai sent `notifications/initialized`*           | `NOTIF`    |
| another JSON-RPC method         | *Claude.ai called `ping`*                              | `RPC`      |
| a JSON-RPC error                | *Your server returned an error*                        | `ERROR`    |
| HTTP with no MCP payload        | the method and path                                    | `HTTP`     |
| something unparseable           | *Something happened*                                   | `OPAQUE`   |
| events the buffer couldn't hold | *Too busy to record `some events`* · we lost some      | `DROP`     |

Every row that has an author names it. One tunnel can carry several clients at
once, so a row that just read *Called `server/discover`* would get read as
belonging to whichever client said hello above it.

A replayed call reads *Replaying `place_order`* rather than naming a client, so
a replay is never mistaken for real agent traffic.

<h2 id="errors-that-arent-your-fault">
  Errors that aren't your fault
</h2>

Not every error is a problem, and the inspector distinguishes them. An error is
treated as **benign** — shown as *handled* or *declined* rather than *hmm, that
broke* — when it is the protocol working as designed:

* Your server declining an **optional** method the client probed for.
* An **HTTP 404**, which is how the spec tells a client its session is gone and
  it should re-initialize.

The rule is deliberately narrow. **A `tools/call` that errors stays red**, even
though the client may well retry it — that's somebody's work failing.

Benign errors don't count toward the Problems tally, so the number next to that
filter is the number of things actually worth looking at.

## Filters

**All** shows everything. **Problems** shows only rows that failed, excluding
the [benign ones](#errors-that-arent-your-fault) above.

The empty state for the Problems filter reads *"Nothing has gone wrong. That's
the good outcome."*, which is the honest framing: an empty problems list is a
result, not a missing feature.

## Which client is this?

Each row is fingerprinted with the client that produced it — Claude.ai,
ChatGPT, Claude Code, Gemini, Grok, Cursor, VS Code, MCP Inspector, curl, or
HeyMCP's own probe.

This is more useful than it sounds. When a tool works in Claude Code and
silently doesn't in Claude.ai, the timeline shows you exactly which client got
as far as what.

HeyMCP's own probe is always fingerprinted as the probe, never mistaken for a
real client, so it never pollutes your view of what agents actually did.

The probe goes in through the front door — the same routing, path policy and
tee a real agent hits — so its traffic is recorded like anyone else's. Activity
**hides those rows by default**, because on a fresh tunnel they would otherwise
be the whole timeline before your agent has connected. *Show HeyMCP's own
check*, under the list, brings them back for when the probe itself is what
you're debugging. What the probe *found* lives in the Handshake tab.

## Replay

Expand any tool call and you get **Try it again** — HeyMCP re-fires that exact
request against your local server.

This is the loop the product is built around:

<Steps>
  <Step title="An agent calls your tool and it breaks">
    The row goes red. Expand it and you can see the arguments the agent actually
    sent, which is frequently the surprise.
  </Step>

  <Step title="You fix your server">
    Edit the code. Your dev server reloads.
  </Step>

  <Step title="Replay">
    Same arguments, same route, no waiting for the agent to try again — and no
    trying to talk it into producing the same call twice.
  </Step>
</Steps>

Replayed events are flagged as replays so they don't look like real agent
traffic later.

## The payload

Expanding a row shows its payload straight away — the click that opened the row
*was* the request to see it.

The viewer is a real one, not a wall of text: objects and arrays fold, types are
told apart by colour, and **the filter box searches keys and values at once**.
Filtering keeps the trail down to each match so you can see where it lives, and
says how many rows it hid rather than quietly showing you a subset. Searching a
key name — `arguments` — keeps that whole subtree, because that is what you
were asking for.

Big payloads open with their shallow structure expanded and large collections
folded, so a `tools/list` from a server with eighty tools stays readable.

Paired rows show two viewers, **Sent** and **Came back**, each with its own
filter.

## Headers

At the top of the payload panel, **headers** shows the HTTP headers for
whichever half you're looking at — the client's request headers, or your
server's response headers. Inside the panel rather than below it, because the
payload scrolls within a fixed height and anything underneath it is easy never
to find.

This is where the failures that produce *no error at all* become visible.
Claude fails silently on a response `content-type` it doesn't accept, and the
`MCP-Protocol-Version` header is what earns a `server/discover` its 400. The
timeline could always show you the effect; the headers show you the cause.

**Secrets never leave the relay.** `Authorization`, cookies, API keys and
anything whose name looks like a credential are redacted before they are
stored — the header *name* survives, and for auth headers so does the scheme,
because "Bearer" versus "Basic" is usually the bug. The value does not. The one
identifier deliberately kept is `Mcp-Session-Id`: it is the spec's own routing
identifier and what the inspector pairs requests on.

Rows recorded before this shipped have no headers, and the panel stays away
rather than offering you an empty box.

*Exchange details*, underneath, is the same event in protocol terms —
direction, kind, rpc id, stream, latency, client — for when the plain-English
layer is what you're trying to see past. It covers both halves, which is why it
is not called *request* details.

## The handshake tab

Alongside **Activity** is **Handshake**, carrying the count of findings from the
[probe](/concepts/the-probe). That's the report on your server itself, as
opposed to the traffic through it.

You can re-run it from there after a fix — the reports are kept as history, so
you can see whether the fix landed.
