different-ai/openwork · error · PluginArchRouteFailure
invalid_mcp_url
invalid_mcp_url
Error message
MCP server URL is invalid.
What it means
First gate in assertRemotePluginMcpUrl(): the candidate URL string cannot be parsed by the WHATWG URL constructor. Thrown as PluginArchRouteFailure(400, 'invalid_mcp_url') so callers can distinguish malformed input from policy violations.
Source
Thrown at ee/apps/den-api/src/routes/org/plugin-system/store.ts:4913
return { config: entry.config, name: entry.name, url }
}
function configVersionOwnsImportedExternalMcpConnection(version: ConfigObjectVersionRow, connectionId: string) {
const spec = parseConfigObjectVersionSpec(version)
const metadata = isRecord(spec.metadata) ? spec.metadata : null
const recordedConnectionId = readRecordString(spec, "externalMcpConnectionId")
|| (metadata ? readRecordString(metadata, "externalMcpConnectionId") : null)
const owned = spec.externalMcpConnectionOwnedByPlugin === true
|| metadata?.externalMcpConnectionOwnedByPlugin === true
return owned && recordedConnectionId === connectionId
}
async function assertRemotePluginMcpUrl(url: string) {
let parsed: URL
try {
parsed = new URL(url)
} catch {
throw new PluginArchRouteFailure(400, "invalid_mcp_url", "MCP server URL is invalid.")
}
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
throw new PluginArchRouteFailure(400, "invalid_mcp_url", "MCP URLs must use HTTP or HTTPS.")
}
if (parsed.protocol === "http:" && !env.allowPrivateMcpUrls) {
throw new PluginArchRouteFailure(400, "invalid_mcp_url", "Hosted MCP connections must use HTTPS.")
}
if (parsed.hash) {
throw new PluginArchRouteFailure(400, "invalid_mcp_url", "MCP URLs must not contain a fragment.")
}
if (parsed.username || parsed.password) {
throw new PluginArchRouteFailure(400, "invalid_mcp_url", "MCP URLs must not contain embedded credentials.")
}
const sensitiveParameters = new Set(["access_token", "api_key", "client_secret", "token", "refresh_token", "id_token", "code_verifier"])
for (const parameter of parsed.searchParams.keys()) {
if (sensitiveParameters.has(parameter.toLowerCase())) {View on GitHub (pinned to 2b7df46e8a)
Solutions
- Prepend the scheme: change 'mcp.example.com/sse' to 'https://mcp.example.com/sse'.
- Trim whitespace and remove non-ASCII/typographic characters from the stored URL.
- Sanity-check in a browser or with `new URL(url)` locally — if it throws, the server will reject it too.
- Ensure template variables were substituted before the value reached the config.
Example fix
// before url: "mcp.example.com:443/sse" // after url: "https://mcp.example.com/sse"
Defensive patterns
Strategy: validation
Validate before calling
function isParseableUrl(value: string): boolean {
try { new URL(value.trim()); return true } catch { return false }
}
if (!isParseableUrl(url)) throw new Error(`Not an absolute URL: ${url}`) Type guard
function isAbsoluteUrl(value: unknown): value is string {
if (typeof value !== "string") return false
try { new URL(value); return true } catch { return false }
} Try / catch
try {
await configureRemoteMcp(url)
} catch (e) {
if (isPluginArchRouteFailure(e) && e.code === "invalid_mcp_url") {
// prompt user for a full absolute https:// URL
}
} Prevention
- Always store MCP URLs with an explicit scheme (https://...)
- Trim whitespace before storing URL input
- Validate with new URL() in the UI before submitting
- Never submit template placeholders unsubstituted
When it happens
Trigger: Configuring a remote MCP server where the `url` value is not a parseable absolute URL — e.g. 'mcp.example.com' (no scheme), 'localhost:3000/sse' (parsed as scheme localhost), empty string, or containing spaces/illegal characters.
Common situations: Users entering a bare hostname without protocol; template placeholders like '${MCP_URL}' left unsubstituted; copy-paste artifacts (trailing spaces, smart quotes); relative URLs pasted from docs.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- OpenWork-managed OAuth requires a remote MCP URL.
- protocol "${url.protocol}" is not allowed
- OpenWork Connect MCP metadata is invalid.
- extensions.add_mcp_url_required
- embedded URL credentials are not allowed
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/170c04cb4df25eb5.
Report an issue: GitHub.