{"record":{"id":"d1c5971f13d00fbd","repo":"Hmbown/CodeWhale","slug":"oauth-login-is-only-supported-for-url-based-mcp-se","errorCode":null,"errorMessage":"OAuth login is only supported for URL-based MCP servers","messagePattern":"OAuth login is only supported for URL-based MCP servers","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/oauth.rs","lineNumber":475,"sourceCode":"where\n    F: std::future::Future<Output = Result<T>>,\n{\n    tokio::select! {\n        biased;\n        _ = cancellation_token.cancelled() => bail!(\"OAuth login was cancelled\"),\n        result = future => result,\n    }\n}\n\nasync fn perform_oauth_login_for_server_inner(\n    name: &str,\n    server: &McpServerConfig,\n    explicit_scopes: Option<Vec<String>>,\n    callback_port: Option<u16>,\n    callback_url: Option<&str>,\n) -> Result<()> {\n    let Some(url) = server.url.as_deref() else {\n        bail!(\"OAuth login is only supported for URL-based MCP servers\");\n    };\n    if server_has_manual_authorization(server) {\n        bail!(\"MCP server '{name}' already has bearer/static Authorization configured\");\n    }\n\n    let discovery = if explicit_scopes.is_none() && server.scopes.is_empty() {\n        oauth_login_support(server).await?\n    } else {\n        None\n    };\n    let resolved_scopes = resolve_oauth_scopes(\n        explicit_scopes,\n        server.scopes.clone(),\n        discovery.and_then(|discovery| discovery.scopes_supported),\n    );\n\n    match perform_oauth_login(\n        name,","sourceCodeStart":457,"sourceCodeEnd":493,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/mcp/oauth.rs#L457-L493","documentation":"perform_oauth_login_for_server_inner requires server.url to be set because OAuth needs an HTTP issuer to discover authorization/token endpoints. A stdio/command-based McpServerConfig has no URL, so the function bails immediately (oauth.rs:475) before any discovery request is made. OAuth is only implemented for URL-based (remote) MCP servers.","triggerScenarios":"Calling perform_oauth_login_for_server (or the TUI MCP login command) on a server entry configured with a command/args (stdio transport) and no url field; the `let Some(url) = server.url.as_deref() else` branch is taken on the first line of the inner function.","commonSituations":"Config copied from a local stdio server template (npx/node/python launchers) and the user tries OAuth anyway; a server entry where the url key was typo'd or omitted so it silently fell back to stdio; assuming all MCP servers support OAuth.","solutions":["Set the server's url to its HTTP(S) endpoint and remove the command-based configuration, then retry login","If the server is genuinely a local process, use its documented static auth (bearer_token_env_var or an env-provided header) instead of OAuth","Check whether the server offers an HTTP/Streamable-HTTP mode and point the config at that URL"],"exampleFix":"// before (config TOML)\n[mcp.servers.my-server]\ncommand = \"npx\"\nargs = [\"-y\", \"some-mcp-server\"]\n\n// after\n[mcp.servers.my-server]\nurl = \"https://mcp.example.com/mcp\"","handlingStrategy":"validation","validationCode":"if server.url.is_none() {\n    tracing::info!(target: \"mcp\", \"skipping OAuth login for stdio server {name}\");\n    return Ok(());\n}","typeGuard":"fn is_url_based_server(server: &McpServerConfig) -> bool {\n    server.url.is_some()\n}","tryCatchPattern":null,"preventionTips":["Only expose the OAuth login action in the UI for servers with a url field","Lint MCP config: warn when OAuth is attempted on command-based entries","Document per-server which auth modes apply: stdio → env/static, url → OAuth"],"tags":["oauth","mcp","configuration","stdio"],"backgroundTag":"oauth-configuration-error","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}