Skip to main content
The handshake probe runs eleven checks against your MCP server every time a tunnel opens, and on demand with heymcp probe. Each finding links here. They exist because MCP client failures are mostly silent. These are the mistakes that produce no error message anywhere — the connector simply doesn’t work, or the tool simply never gets called.

Severities

Client limits these checks use

Where a rule refers to “the strictest client”, these are the numbers:

Transport

transport/content-type

will break · Response content-type is wrong for streamable HTTP
Your server answered initialize with a content-type that isn’t application/json or text/event-stream. Why it matters. This is the classic silent Claude.ai failure. Streamable HTTP requires one of those two content types, and a server that answers with anything else simply never connects — no error in the UI, nothing in the browser console, and frequently nothing in your own logs either. If you have ever added a connector and watched it do absolutely nothing, this is the first thing to check. How to fix it. Set the header on your MCP route. text/plain is the usual culprit, often from a framework’s default for a string return value.

transport/accept-sse

will break · Server rejects Accept: text/event-stream
A request carrying Accept: text/event-stream was rejected, usually with a 406 or 415. Why it matters. Every agent client asks for streaming on the MCP route, so this blocks all of them. It is not a Claude-specific quirk. How to fix it. Accept both application/json and text/event-stream on the MCP route. If you’re using a content-negotiation middleware, this is normally where it’s coming from.
When this fires, the probe retries without SSE so you still get the rest of the report. A half report with a named cause beats no report.

Protocol

mcp/protocol-version

heads up · Negotiated protocol version some clients cannot speak
Fires in one of two situations. Your server negotiated a revision HeyMCP doesn’t recognise. The revisions it knows are:
If the value looks like a typo, the handshake will fail for every client. If it’s simply newer than your CLI, update it:
Your server negotiated a revision a known client can’t speak. The finding names the clients by name. ChatGPT, for example, does not negotiate 2024-11-05 — a server pinned to it locks ChatGPT out entirely. How to fix it. Support a newer revision, or accept that the named clients won’t work.
A server newer than the check is not flagged. Comparison is against each client’s oldest supported revision, because a client that speaks newer versions negotiates down — that’s what negotiation is for.

Tools

tool/no-description

heads up · A tool has no description
One of your tools has a name and nothing else. Why it matters. Agents guess what check_order_status does from its name alone. Sometimes they guess right. When they guess wrong, the tool either never gets called or gets called in the wrong situation, and there’s no error to notice — it just quietly doesn’t work as well as it should. How to fix it. A single line. What it does, and when to use it.

tools/description-length

heads up · Tool description exceeds a client’s limit
A description is longer than the strictest client will take. The finding names which clients cap below your length. Why it matters. Claude.ai and ChatGPT cap at about 1024 characters. Past that the description is truncated or dropped — and a truncated description can end mid-sentence, which is worse than a short one. Claude Code accepts about 8192, which is why a tool can work perfectly there and behave oddly in Claude.ai. If you’re chasing that exact symptom, this is usually why. How to fix it. Move the detail into the parameter descriptions, where it belongs anyway. The tool description is for when to reach for this; the parameters are for what to put in it.

tools/name-length

heads up · Tool name exceeds a client’s limit
A tool name is longer than the strictest client accepts — 64 characters for Claude.ai and ChatGPT, 128 for Claude Code. Why it matters. An over-long name means the client silently loses the tool. How to fix it. Shorten it. Names this long are usually carrying explanation that should be in the description.

tools/name-collision

will break · Two tools differ only by case
Two of your tools have names that are identical once lowercased. Why it matters. Clients match tool names case-insensitively, so as far as the agent is concerned they are one tool — and one of them can never be called. Which one wins is not something you should rely on. How to fix it. Rename one.

Schemas

schema/no-format

heads up · String parameter looks semantic but has no format
A string parameter whose name means something specific — a time, an email, a URL — with no format constraint to say so. Why it matters. This is the finding behind the canonical HeyMCP demo. Ask Claude to book a table for seven and it will send:
Not 2026-08-15T19:00:00Z. "7pm". Your parser throws, the agent sees an opaque failure, and it may well try again with "19:00" and fail differently. The check recognises three groups: How to fix it. Add the format and an example. The example does most of the work — models pattern-match on it.

schema/missing-required

heads up · Object schema marks nothing as required
A tool takes parameters but marks none of them required. Why it matters. An agent may call it with no arguments at all, which normally surfaces as a confusing 500 rather than a helpful validation message. From the agent’s side that looks like a broken tool, and it may stop trying. How to fix it. Add a required array for the parameters you genuinely need.

schema/deep-nesting

heads up · Input schema nests deeper than clients handle well
The input schema nests more than three levels deep. Objects and array items both count. Why it matters. Agents reliably fill in flat objects. Past three levels they start producing malformed nested structures — the right keys in the wrong place, or a level omitted entirely. How to fix it. Flatten it, or split the tool in two. A tool that needs a deeply nested object is often two tools wearing a coat.

schema/additionalProperties-strict

heads up · Strict schema plus chatty client equals validation storms
A tool sets "additionalProperties": false. Why it matters. Agents routinely add an unasked-for key — a notes, a reason, something they thought would help. With strict validation that bounces, and the agent frequently retries in a loop, adding a slightly different extra key each time. You get a burst of near-identical failures rather than one clean error. How to fix it. Drop the constraint, or ignore unknown keys server-side. If you need to reject unexpected input for real reasons, do it with a clear error message that tells the agent what was wrong, rather than a schema rejection it can’t read.