Kuberwastaken/claurst · error · anyhow::Error
MCP server ' ' has no URL configured (required for OAuth)
Error message
MCP server '{}' has no URL configured (required for OAuth) What it means
begin_auth() requires the server config to carry a URL because OAuth discovery is done over HTTP against the server's base URL. If the matched config has no 'url' field, this error is thrown. Stdio (command-based) MCP servers cannot participate in this OAuth flow.
Solutions
- Add a 'url' field pointing at the server's HTTP/SSE endpoint in its config entry
- If the server is stdio-only, skip OAuth for it — it does not use HTTP auth
- Verify the JSON config field is named 'url' and sits on the server object, not a sibling
- Use a different auth mechanism (env-var token) for command-based servers
Example fix
// before (settings.json)
{ "mcpServers": { "linear": { "command": "npx", "args": ["-y", "mcp-linear"] } } }
// after
{ "mcpServers": { "linear": { "url": "https://mcp.linear.app/sse" } } } Defensive patterns
Strategy: validation
Validate before calling
let cfg = hub.server_config(server_name)
.ok_or_else(|| anyhow::anyhow!("server '{}' not configured", server_name))?;
if cfg.url.is_none() {
anyhow::bail!("server '{}' is stdio-only; OAuth requires a url", server_name);
} Type guard
fn has_oauth_url(cfg: &McpServerConfig) -> bool {
cfg.url.as_deref().map_or(false, |u| u.starts_with("http"))
} Try / catch
match hub.begin_auth(server).await {
Ok(s) => s,
Err(e) if e.to_string().contains("no URL configured") => {
eprintln!("{server} has no url; use env-var auth instead");
return Ok(());
}
Err(e) => return Err(e),
} Prevention
- Give every remote MCP server a url field in its config
- Skip OAuth in tooling for servers configured with command (stdio)
- Validate server configs at load time: url required for remote servers
When it happens
Trigger: Calling McpHub::begin_auth(server_name) for a server whose config entry lacks a 'url' key — typically a stdio server defined with 'command' instead of 'url'.
Common situations: Attempting browser OAuth against a local stdio server; config migrated from another tool that used a different field name; url field accidentally deleted or mis-indented in settings.json.
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
- Unknown MCP server
- MCP server ' ' is configured as ' ' but missing URL
- Unknown MCP server
- invalid legacy SSE base URL
- MCP server ' ': unsupported transport type
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/ab3971a0490205cb.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/mcp/src/lib.rs:1228
}
/// Initiate OAuth 2.0 + PKCE for an HTTP MCP server.
pub async fn initiate_auth(&self, server_name: &str) -> anyhow::Result<String> {
Ok(self.begin_auth(server_name).await?.auth_url)
}
/// Build a full OAuth authorization session for an HTTP/SSE MCP server.
pub async fn begin_auth(&self, server_name: &str) -> anyhow::Result<oauth::McpAuthSession> {
let config = self
.server_configs
.get(server_name)
.ok_or_else(|| anyhow::anyhow!("Unknown MCP server: {}", server_name))?;
let base_url = config
.url
.as_deref()
.ok_or_else(|| {
anyhow::anyhow!(
"MCP server '{}' has no URL configured (required for OAuth)",
server_name
)
})?;
oauth::begin_mcp_auth(server_name, base_url).await
}
/// Run the browser-based OAuth flow and persist the resulting token.
pub async fn authenticate(&self, server_name: &str) -> anyhow::Result<oauth::McpAuthResult> {
let config = self
.server_configs
.get(server_name)
.ok_or_else(|| anyhow::anyhow!("Unknown MCP server: {}", server_name))?;
let base_url = config
.url
.as_deref()
View on GitHub (pinned to b0637c97ec)