{"record":{"id":"9e5e1e5fe9a0aab0","repo":"odysseus-dev/odysseus","slug":"url-is-required-for-sse-transport","errorCode":null,"errorMessage":"url is required for SSE transport","messagePattern":"url is required for SSE transport","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/mcp/mcp_routes.py","lineNumber":180,"sourceCode":"        transport: str = Form(\"stdio\"),\n        command: str = Form(None),\n        args: str = Form(\"[]\"),\n        env: str = Form(\"{}\"),\n        url: str = Form(None),\n        oauth_file: str = Form(None),\n        oauth_config: str = Form(None),\n    ):\n        \"\"\"Add a new MCP server config and attempt connection. Admin-only:\n        registering a stdio server is equivalent to executing arbitrary\n        binaries on the host.\"\"\"\n        require_admin(request)\n        server_id = str(uuid.uuid4())[:8]\n\n        # Validate\n        if transport == \"stdio\" and not command:\n            raise HTTPException(400, \"command is required for stdio transport\")\n        if transport == \"sse\" and not url:\n            raise HTTPException(400, \"url is required for SSE transport\")\n        if transport == \"http\" and not url:\n            raise HTTPException(400, \"url is required for HTTP transport\")\n\n        # Parse JSON fields\n        try:\n            parsed_args = json.loads(args) if args else []\n        except json.JSONDecodeError:\n            parsed_args = []\n        try:\n            parsed_env = json.loads(env) if env else {}\n        except json.JSONDecodeError:\n            parsed_env = {}\n        if not isinstance(parsed_env, dict):\n            parsed_env = {}\n\n        # Parse OAuth config\n        parsed_oauth_config = None\n        if oauth_config:","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/mcp/mcp_routes.py#L162-L198","documentation":"The admin MCP server registration endpoint rejects transport=\"sse\" configurations that omit url. SSE servers connect to a remote HTTP endpoint that streams events, so the URL is the connection target and is mandatory before any connect attempt.","triggerScenarios":"POST with transport=\"sse\" and url empty/omitted; typo'd form field name (e.g. \"endpoint\" instead of \"url\"); JSON body sent to the Form()-based endpoint so url arrives as None.","commonSituations":"Switching a server entry from stdio to SSE and leaving the command field but no url; form-binding bug that drops the url input; copy-pasting a config where the field is named uri/endpoint.","solutions":["Include url=\"https://host.example/sse\" as a form field when transport=\"sse\".","Verify the field is literally named 'url' and sent as form data, not JSON.","If the server is local-stdio, use transport=\"stdio\" with a command instead."],"exampleFix":"# before\ndata = {\"transport\": \"sse\"}\n\n# after\ndata = {\"transport\": \"sse\", \"url\": \"https://mcp.example.com/sse\"}","handlingStrategy":"validation","validationCode":"def sse_config_valid(data: dict) -> bool:\n    return data.get(\"transport\") != \"sse\" or bool(data.get(\"url\"))","typeGuard":null,"tryCatchPattern":"Treat as a client bug: catch the 400, log the payload, fix the url field — no retry.","preventionTips":["Name the field exactly 'url'.","Validate url parses as https:// before submitting."],"tags":["mcp","validation","sse","url"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}