# Troubleshooting the MCP Server Handler

When an AI client calls a tool exposed by the
[MCP Server handler](../handlers/mcp-server.mdx), a failure often surfaces as
nothing more than `Tool call failed: 500`, with no indication of _why_. The
underlying gateway route can return `200` while the MCP client still rejects the
response, which makes these failures hard to diagnose from the client alone.

This page shows how to turn on the handler's debug logging, read the log lines
that reveal a misconfiguration, and fix the most common cause: a tool that
advertises an output schema but returns no structured content.

:::tip

Most "200 on the gateway, error in the client" failures come from the
[`includeOutputSchema` and `includeStructuredContent` options](../handlers/mcp-server.mdx#mcp-2025-06-18-global-options).
If you enabled one without the other, jump straight to
[Tool call failed: 500](#tool-call-failed-500--has-an-output-schema-but-did-not-return-structured-content).

:::

## Enable debug logging

The MCP Server handler can emit verbose logs covering server startup, tool
registration, and each tool call. These logs are the fastest way to confirm what
the server actually advertised to the client.

<Stepper>

1. **Turn on `debugMode`.** Set `debugMode: true` in the handler options for
   your MCP route in `routes.oas.json`:

   ```json
   "post": {
     "x-zuplo-route": {
       "handler": {
         "export": "mcpServerHandler",
         "module": "$import(@zuplo/runtime)",
         "options": {
           "name": "example-mcp-server",
           "version": "1.0.0",
           "debugMode": true
         }
       }
     }
   }
   ```

2. **Redeploy so the server cold-starts.** Some of the most useful log lines —
   server startup and tool registration — are written only when the MCP server
   initializes (a cold start). An already-running worker won't re-emit them.
   Deploy the working copy or environment again so a fresh worker boots with
   `debugMode` enabled.

3. **View the logs.** Open your project in the Portal and go to **Observability
   → Logs**. Trigger a tool call from your MCP client, then read the entries for
   the MCP route. Filter on the request's `zuplo-request-id` to isolate a single
   call.

</Stepper>

:::caution

Leave `debugMode` off in production. It logs verbose per-call detail that adds
noise and overhead. Turn it on to diagnose an issue, then turn it back off and
redeploy.

:::

### Key log lines to read

With `debugMode: true`, the handler writes lines at each stage of its lifecycle.
The exact format may change between runtime versions, but the fields below are
what you're looking for:

| Log line                       | When it's written       | What to check                                                          |
| ------------------------------ | ----------------------- | ---------------------------------------------------------------------- |
| `MCP Server cold start`        | A fresh worker boots    | Confirms `debugMode` is active and a new worker started                |
| `MCP tool registered`          | Each tool is registered | The `includeStructuredContent` and `hasOutputSchema` fields for a tool |
| `MCP Server response complete` | A tool call finishes    | The response was produced and sent back to the client                  |

The `MCP tool registered` line is the important one. For a tool whose calls fail
in the client, you'll typically see a line resembling:

```
MCP tool registered { name: "get_current_weather", hasOutputSchema: true, includeStructuredContent: false }
```

`hasOutputSchema: true` with `includeStructuredContent: false` is the exact
misconfiguration described in the next section.

## Tool call failed: 500 / "has an output schema but did not return structured content"

**Symptom.** An AI client (such as Claude) reports `Tool call failed: 500` when
it invokes a tool. With more verbose client logging, the underlying error reads:

```
Tool <tool_name> has an output schema but did not return structured content
```

Meanwhile, the gateway's own logs show the route that backs the tool returned
`200`.

**Likely cause.** The handler is configured with `includeOutputSchema: true` but
not `includeStructuredContent: true`. Under the
[MCP `2025-06-18`](https://modelcontextprotocol.io/specification/2025-06-18)
structured-content behavior, when a tool advertises an `outputSchema`, the
client expects every successful result to include a matching `structuredContent`
object. With `includeOutputSchema` on and `includeStructuredContent` off, the
server advertises the schema but returns only `text` content, so a
spec-compliant client rejects an otherwise-successful `200` response.

The `MCP tool registered` debug line confirms it:
`hasOutputSchema: true, includeStructuredContent: false`.

**Fix.** Enable both options together on the MCP Server handler:

```json
"options": {
  "name": "example-mcp-server",
  "version": "1.0.0",
  "includeOutputSchema": true,
  "includeStructuredContent": true
}
```

Then redeploy. The next `MCP tool registered` line should read
`hasOutputSchema: true, includeStructuredContent: true`, and the tool call
succeeds.

:::note

If you don't need output schemas at all, the alternative fix is to turn both off
(the defaults). The two options are designed to move together: enable
`includeOutputSchema` _only_ alongside `includeStructuredContent`.

:::

For the full definitions of both options, see the
[MCP `2025-06-18` Global Options](../handlers/mcp-server.mdx#mcp-2025-06-18-global-options)
in the handler reference.

## Client-side debugging tools

When the gateway looks healthy, reproduce the failure against the client to see
the protocol-level error the AI client hides:

- **MCP Inspector** — the
  [official inspector](https://github.com/modelcontextprotocol/inspector)
  (`npx @modelcontextprotocol/inspector`) connects to your remote server, lists
  tools, and calls them while showing the raw JSON-RPC messages. See
  [Testing](./testing.mdx) for connection steps.
- **mcpjam** — the [mcpjam inspector](https://github.com/mcpjam/inspector) is an
  open-source MCP testing client that's useful for exercising tool calls and
  inspecting responses outside of an AI client.
- **Claude Code** — run with the `--debug` flag (for example, `claude --debug`)
  to print MCP protocol traffic, including the full tool-call error that the
  chat UI summarizes as `Tool call failed: 500`.

## Checklist: the gateway returns 200 but the client errors

When a tool call fails in the client but the gateway reports success, verify
each of these in order:

1. **`debugMode` is on and a fresh worker booted.** Confirm a
   `MCP Server cold start` line appears after your latest deploy. Without a cold
   start, you're reading stale logs.
2. **Schema and structured content move together.** In the `MCP tool registered`
   line, `hasOutputSchema` and `includeStructuredContent` should either both be
   `true` or both be `false` — never `hasOutputSchema: true` with
   `includeStructuredContent: false`.
3. **The output schema is a valid `type: object` JSON Schema.** Some clients
   reject schemas that aren't `type: object`. The same applies to the
   `structuredContent` the server returns. See the
   [compatibility caveat](../handlers/mcp-server.mdx#mcp-2025-06-18-global-options).
4. **The route really returns JSON.** `includeStructuredContent` parses the
   response body as JSON to build `structuredContent`. A non-JSON `200` body
   can't be parsed into the advertised schema.
5. **Reproduce in a raw client.** Call the tool through the
   [MCP Inspector](./testing.mdx) or `curl` to read the JSON-RPC error directly,
   rather than the client's summarized `500`.

## Next steps

- [MCP Server handler reference](../handlers/mcp-server.mdx) — every handler
  configuration option.
- [Testing your MCP server](./testing.mdx) — MCP Inspector and `curl` recipes.
- [MCP Server tools](./tools.mdx) — editing tool definitions and `outputSchema`.
