> ## 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 handshake probe

> HeyMCP connects to your server as an MCP client before any real agent does.

The moment your tunnel opens, HeyMCP performs the MCP handshake against your
server itself — as a real client, over the real routing — and reports what it
found:

```
●  myapp — https://amber-jetty.t.heymcp.dev/mcp
✓  handshake ok — 3 tools
```

Or, when it isn't ok:

```
!  handshake ok — 3 tools, 1 thing worth fixing
!  Times might arrive in the wrong format  schema/no-format
   `place_order.collection_time` has no format constraint — clients often send
   things like "7pm". Add `"format": "date-time"` and an example like
   `2026-08-15T19:00:00Z`.
```

This exists because **agent clients fail silently**. Claude.ai in particular
tells you nothing at all when it can't use your server. The probe is the thing
that speaks up first.

## What it does

<Steps>
  <Step title="initialize">
    Sends `initialize` with `Accept: application/json, text/event-stream`,
    asking for protocol revision `2025-11-25` — the newest the official SDKs
    ship, rather than the newest that exists, because a server handed a version
    it has never heard of is entitled to refuse outright.
  </Step>

  <Step title="notifications/initialized">
    The lifecycle notification, in order, because some servers refuse
    `tools/list` before it.
  </Step>

  <Step title="tools/list">
    Collects your tools and their input schemas.
  </Step>

  <Step title="The checks">
    Runs all eleven rules against what it found. See the
    [checks reference](/lint).
  </Step>
</Steps>

Every request after `initialize` carries the **negotiated** protocol version,
not a hardcoded one. Sending a hardcoded version is how a client ends up telling
a server it speaks a revision that server just declined — the source of the
`Bad Request: Unsupported protocol version` failure real clients hit.

## It goes in the front door

The probe reaches your server through the **same edge, routing, path policy and
event tee that a real agent hits** — not by dialling your local port directly.

So a misconfiguration that would break Claude.ai breaks the probe first, which
is the entire point. A probe that took a shortcut would happily pass on a setup
no real client can use.

## When your server refuses SSE

If your server rejects `Accept: text/event-stream` with a 406 or 415, the probe
doesn't give up. It names the problem —
[`transport/accept-sse`](/lint#transport-accept-sse) — and retries without SSE
so you still get the rest of the report.

A half report with a clear cause beats no report.

## Running it without a tunnel

You don't need a public URL to get a report:

```bash theme={null}
heymcp probe localhost:8000
```

Handshake plus checks, straight against your local server, nothing published.
Good in CI — it exits `2` if anything will break, `1` on warnings only, `0` when
clean. See [`heymcp probe`](/cli/probe).

## Re-probing after a fix

In the inspector's **Handshake** tab, re-run the probe against the live tunnel.

Reports are kept as history rather than overwritten, so you can see whether the
change you just made actually cleared the finding.

## Protocol versions HeyMCP knows

```
2024-10-07   2024-11-05   2025-03-26   2025-06-18   2025-11-25   2026-07-28
```

If your server negotiates something outside this list, you get a
[`mcp/protocol-version`](/lint#mcp-protocol-version) warning — either it's a
typo, or your HeyMCP build is older than your server and wants updating.

<Note>
  `2026-07-28` is in the list because Claude already sends it. A server that
  answers with it is ahead of HeyMCP, not broken, and is not flagged.
</Note>
