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

# Troubleshooting

> What to check, in the order that resolves things fastest.

## Start here

Almost every "it doesn't work" resolves to one of three questions, and they're
worth asking in this order:

<Steps>
  <Step title="Does the handshake report say anything?">
    ```bash theme={null}
    heymcp probe localhost:8000
    ```

    Two findings block clients completely and are invisible from your side:
    [`transport/content-type`](/lint#transport-content-type) and
    [`transport/accept-sse`](/lint#transport-accept-sse). Rule these out before
    touching anything else.
  </Step>

  <Step title="Did the request reach the inspector?">
    Open your session's timeline and try the client again.

    * **Nothing appears** → the client never reached the edge. It's the URL, or
      the tunnel isn't running.
    * **Rows appear** → the client got through. The problem is between the edge
      and your server, or in your server, and the timeline will show you which.
  </Step>

  <Step title="What does the error code say?">
    Every edge failure carries a JSON-RPC code. The table below decodes them.
  </Step>
</Steps>

## Edge error codes

Every failure at the edge answers with a real HTTP status **and** a JSON-RPC
error object, because an HTML error page is an unretryable, confusing failure
inside an agent client.

| Code     | HTTP | Meaning                                                          | What to do                                                                     |
| -------- | ---- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `-32001` | 503  | This tunnel isn't connected right now                            | Run `heymcp share` again. The hostname is known and yours — it's just offline. |
| `-32002` | 404  | No tunnel is bound to this hostname                              | Check the URL. For a custom domain, check it's verified and active.            |
| `-32003` | 404  | That path isn't shared through this tunnel                       | Only `/mcp*` and `/.well-known/*` are forwarded by default. Add `--all-paths`. |
| `-32004` | 401  | This tunnel requires a bearer token                              | You shared with `--bearer` and the client isn't sending it — or can't.         |
| `-32005` | 413  | Request body is larger than your plan allows                     | 2 MB on Hobby, 8 MB on Pro.                                                    |
| `-32006` | 429  | You've hit a plan limit                                          | Check concurrent tunnels and monthly events.                                   |
| `-32007` | 502  | HeyMCP reached your machine, but your local server didn't answer | Your server crashed, isn't listening, or the target address is wrong.          |
| `-32008` | 504  | Your local server didn't respond in time                         | Something is hanging. Check the inspector for where it stopped.                |

<Note>
  **The 404-vs-503 split is deliberate.** An unknown hostname gets 404; a known
  hostname with no live tunnel gets 503. That's so clients *retry* rather than
  concluding the connector is gone and quietly forgetting it.
</Note>

## The connector doesn't appear

<AccordionGroup>
  <Accordion title="Nothing in the inspector at all">
    The client never reached the edge.

    * Check the URL character by character. It must end in `/mcp`.
    * Confirm `heymcp share` is still running in your terminal.
    * `curl -i https://your-slug.t.heymcp.dev/mcp` — a 503 means the tunnel is
      down, a 404 means the hostname or path is wrong, and a JSON-RPC response
      means the tunnel is fine and the problem is client-side.
  </Accordion>

  <Accordion title="INIT arrives, then nothing">
    Your server accepted `initialize` and then something went wrong. Expand the
    `INIT←` row to see exactly what it answered.

    An `OPAQUE` row here usually means an HTML error page where JSON was
    expected — a framework exception page, most often.
  </Accordion>

  <Accordion title="Claude.ai in particular">
    Claude.ai fails silently, with nothing in the UI. Work through the
    [Claude.ai troubleshooting list](/clients/claude-ai#its-not-showing-up),
    which is ordered by how often each cause is the answer.
  </Accordion>
</AccordionGroup>

## The tools are never called

The client connected and can see your tools, and is choosing not to use them.
This is a schema problem, not a transport one.

| Symptom                                   | Likely cause                                                                        |
| ----------------------------------------- | ----------------------------------------------------------------------------------- |
| The agent doesn't know when to use a tool | [`tool/no-description`](/lint#tool-no-description)                                  |
| Works in Claude Code, not in Claude.ai    | [`tools/description-length`](/lint#tools-description-length) — 8192 vs \~1024 chars |
| One of two similar tools is never picked  | [`tools/name-collision`](/lint#tools-name-collision)                                |
| A tool vanished entirely                  | [`tools/name-length`](/lint#tools-name-length)                                      |

## The tool is called, and fails

Expand the row in the inspector. You can see the arguments the agent actually
sent, which is usually the surprise.

| What you see                       | Cause                                                                                                                           |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| A date like `"7pm"`                | [`schema/no-format`](/lint#schema-no-format)                                                                                    |
| A call with no arguments at all    | [`schema/missing-required`](/lint#schema-missing-required)                                                                      |
| A burst of near-identical failures | [`schema/additionalProperties-strict`](/lint#schema-additionalProperties-strict) — the agent added an extra key and is retrying |
| A malformed nested object          | [`schema/deep-nesting`](/lint#schema-deep-nesting)                                                                              |

Fix your server, then hit **Try it again** on the row to replay that exact call
without waiting for the agent to reproduce it.

## Streaming problems

<AccordionGroup>
  <Accordion title="A streaming response arrives all at once">
    Not HeyMCP — the edge forwards each chunk the instant it arrives and never
    buffers. Something in your own stack is buffering: output buffering in PHP,
    a reverse proxy in front of your dev server, or a framework helper that
    collects the whole body before sending.
  </Accordion>

  <Accordion title="The connection times out on a long tool call">
    The edge allows 30 seconds to the first byte and 300 seconds of idle time
    after that. A tool that takes minutes needs to send *something* —
    a keep-alive SSE comment is enough — rather than going silent.
  </Accordion>

  <Accordion title="A DROP marker in the timeline">
    More than 2,000 events buffered for one session, so the oldest were dropped.
    The marker is there so a gap in the timeline is never silent. Nothing was
    lost on the wire — this only affects what the inspector kept.
  </Accordion>
</AccordionGroup>

## Tunnel and CLI problems

<AccordionGroup>
  <Accordion title="Too many tunnels open">
    3 on Hobby, 10 on Pro. `heymcp sessions` shows what's live; closing the
    terminal frees the slot.
  </Accordion>

  <Accordion title="The tunnel keeps reconnecting">
    Normal on a flaky network — the CLI backs off and retries, quietly for the
    first 30 seconds. Reconnecting within 15 minutes resumes the same session,
    so your timeline isn't forked.
  </Accordion>

  <Accordion title="command not found: heymcp">
    The install directory isn't on your `PATH`. The installer names the file it
    edited; open a new terminal, or source it. See
    [Installation](/installation#verifying-it-worked).
  </Accordion>

  <Accordion title="A protocol version warning against a server you know is fine">
    Your CLI is older than your server. Update it:

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

  <Accordion title="--slug or --domain refused">
    Both are Pro. Check with `heymcp whoami`. For a domain, also confirm it's
    verified and active on the Domains page.
  </Accordion>
</AccordionGroup>

## Still stuck

Export the session and you have the whole exchange in one file:

```bash theme={null}
heymcp export <session-id> > session.json
```

That's a much better bug report than a description, because it contains what the
client actually sent rather than what anyone thinks it sent.
