Skip to content

Auth-failure detection substring-matches server error text on the MCP tool-call path #2057

Description

@Aaronontheweb

Part of #2058. Design: openspec/changes/mcp-tool-outcome-receipts/design.md (D4).

Problem

McpClientManager.ReportToolFailure runs IsAuthFailureMessage over the text of an isError: true tool result and, on a substring match (unauthorized, forbidden, token expired, ...), moves the server to AuthFailed with the remedy Run: netclaw mcp auth <name>.

  • A normal business error such as {"error":"Request failed: 403 Forbidden"} from a tool that proxies a REST API trips the check. For a server that does not use OAuth, netclaw mcp auth is the wrong remedy. A false authentication_failed alert fires, and netclaw mcp list and doctor report auth failed until the next invocation reconnects for nothing.
  • A real credential failure on a server that does not use OAuth is not detected. A static bearer or API key that expires mid-session comes back as HTTP 401 on every tool call, but nothing marks the server, so netclaw mcp list keeps reporting Connected.

The rule must not read header names. A server can authenticate with any header. The daemon already knows whether it uses OAuth for a server: it holds OAuth tokens for it, or the SDK reported a Bearer challenge.

Expected

  • A 401 on a tool call or a catalog refresh moves the server to AuthFailed. The remedy follows the OAuth state, not the config shape: netclaw mcp auth when the daemon holds OAuth tokens for the server or the failure is an OAuth challenge; "check configured credentials or headers" otherwise.
  • A 403 on a tool call does not change the server state. It is an access_denied result only.
  • Tool-declared error text demotes a server only when the daemon holds OAuth tokens for it. Any other server stays Connected, with the Warning log unchanged.
  • The connect path uses the same rule. A bare 401 or 403 at initialize with no tokens and no challenge is AuthFailed with the credentials remedy, not Unreachable.
  • CreateUnavailableException and netclaw doctor read the remedy from the published status instead of a fixed string or a scheme test of their own.

Tests

  • HTTP server with an X-Api-Key header, no tokens; tool call throws 401. AuthFailed; message names credentials or headers; no netclaw mcp auth; next call reconnects.
  • HTTP server with stored OAuth tokens; tool call throws 401. AuthFailed; remedy names netclaw mcp auth.
  • HTTP server, no tokens; tool call throws the SDK's Bearer-challenge McpException. AuthFailed; remedy names netclaw mcp auth.
  • HTTP server; tool call throws 403. Stays Connected.
  • HTTP server, no headers, no tokens; isError: true with expired-token text. Stays Connected.
  • HTTP server with stored tokens; isError: true with expired-token text. AuthFailed (current behavior).
  • Static-header server and stdio server; isError: true with Forbidden text. Stay Connected.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmcpModel context protocol server / client issues.reliabilityRetries, resilience, graceful degradation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions