| 176 | } |
| 177 | |
| 178 | func isAllowedDynamicClientRedirectURI(uri string) bool { |
| 179 | parsed, err := url.Parse(uri) |
| 180 | if err != nil { |
| 181 | return false |
| 182 | } |
| 183 | |
| 184 | switch parsed.Scheme { |
| 185 | case "http", "https": |
| 186 | return isLocalhostURI(uri) || isAllowedHostedRedirectURI(parsed) |
| 187 | case "cursor", "vscode", "vscode-insiders": |
| 188 | return true |
| 189 | case "jetbrains": |
| 190 | return parsed.Host == "gateway" && parsed.Path != "" && parsed.Path != "/" |
| 191 | default: |
| 192 | return false |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // isAllowedHostedRedirectURI whitelists the fixed, vendor-controlled OAuth |
| 197 | // callback URLs used by popular hosted MCP clients (web/desktop apps that |