Hmbown/CodeWhale · error

OAuth login is only supported for URL-based MCP servers

Error message

OAuth login is only supported for URL-based MCP servers

What it means

OAuth login resolves the server's authorization endpoints from its URL; servers configured only with a command (stdio transport) have no URL to run a web OAuth flow against, so resolve_oauth_login rejects them up front. OAuth in this library is defined for HTTP/SSE (URL-based) MCP servers only.

Solutions

  1. Add a valid `url` to the server's McpServerConfig if it is actually an HTTP MCP server
  2. If the server is genuinely stdio-based, authenticate via environment variables or headers in the command config instead of OAuth
  3. Check the config for a typo that dropped the url field
  4. Use the server's documented auth mechanism (API key env var, static header) for command-based servers

Example fix

// before
{ "my-server": { "command": "npx", "args": ["-y", "mcp-remote"] } }
// after
{ "my-server": { "url": "https://mcp.example.com/mcp" } }
Defensive patterns

Strategy: validation

Validate before calling

fn can_oauth_login(server: &McpServerConfig) -> bool {
    server.url.as_deref().map(|u| u.starts_with("http")).unwrap_or(false)
}

Type guard

fn url_based_server(server: &McpServerConfig) -> Option<&str> {
    server.url.as_deref()
}

Try / catch

match server.url.as_deref() {
    None => eprintln!("stdio server: use env vars/headers for auth, not OAuth"),
    Some(_) => perform_oauth_login_for_server(&server).await?,
}

Prevention

When it happens

Trigger: Calling perform_oauth_login_for_server (via resolve_oauth_login) for an McpServerConfig where server.url is None — i.e. a stdio/command-based server such as {"command": "npx", "args": ["-y", "some-mcp"]}.

Common situations: Running /mcp login against a locally launched stdio MCP server; a config where the url field was omitted or mistyped so the server fell back to command mode; users assuming all MCP servers support OAuth.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/d1c5971f13d00fbd. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/mcp/oauth.rs:1112

{
    tokio::select! {
        biased;
        _ = cancellation_token.cancelled() => bail!("OAuth login was cancelled"),
        result = future => result,
    }
}

/// Shared gate + scope resolution for `/mcp login` and the model-driven
/// authenticate tool: URL-based servers only, no manual Authorization config,
/// scopes from explicit argument, config, or discovery (in that order).
async fn resolve_oauth_login(
    name: &str,
    server: &McpServerConfig,
    explicit_scopes: Option<Vec<String>>,
    network_policy: Option<&NetworkPolicyDecider>,
) -> Result<(String, ResolvedMcpOAuthScopes)> {
    let Some(url) = server.url.as_deref() else {
        bail!("OAuth login is only supported for URL-based MCP servers");
    };
    if server_has_manual_authorization(server) {
        bail!("MCP server '{name}' already has bearer/static Authorization configured");
    }

    let discovery = if explicit_scopes.is_none() && server.scopes.is_empty() {
        oauth_login_support(server, network_policy).await?
    } else {
        None
    };
    let resolved_scopes = resolve_oauth_scopes(
        explicit_scopes,
        server.scopes.clone(),
        discovery.and_then(|discovery| discovery.scopes_supported),
    );
    Ok((url.to_string(), resolved_scopes))
}

View on GitHub (pinned to 73e0f67d83)