镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

CLI: OAuth denials and other non-usage failures exit 1 with code "error", the same as a usage error #2842

Open

Description

@CloneOfAlex

Which version line?

v2 — current (@modelcontextprotocol/inspector@latest)

Which client?

CLI

Inspector version

2.13.0 (v2/main @ d7f6cee, built from source)

Node version

v22.23.4

Operating system (and browser, for the web client)

Linux (Debian)

Transport

Streamable HTTP

MCP server under inspection

The repo's own OAuth test server: node test-servers/build/server-composable.js --config test-servers/configs/oauth-revocation-http.json (Streamable HTTP on :8083, DCR + refresh + revocation).

Steps to reproduce

  1. Start the OAuth test server above.
  2. mcp-inspector --cli http://127.0.0.1:8083/mcp --transport http --method tools/list (interactive OAuth).
  3. Decline at the authorization server, so the callback carries error=access_denied.

Expected behavior

A distinct, machine-readable outcome for an OAuth protocol error — e.g. code oauth_error with exit 3 (AUTH_REQUIRED), and error / error_description as separate envelope fields — so a script or CI job can tell "the authorization server said no" from "the command line is wrong".

Actual behavior

{"error":{"code":"error","message":"<error_description>"}} and exit 1, which EXIT_CODES defines as USAGE.

The catch-all at the end of classifyError (core/cli/error-handler.ts) returns EXIT_CODES.USAGE / "error" for anything not matched earlier, so OAuth callback errors, OAuth callback timeouts and fetch-guard refusals (FetchTargetError) all exit 1 with the same code as a mistyped flag.

Logs, errors, or screenshots

Suggested: classify OAuth callback / token-endpoint protocol errors as oauth_error (exit 3), FetchTargetError as fetch_refused, the callback timeout as its own code, and keep exit 1 for genuine usage errors only.

Already prototyped a fix?

No response

Before you submit

  • I searched existing issues and this is not a duplicate.
  • This is not a security vulnerability report (those go through the private advisory process).

Activity

  1. cliffhall commented on Oct 29, 2026

    @cliffhall
    Member

    Triage: Priority Low (total 5)

    • Severity 2 — scripts cannot tell an OAuth denial from a usage error by exit code; the message still distinguishes them
    • Urgency 2 — wanted eventually, nothing blocked on it
    • Bonuses: +1 bug label (no milestone, assignee, epic, engagement or reporter-set priority)

    Board: #28, Status Incoming (awaiting maintainer review — no milestone yet).

  2. modified the milestones: v2.14.0, v2.11.0 on Oct 29, 2026
  3. Ben-Home commented on Oct 30, 2026

    @Ben-Home
  4. techaurah commented on Nov 6, 2026

    @techaurah

    Design notes for this one, following the repo's contribution flow (comment first, maintainers open the PR).

    Problem. When an OAuth-protected server fails during discovery or token exchange, the client surfaces a generic connection failure. Three distinct failure classes are collapsed into one: (1) protocol errors returned by the authorization server (a structured OAuth error / error_description payload, for example invalid_client or invalid_grant), (2) transport failures reaching the target (FetchTargetError in the auth layer), and (3) timeouts waiting on the authorization server (OAuthRequestTimeoutError). Users cannot tell whether to fix their server configuration, their network, or simply retry.

    Proposal.

    1. Preserve the OAuth error payload end to end: when the authorization server returns a structured error, surface error and error_description in the connection error UI instead of replacing them with a generic message, and log the raw payload once at debug level.
    2. Map the three classes to three distinct user-facing states: protocol error (show the server's description and point at server configuration), target unreachable (FetchTargetError: show the target URL and the underlying fetch failure), and timeout (OAuthRequestTimeoutError: show the elapsed limit and offer a retry action).
    3. Keep the mapping in one place: a single error-normalization step in the auth flow that converts thrown errors into a typed connection error, so future error classes land in the same UI path instead of each call site formatting its own message.

    What this deliberately does not do. No retry policy changes and no changes to the token flow itself; this is about faithful reporting of errors the code already detects.

    Happy to refine the class boundaries against the current auth code if maintainers want a different split before a PR is opened.

  5. CloneOfAlex commented on Nov 9, 2026

    @CloneOfAlex
    Author

    Checked against the 2.14.0 release: still reproduces. classifyError in the CLI build still ends in the catch-all that returns EXIT_CODES.USAGE with code "error", and there is no oauth_error code anywhere in the package. An OAuth denial, an OAuth callback timeout and a FetchTargetError all still exit 1, the same as a mistyped flag.

    Two notes:

    1. Milestone. This is now on v2.11.0, which is closed and older than the 2.13.0 build I reported against. Looks like it was meant to stay on v2.14.0 (or the next open one) before that shipped. @cliffhall could you move it?

    2. Scope. The design notes above are about the web client's connection-error UI. That is a fair improvement, but it is a different surface. This issue is only the CLI: the JSON envelope code and the process exit code that scripts and CI branch on. A mapping that would close it:

    Failure code Exit
    Authorization server returns error / error_description (callback or token endpoint) oauth_error (with error and error_description as separate fields) 3 (AUTH_REQUIRED)
    Timed out waiting for the OAuth callback oauth_timeout 3 (AUTH_REQUIRED)
    Fetch guard refused the target (FetchTargetError) fetch_refused not 1, since it is a policy refusal, not bad arguments
    Genuine usage error error / usage 1

    I see fork commits referencing this issue but no PR yet. Once there is a branch, I'm happy to verify it against test-servers/configs/oauth-revocation-http.json with the same decline-at-consent repro.

  6. CloneOfAlex commented on Nov 9, 2026

    @CloneOfAlex
    Author

    Checked against the 2.14.0 release: still reproduces. classifyError in the CLI build still ends in the catch-all that returns EXIT_CODES.USAGE with code "error", and there is no oauth_error code anywhere in the package. An OAuth denial, an OAuth callback timeout and a FetchTargetError all still exit 1, the same as a mistyped flag.

    Two notes:

    1. Milestone. This is now on v2.11.0, which is closed and older than the 2.13.0 build I reported against. Looks like it was meant to stay on v2.14.0 (or the next open one) before that shipped. @cliffhall could you move it?

    2. Scope. The design notes above are about the web client's connection-error UI. That is a fair improvement, but it is a different surface. This issue is only the CLI: the JSON envelope code and the process exit code that scripts and CI branch on. A mapping that would close it:

    Failure code Exit
    Authorization server returns error / error_description (callback or token endpoint) oauth_error (with error and error_description as separate fields) 3 (AUTH_REQUIRED)
    Timed out waiting for the OAuth callback oauth_timeout 3 (AUTH_REQUIRED)
    Fetch guard refused the target (FetchTargetError) fetch_refused not 1, since it is a policy refusal, not bad arguments
    Genuine usage error error / usage 1

    I see fork commits referencing this issue but no PR yet. Once there is a branch, I'm happy to verify it against test-servers/configs/oauth-revocation-http.json with the same decline-at-consent repro.

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 workingv2Issues and PRs for v2

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions