screenpipe/screenpipe · error
invalid MCP resource URL: {}
Error message
invalid MCP resource URL: {} What it means
protected_resource_metadata_url parses the server's resource string as a reqwest::Url to derive well-known protected-resource metadata URLs; a parse failure produces this error wrapping the url::ParseError. The resource must be an absolute, valid URL for RFC 9728/9470 discovery to work.
Source
Thrown at crates/screenpipe-connect/src/mcp_servers.rs:1124
/// Note: do NOT use [`random_url_token`] here — it base64-encodes a 128-char
/// hex string, yielding a 171-char verifier. Strict token endpoints (e.g.
/// Krisp) validate the length and reject the exchange with
/// `400 invalid_request: Invalid parameter: code_verifier`.
fn pkce_verifier() -> String {
let mut raw = [0u8; 32];
raw[..16].copy_from_slice(uuid::Uuid::new_v4().as_bytes());
raw[16..].copy_from_slice(uuid::Uuid::new_v4().as_bytes());
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(raw)
}
fn pkce_challenge(verifier: &str) -> String {
let digest = Sha256::digest(verifier.as_bytes());
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(digest)
}
fn protected_resource_metadata_url(resource: &str) -> Result<Vec<String>> {
let resource_url =
reqwest::Url::parse(resource).map_err(|e| anyhow!("invalid MCP resource URL: {}", e))?;
let mut urls = Vec::new();
// Origin-based URL first — this is what Notion's guide and RFC 9470 recommend:
// new URL("/.well-known/oauth-protected-resource", serverUrl) resolves to the
// origin, not the path. Try this first to avoid spurious auth challenges on
// sub-path variants.
let mut origin = resource_url.clone();
origin.set_path("/.well-known/oauth-protected-resource");
origin.set_query(None);
origin.set_fragment(None);
urls.push(origin.to_string());
// RFC path variant (path component embedded after the well-known prefix).
let original_path = resource_url
.path()
.trim_start_matches('/')
.trim_end_matches('/');
if !original_path.is_empty() {View on GitHub (pinned to 4ebf712990)
Solutions
- Fix the resource field in the MCP server config to be an absolute URL including scheme (e.g. https://mcp.notion.com/mcp)
- Validate/normalize the URL (prepend https:// if scheme missing, trim whitespace) when saving config
- Verify no quoting/escape artifacts got persisted with the resource string
Example fix
// before: bare-host resource fails Url::parse
let urls = protected_resource_metadata_url("mcp.notion.com")?;
// after: normalize before calling
let resource = "mcp.notion.com";
let normalized = if resource.contains("://") { resource.to_string() } else { format!("https://{resource}") };
let urls = protected_resource_metadata_url(normalized.trim())?; Defensive patterns
Strategy: validation
Validate before calling
fn valid_resource_url(s: &str) -> bool {
reqwest::Url::parse(s.trim())
.map(|u| u.scheme().starts_with("http"))
.unwrap_or(false)
}
// call only if valid_resource_url(&cfg.resource) Type guard
fn parse_resource(s: &str) -> Option<reqwest::Url> {
let s = s.trim();
let s = if s.contains("://") { s.to_string() } else { format!("https://{s}") };
reqwest::Url::parse(&s).ok().filter(|u| u.scheme().starts_with("http"))
} Try / catch
match discovery_result {
Err(e) if e.to_string().contains("invalid MCP resource URL") => {
// normalize: trim + prepend scheme, then retry discovery
let fixed = normalize_url(&cfg.resource);
discovery_with_resource(&fixed).await
}
other => other,
} Prevention
- Always store resource as a full absolute URL with scheme
- Normalize (trim, prepend https://) at config-save time
- Add a config validator that runs Url::parse on resource before persisting
- Beware copy-paste artifacts (quotes, spaces, missing scheme) in URL fields
When it happens
Trigger: Calling the OAuth discovery path for an MCP server whose stored resource value is malformed: missing scheme ("notion.com/mcp"), fully empty, containing spaces/illegal characters, or a relative path.
Common situations: Config saved from a form where the resource URL was typed without https://; copy-paste dropping the scheme; server config migration introducing a placeholder resource value; trailing junk like quotes or whitespace in the stored string.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- OAuth is only supported for HTTP MCP servers
- invalid OAuth issuer URL: {}
- server_id is required
- tool is required
- unknown MCP server: {}
AI-assisted analysis of screenpipe/screenpipe@4ebf712990 (2026-09-01).
Data as JSON: /api/errors/ffdff804f5ac423f.
Report an issue: GitHub.