Skip to content

Claude remote MCP connector: Anthropic's internal /tool_result and /attach_resource return 404/400 after successful OAuth #207

Description

@ninjamonkno1

What happened?

What happened?

A third-party remote HTTP MCP server (WordPress plugin, spec-compliant per @modelcontextprotocol/inspector --cli) cannot be used from Claude Desktop or Claude.ai despite the OAuth flow and MCP handshake succeeding end-to-end on the server side.

Two failure modes seen in the same 72-hour window against the same server:

  1. "Authorization with the MCP server failed. If this persists, share this reference with support: ofid_*" — appears even though our server's POST /oauth/token returned 200 with a valid access_token + refresh_token pair. Claude's backend then never uses the issued token against the MCP endpoint — zero Bearer-authenticated requests reach the server after the 200 token issuance.
  2. "Tool result could not be submitted. The request may have expired or the connection was interrupted. Refresh the page to continue." — appears AFTER the server successfully returned a tool result (tools/call → 200 with real data). Claude Desktop's own log shows its internal POST to /api/organizations/<ORG>/chat_conversations/<CID>/tool_result returning 404 from Anthropic's infrastructure.

The failing endpoints (/tool_result, /attach_resource) are under https://claude.ai/api/organizations/… — Anthropic's own backend, not the MCP server.

Failure is intermittent on the same connector with the same credentials. This matches three other open/closed reports in this repository in the same 72-hour window:

Three independent MCP servers across three different OAuth flows (DCR, static, pre-registered) all reporting ofid_* failures in the same window strongly suggests a regression in Anthropic's remote-MCP backend.

What did you expect to happen?

What did you expect to happen?

After the server returns HTTP 200 for POST /oauth/token with a valid access_token, Claude's backend should:

  1. Store the token against the connector.
  2. POST /mcp initialize with Authorization: Bearer <access_token> to open the session.
  3. POST /mcp tools/list and make subsequent tools/call requests as the user invokes them.

Additionally, after the server returns HTTP 200 for a valid tools/call with an MCP-spec-compliant result shape ({"content": [{"type": "text", "text": "..."}]}), Claude Desktop's frontend should render the result in the chat conversation. The internal POST /api/organizations/<ORG>/chat_conversations/<CID>/tool_result should accept the result (not return 404).

Steps to reproduce

Steps to reproduce

  1. Deploy any MCP server that is spec-compliant per @modelcontextprotocol/inspector --cli (verified with tools/list + tools/call both returning 200 end-to-end).
  2. In the admin UI, issue a pre-registered OAuth client_id (RFC 7591 style, stored in the server's client registry).
  3. In Claude Desktop → Settings → Connectors → Add custom connector:
    • Remote MCP server URL: https://<my-server>/mcp
    • Advanced settings → OAuth Client ID: paste the pre-registered c-xxxxxxxx-… value
    • OAuth Client Secret: any non-empty string (server accepts + ignores — it's a public-client-only OAuth provider using PKCE)
    • Click Add
  4. Complete the OAuth consent in the browser.
  5. Observe ONE of:
    • User-facing popup "Authorization with the MCP server failed … ofid_<hex>" — despite the server's /oauth/token returning 200 with a valid token pair in the same second.
    • OR: connector reports Connected; attempting a tool call in chat returns "Tool result could not be submitted. The request may have expired or the connection was interrupted." — despite the server returning a 200 tools/call response with valid content.
  6. Inspect ~/Library/Logs/Claude/claude.ai-web.log for the 404 from /api/organizations/<my-org-id>/chat_conversations/<conversation-id>/tool_result or the 400 from /api/organizations/<my-org-id>/mcp/attach_resource.

Area

OAuth / Authentication

MCP Server (if applicable)

Custom third-party remote HTTP MCP server (WordPress plugin). Not an Anthropic first-party connector. Transport: Streamable HTTP. OAuth 2.1 + PKCE S256. Spec versions negotiated: 2025-03-26, 2025-06-18, 2025-11-25 (server echoes the client's requested version).

Error messages or logs

### Anthropic backend 404 on `/tool_result` (captured in `~/Library/Logs/Claude/claude.ai-web.log`)


2026-04-22 12:15:27 [error] [REACT_QUERY_CLIENT] QueryClient error: {
  "type":"not_found_error",
  "statusCode":404,
  "endpoint":"/api/organizations/<my-org-id>/chat_conversations/<conversation-id-1>/tool_result",
  "method":"POST",
  "name":"Error",
  "message":"Tool result could not be submitted. The request may have expired or the connection was interrupted. Refresh the page to continue."
}


Second identical 404 on a different conversation:

2026-04-22 14:01:45 [error] ... endpoint:"/api/organizations/<my-org-id>/chat_conversations/<conversation-id-2>/tool_result" ... statusCode:404 ...


### Anthropic backend 400 on `/attach_resource`


2026-04-21 18:12:28 [error] [REACT_QUERY_CLIENT] QueryClient error: {
  "type":"invalid_request_error",
  "statusCode":400,
  "endpoint":"/api/organizations/<my-org-id>/mcp/attach_resource",
  "method":"POST",
  "name":"Error",
  "message":"Failed to attach resource"
}


Repeated at 18:13:51, 18:15:40, 19:04:45 on the same endpoint.

### `ofid_*` reference IDs captured from user-facing error popups

- `ofid_85cd2e4a0b73f3d5` (2026-04-20)
- `ofid_46e4754ae5da34ae`
- `ofid_e93e44ddf2c49036` (2026-04-21)
- `ofid_77168964b1b29021`
- `ofid_7d9751657753d0a3`
- `ofid_19da4fe1f9748428`
- `ofid_65e8a6546e02aeb4`
- `ofid_ba382a9d3e8cac60`
- `ofid_482b0ecebb7ebad1` (2026-04-22, after shipping the `client_secret_basic` fix)

### Server-side evidence that the MCP protocol and OAuth succeed

Successful OAuth + MCP handshake captured in the server's internal debug ring buffer, 2026-04-22 12:02:05–12:02:16 UTC:

| Timestamp UTC | Method | Status | User-Agent |
|---|---|---|---|
| 12:02:05 | `POST /oauth/token` (`grant_type=authorization_code`, PKCE S256, resource param matches canonical MCP URL) | **200** — access_token + refresh_token issued | `python-httpx/0.28.1` (Anthropic backend) |
| 12:02:08 | `POST /mcp initialize` (protocolVersion `2025-11-25`, clientInfo `Anthropic/ClaudeAI 1.0.0`) | **200** — session created | `Claude-User` |
| 12:02:08 | `POST /mcp notifications/initialized` | **202** | `Claude-User` |
| 12:02:09 | `POST /mcp tools/list` | **200** — 15 tools, 62.6ms | `Claude-User` |
| 12:02:09 | `POST /mcp resources/list` | **200** | `Claude-User` |
| 12:02:16 | `GET /mcp` (SSE stream open) | held | `Claude-User` |

Separate successful tool invocation captured 2026-04-22 13:44:11 UTC:
- `POST /mcp tools/call` name=`<tool>` → **200** in 80.6 ms with valid `{"content": [{"type": "text", "text": "..."}]}` response. Claude Desktop's UI showed "Tool result could not be submitted" immediately after, corresponding to the `/tool_result` 404 shown above.

Post-`client_secret_basic`-fix token issuance 2026-04-22 14:05:26 UTC:
- `POST /oauth/token`**200**. Access token + refresh token issued.
- **Zero subsequent Bearer-authenticated requests** from Claude's backend to `/mcp` in the 10-minute window. User saw "Authorization with the MCP server failed, `ofid_482b0ecebb7ebad1`" despite the successful issuance.

Additional context

What I ruled out on the server side (11 alpha versions of work)

  • Protocol shape — Inspector CLI passes end-to-end.
  • OAuth flow — Token endpoint returns 200 with valid access+refresh on every captured attempt.
  • PKCE — S256 verified on every auth_code exchange.
  • Audience binding (RFC 8707) — resource parameter enforced, matches canonical MCP URL.
  • WWW-Authenticate header shape — RFC 6750 compliant; error= omitted on no-token 401 per §3.1.
  • client_secret_basic support — Added per issue Cannot reach valid MCP server #200's resolution; did not resolve this symptom.
  • Protocol version negotiation — Server echoes client's requested version.
  • SSE GET handler — Returns real text/event-stream with heartbeats; Claude opens and holds the stream.
  • Redirect method — 302 on consent (no Oauth Issue #109-class 307-preservation bug).
  • Site-root /token and /authorize shortcuts — Direct PHP dispatch, no body-dropping 307.

What points to an Anthropic-side backend regression

  1. Three independent third-party MCP servers (this one, Cannot reach valid MCP server #200's portal.dalegroup.net, Claude UI: connection error #201's mcp-dev.shooju.com) all hit ofid_* failures in the same 72-hour window with the server code unchanged.
  2. Claude UI: connection error #201's reporter notes their colleague on a different Anthropic account connects to the same server without issue.
  3. Claude UI: connection error #201's server works fine with ChatGPT.
  4. /tool_result and /attach_resource 4xx responses visible in Claude Desktop's own ~/Library/Logs/Claude/claude.ai-web.log are under api/organizations/<ORG>/... — Anthropic infrastructure, not the MCP server.
  5. Server-side evidence shows successful token issuance followed by zero token use by Claude's backend in at least one captured attempt — no server code path can cause Claude's backend to skip sending a request.

Requested action

  1. Cross-reference the ofid_* IDs above against Anthropic's internal error traces.
  2. Investigate why /api/organizations/<ORG>/chat_conversations/<CID>/tool_result 404s for active conversations after a 200 tool response from the MCP server.
  3. Investigate why /api/organizations/<ORG>/mcp/attach_resource 400s for resources from a valid resources/list.
  4. Investigate why an issued access_token (200 from /oauth/token) is sometimes never subsequently used by Claude's backend against the MCP endpoint.
  5. If the behaviour is org-specific (as implied by Claude UI: connection error #201), surfacing the state flag at the affected org that differs from a working org would narrow the repro significantly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions