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
- Add a valid `url` to the server's McpServerConfig if it is actually an HTTP MCP server
- If the server is genuinely stdio-based, authenticate via environment variables or headers in the command config instead of OAuth
- Check the config for a typo that dropped the url field
- 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
- Only advertise OAuth login for servers with an http(s) url in config
- For stdio/command servers, wire auth through environment variables
- Validate McpServerConfig shape before presenting login options in UI
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
- invalid Codewhale-owned ChatGPT OAuth generation; expected…
- invalid Codewhale-owned xAI OAuth generation; expected…
- invalid JSON payload at key
- invalid MCP OAuth callback port 0
- MCP server ' ' already has bearer/static Authorization…
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)