> ## 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.

# Checks reference

> Every rule the handshake probe runs, what it means, and how to fix it.

The [handshake probe](/concepts/the-probe) runs eleven checks against your MCP
server every time a tunnel opens, and on demand with
[`heymcp probe`](/cli/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

| Shown as       | Meaning                                              |
| -------------- | ---------------------------------------------------- |
| **will break** | Some client cannot use your server at all. Fix this. |
| **heads up**   | Will bite with some client, some of the time.        |
| **note**       | Worth knowing.                                       |

## Client limits these checks use

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

|                    | Claude.ai               | ChatGPT                 | Claude Code             |
| ------------------ | ----------------------- | ----------------------- | ----------------------- |
| Tool description   | 1024 chars              | 1024 chars              | 8192 chars              |
| Tool name          | 64 chars                | 64 chars                | 128 chars               |
| Protocol revisions | 2024-11-05 → 2026-07-28 | 2025-03-26 → 2025-06-18 | 2024-11-05 → 2025-06-18 |

***

## Transport

<h3 id="transport-content-type">
  transport/content-type
</h3>

<Info>**will break** · Response content-type is wrong for streamable HTTP</Info>

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.

```
Content-Type: application/json
```

***

<h3 id="transport-accept-sse">
  transport/accept-sse
</h3>

<Info>**will break** · Server rejects `Accept: text/event-stream`</Info>

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.

<Note>
  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.
</Note>

***

## Protocol

<h3 id="mcp-protocol-version">
  mcp/protocol-version
</h3>

<Info>**heads up** · Negotiated protocol version some clients cannot speak</Info>

Fires in one of two situations.

**Your server negotiated a revision HeyMCP doesn't recognise.** The revisions it
knows are:

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

If the value looks like a typo, the handshake will fail for every client. If
it's simply newer than your CLI, update it:

```bash theme={null}
curl -fsSL https://heymcp.dev/install.sh | sh
```

**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.

<Note>
  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.
</Note>

***

## Tools

<h3 id="tool-no-description">
  tool/no-description
</h3>

<Info>**heads up** · A tool has no description</Info>

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.

***

<h3 id="tools-description-length">
  tools/description-length
</h3>

<Info>**heads up** · Tool description exceeds a client's limit</Info>

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*.

***

<h3 id="tools-name-length">
  tools/name-length
</h3>

<Info>**heads up** · Tool name exceeds a client's limit</Info>

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.

***

<h3 id="tools-name-collision">
  tools/name-collision
</h3>

<Info>**will break** · Two tools differ only by case</Info>

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

<h3 id="schema-no-format">
  schema/no-format
</h3>

<Info>**heads up** · String parameter looks semantic but has no `format`</Info>

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:

```json theme={null}
{ "collection_time": "7pm" }
```

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:

| Parameter name contains                                                                                    | Suggested `format` | What agents send instead        |
| ---------------------------------------------------------------------------------------------------------- | ------------------ | ------------------------------- |
| `time`, `date`, `datetime`, `timestamp`, `when`, `deadline`, `expiry`, `expires`, `scheduled`, `due`, `at` | `date-time`        | things like `"7pm"`             |
| `email`, `mail`                                                                                            | `email`            | names instead of addresses      |
| `url`, `uri`, `link`, `href`, `website`, `webhook`, `endpoint`                                             | `uri`              | bare hostnames without a scheme |

**How to fix it.** Add the format and an example. The example does most of the
work — models pattern-match on it.

```json theme={null}
{
  "collection_time": {
    "type": "string",
    "format": "date-time",
    "description": "When to collect the order",
    "examples": ["2026-08-15T19:00:00Z"]
  }
}
```

***

<h3 id="schema-missing-required">
  schema/missing-required
</h3>

<Info>**heads up** · Object schema marks nothing as required</Info>

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.

***

<h3 id="schema-deep-nesting">
  schema/deep-nesting
</h3>

<Info>**heads up** · Input schema nests deeper than clients handle well</Info>

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.

***

<h3 id="schema-additionalProperties-strict">
  schema/additionalProperties-strict
</h3>

<Info>**heads up** · Strict schema plus chatty client equals validation storms</Info>

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.
