[BUG] Custom MCP connector: tool calls dropped before reaching origin — Claude Code, claude.ai Chat and Cowork

Status Closed — not planned
Maintainer reply None cached
Activity 1 comment · opened Sep 13, 2026 · closed Oct 12, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Tool calls to a custom self-hosted remote MCP connector are intermittently dropped before reaching the origin server. The client reports an error blaming my infrastructure; the origin's access logs prove no request ever arrived.

Observed on three separate surfaces, same account, same connector, same day:

  • Claude Code (desktop app, on my own Windows machine) — This connector's server hostname doesn't resolve or isn't reachable from this network.
  • claude.ai Chat — same message, and usefully it carries a request_id:

{"error": "This connector's server hostname doesn't resolve or isn't reachable from this network. The connector may be misconfigured.", "request_id": "req_011CezoxwxEMbE9gCwJv9e5u"}

  • Claude Cowork (cloud-hosted on Anthropic infrastructure, not local) — Cloudflare 502 carrying "zone":"api.anthropic.com" and "error_category":"origin".

That spread matters: my local network, DNS and machine cannot account for failures originating from cloud Cowork and from claude.ai, and a client-side defect would have to exist independently in three unrelated clients.

Measured rate: 2 drops in 62 calls (~3%) across six batches. The two clearest instances:

  • Claude Code, 6 concurrent calls — 5 arrived at origin (200 OK), 1 never arrived
  • claude.ai Chat, 18 sequential calls — 17 arrived (200 OK), 1 never arrived (03_voice), with the request_id above

The second is important twice over: that batch was dispatched sequentially, one call per turn-step, so concurrency is not required; and re-running the identical 18-path request immediately afterwards returned 18/18, including the exact path that had failed — so it is not path-, payload- or content-dependent either.

The origin was healthy throughout — 0 errors, 0 restarts, and zero 5xx responses in its entire access log history. It has never returned a 502 to anything.

Origin setup: self-hosted Python MCP server (mcp SDK / FastMCP), Streamable HTTP, json_response=True, behind a Caddy reverse proxy, MCP-native OAuth + bearer token. Seven such services on the same host behind the same proxy and auth.

Already ruled out — origin-side session state. Suspecting the client and origin were disagreeing about session identity, I switched the server from stateless_http=False to stateless_http=True, so there is no in-memory session to lose and no Mcp-Session-Id to go stale. The symptom persisted unchanged.

What Should Happen?

Tool calls to a custom remote MCP connector should reach the origin — or, when they cannot, fail with an error that names the actual failure.

Specifically:

  1. Tool calls should not be silently dropped in transit. Two vanished out of 44, on two different surfaces, one of them from a purely sequential batch. The origin never saw either.
  1. A delivery failure should not be reported as an origin failure. "This connector's server hostname doesn't resolve or isn't reachable from this network" is actively misleading: at the moment it was shown, the hostname resolved on public DNS, the host was reachable, and it was returning 200 OK to Anthropic's own egress addresses immediately before and after — in the 18-call batch, on the calls either side of the one that failed. That message cost me roughly a day investigating DNS, tunnel config, reverse-proxy routing and service health before the origin's access logs showed nothing was arriving at all.
  1. Likewise the 502 should not carry "error_category":"origin" when the origin received no request.
  1. Surface the request_id consistently. claude.ai Chat returns one with this error — req_011CezoxwxEMbE9gCwJv9e5u — which should be enough to trace the failing request server-side. Claude Code's version of the same error surfaces no identifier at all, and Cowork's 502 gives only a Cloudflare ray_id. Same failure, three different levels of traceability.
  1. retry_after should be usable. Every 502 advertises "retryable": true, "retry_after": 60, but a retry at +81 seconds returned another 502; recovery took roughly three minutes and two attempts.

Error Messages/Logs

=== THE MOST TRACEABLE INSTANCE (claude.ai Chat) ===
One call out of 18 failed. Verbatim error, including the request_id:

{"error": "This connector's server hostname doesn't resolve or isn't reachable from this
network. The connector may be misconfigured.", "request_id": "req_011CezoxwxEMbE9gCwJv9e5u"}

Origin side for that same batch:
  CallToolRequest received: 17      (Caddy: 17 x 200, 1 x 400 handshake probe)
  CallToolRequest missing:   1      <- the 03_voice call above
  service errors: 0
  NRestarts:      0
The 17 that arrived include the calls immediately before and after the one that vanished,
so the origin was reachable and answering throughout.

=== SAME ERROR, CLAUDE CODE (no request_id surfaced) ===
This connector's server hostname doesn't resolve or isn't reachable from this network.
The connector may be misconfigured.

Origin side, 6 concurrent calls from Claude Code:
  CallToolRequest received: 5       (all 200 OK)
  CallToolRequest missing:  1
  service errors: 0,  NRestarts: 0

Caddy access log, same window (the 400s are normal handshake probes):
  12:10:59  400  160.79.106.165
  12:11:00  200  160.79.106.168
  12:11:00  400  160.79.106.185
  12:11:01  200  160.79.106.160
  12:11:02  200  160.79.106.179
  12:11:02  200  160.79.106.164
  12:11:03  200  160.79.106.36

=== CLAUDE COWORK (cloud-hosted) ===
Error POSTing to endpoint: {"title":"Error 502: Bad gateway","status":502,
"error_name":"origin_bad_gateway","error_category":"origin","zone":"api.anthropic.com",
"cloudflare_error":true,"retryable":true,"retry_after":60}

Cloudflare ray IDs (all 2026-09-13, all on vault_edit):
  03:17:43Z  a3a4002208b827e1   502
             backoff 70s
  03:19:04Z  a3a402185a21cd80   502 again (+81s, past the advertised retry_after of 60)
             backoff 100s
             200, write applied (~+187s after the first failure)
  03:39:39Z  a3a4203f2d7c22b9   502  ->  backoff 95s  ->  200
  03:46:41Z  a3a42a922a3e2322   502  ->  backoff 95s  ->  200
Every retry used a byte-identical payload, so payload size/shape is not the trigger.

=== EARLIER FAILURE WINDOW - CallTool counts per connector ===
Tool invocations not arriving while session setup for the same connectors did:
  vault      CallTool=5  last 01:58:43   <- 11:19 call never arrived
  search     CallTool=3  last 11:11:14   <- 11:19 call never arrived
  evidence   CallTool=0  none that day
  legal      CallTool=0  none that day
  rkb        CallTool=1  last 11:19:38   <- arrived, 200 OK
  assets     CallTool=1  last 11:19:46   <- arrived, 200 OK

During that window the origin logged a COMPLETE, successful session for one of the
"failing" connectors: "Created new transport" followed by ListToolsRequest -> 200 OK.
The connection was established and the tool list served; only the invocation was lost.

=== ORIGIN HEALTH ACROSS ALL OF THE ABOVE ===
  NRestarts = 0 throughout (no crash, no automatic restart)
  uptime ~19 days at the first failure; ~19 hours at the later ones, the only restart
    in between being my own deliberate one for the stateless_http change
  5xx responses in the entire access log: 0
  401s present and correct (unauthenticated internet scanner probes)

Steps to Reproduce

No deterministic reproduction — it is a low-rate intermittent fault. Reporting the measurement rather than a recipe, because my first attempt at a recipe did not survive testing.

  1. Add a custom remote MCP connector (claude.ai → Settings → Connectors) pointing at a self-hosted Streamable HTTP MCP server you control — one whose access logs and application logs you can read.
  2. Issue batches of tool calls to it and record the client-side success count.
  3. Compare that against the origin's access log and application log for the same window.

Measured across six batches on three surfaces:

| batch | client | sent | arrived at origin | dropped |
|---|---|---|---|---|
| 1 | Claude Code | 6 (concurrent) | 5 | 1 |
| 2 | Cowork (cloud) | 6 | 6 | 0 |
| 3 | Claude Code | 8 (concurrent) | 8 | 0 |
| 4 | claude.ai Chat | 6 (sequential) | 6 | 0 |
| 5 | claude.ai Chat | 18 (sequential) | 17 | 1 |
| 6 | claude.ai Chat | 18 (sequential) | 18 | 0 |
| total | | 62 | 60 | 2 |

Roughly 3%, on at least two different surfaces. Per-surface: Claude Code 1 drop in 14, claude.ai Chat 1 drop in 42 — numbers far too small to claim any surface is more affected than another.

Concurrency is not the trigger. I initially thought it was, because the first batch dropped one immediately. Batch 5 disproves it: 18 calls dispatched sequentially, one per turn-step, and one still vanished. Batch 6 was byte-for-byte the same request as batch 5 and came back clean, including the exact path that had failed. Larger batches simply give the fault more chances to fire.

Step 3 is the one that matters. Comparing the client's success count against the origin's own logs is what distinguishes a delivery failure from an origin failure. Without it this presents as an origin or network fault — exactly what all three error messages assert, and where I wasted a day.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

Claude 1.52386.3 (41896e) 2026-09-11T17:35:10.000Z — Claude for Windows desktop app

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Other

Additional Information

Versions and surfaces

  • Claude for Windows desktop app — Claude 1.52386.3 (41896e) 2026-09-11T17:35:10.000Z
  • claude.ai Chat — web, Opus 4.6
  • Claude Cowork, cloud-hosted — not local. The currently-active status.claude.com incident "Degraded functionality for Claude Cowork on Windows" concerns local Cowork and a Sep 8 Windows update blocking local command execution, so it does not apply to these sessions.
  • Windows 11
  • The bundled Claude Code CLI version could not be read separately — the app ships as an MSIX package under Program Files\WindowsApps, which is ACL-protected, and claude --version is not on PATH for this install. The app version string above is what the app itself reports.
  • status.claude.com reported "Claude API: Operational" throughout every failure described here.

Possibly relevant: gateway egress rotates between requests

Within a single logical session, requests reach the origin from many distinct Anthropic egress addresses. Observed across one day: 160.79.106.36, .37, .160, .161, .163, .164, .165, .168, .173, .175, .176, .178, .179, .183, .184, .185, .187. Consecutive requests in one round-trip routinely come from different nodes — e.g. a 400 from .184 at 15:12:39 followed by the 200 OK CallToolRequest from .187 at 15:12:40.

Recording this as an observation only, not a claim about cause.

Related issues

  • #48277, #48291 — same Error POSTing to endpoint … 502 string, but mcp-proxy.anthropic.com and Anthropic-hosted connectors; #48291 closed as not planned
  • #69426 — a 502 on a connected MCP server driving a retry loop that drained a session's token budget
  • #6341 — closed as duplicate; different root cause (file token limits)
  • #64414 — different layer (custom model API gateway, not MCP), but a documented case of Cowork and Code diverging against custom infrastructure; closed as not planned
  • #61682 — Anthropic-hosted GitHub connector reporting "Connected" while exposing no tools; similar "connected but non-functional" presentation, but no custom origin and no delivery failure

This report differs from all of them in being a custom self-hosted connector, failing across three surfaces (local Claude Code, claude.ai Chat, and cloud Cowork), with origin-side logs proving non-arrival, a measured drop rate over 44 calls, a request_id for one failing request, and origin session state experimentally eliminated.

Note

The origin hostname is deliberately omitted — it is a publicly-reachable endpoint fronting private material. Happy to share it privately with Anthropic if that helps trace req_011CezoxwxEMbE9gCwJv9e5u or the ray IDs above.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗