{"record":{"id":"2c140240a56b61b1","repo":"HKUDS/DeepTutor","slug":"mcp-blocked-url","errorCode":"mcp.blocked_url","errorMessage":"error","messagePattern":"error","errorType":"error_code","errorClass":"UserMcpError","httpStatus":400,"severity":"error","filePath":"deeptutor/services/mcp/user_config.py","lineNumber":189,"sourceCode":"        raise UserMcpError(\"mcp.invalid_name\", f\"Invalid server name {name!r}\")\n    if name.startswith(_RESERVED_NAME_PREFIXES):\n        raise UserMcpError(\n            \"mcp.name_reserved\",\n            f\"{name!r} starts with a reserved tool-name prefix\",\n        )\n    transport = cfg.resolved_type()\n    if transport == \"stdio\" or cfg.command:\n        raise UserMcpError(\n            \"mcp.stdio_not_allowed\",\n            \"A server you configure yourself must be a remote URL: a stdio \"\n            \"server runs a command on the host and stays administrator-only.\",\n        )\n    if transport not in (\"sse\", \"streamableHttp\"):\n        raise UserMcpError(\"mcp.no_transport\", \"Provide an http(s) URL for the server\")\n    if validate_url:\n        ok, error = validate_mcp_url(cfg.url, strict=True)\n        if not ok:\n            raise UserMcpError(\"mcp.blocked_url\", error)\n\n\ndef _read_raw(owner_id: str) -> MCPConfig:\n    \"\"\"The file as stored, *without* the connectability filtering.\n\n    Writes must preserve entries this deployment refuses to connect (a stdio\n    entry left over from a hand edit), or saving one server would silently\n    delete another.\n    \"\"\"\n    path = user_mcp_path(owner_id)\n    if not path.exists():\n        return MCPConfig()\n    try:\n        return MCPConfig.model_validate(json.loads(path.read_text(encoding=\"utf-8\")))\n    except (OSError, json.JSONDecodeError, ValueError):\n        return MCPConfig()\n\n","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/mcp/user_config.py#L171-L207","documentation":"When validate_url=True, the candidate server URL is passed to validate_mcp_url(url, strict=True); if it fails (non-http(s) scheme, unreachable host, loopback/private address, or otherwise blocked), the returned error message is wrapped in a UserMcpError with code mcp.blocked_url.","triggerScenarios":"Calling _assert_self_service_allowed with validate_url=True (via load_user_mcp_config or save_user_server) where cfg.url is non-http(s), points at localhost/private networks, or violates the strict SSRF rules in validate_mcp_url.","commonSituations":"Pointing a user MCP server at a local development server (http://localhost:3000/sse), an internal IP, or leaving a placeholder URL; strict mode is enabled during interactive 'add server' flows.","solutions":["Use a publicly reachable https:// URL for the server","If you need a local server for development, run with validate_url disabled (admin/local mode) or expose it via a tunnel the validator permits","Check the exact reason inside the error message returned by validate_mcp_url — it names the specific rule that failed"],"exampleFix":"# before\nawait save_user_server(user_id, \"local\", McpServerConfig(url=\"http://localhost:8080/sse\", type=\"sse\"), validate_url=True)\n# after\nawait save_user_server(user_id, \"prod\", McpServerConfig(url=\"https://mcp.example.com/sse\", type=\"sse\"), validate_url=True)","handlingStrategy":"try-catch","validationCode":"from deeptutor.services.mcp import validate_mcp_url\n\nok, reason = validate_mcp_url(cfg.url, strict=True)\nif not ok:\n    show_error(reason)","typeGuard":null,"tryCatchPattern":"try:\n    await save_user_server(user_id, name, cfg, validate_url=True)\nexcept UserMcpError as e:\n    if e.code == \"mcp.blocked_url\":\n        show_error(f\"URL rejected: {e.message}\")\n    else:\n        raise","preventionTips":["Pre-validate URLs with validate_mcp_url before saving","Default to public https endpoints for user-configured servers"],"tags":["mcp","url-validation","ssrf","security"],"backgroundTag":"url-validation-failed","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}