officialRegistry.ts
1 import axios from 'axios' 2 import { logForDebugging } from '../../utils/debug.js' 3 import { errorMessage } from '../../utils/errors.js' 4 5 type RegistryServer = { 6 server: { 7 remotes?: Array<{ url: string }> 8 } 9 } 10 11 type RegistryResponse = { 12 servers: RegistryServer[] 13 } 14 15 // URLs stripped of query string and trailing slash — matches the normalization 16 // done by getLoggingSafeMcpBaseUrl so direct Set.has() lookup works. 17 let officialUrls: Set<string> | undefined = undefined 18 19 function normalizeUrl(url: string): string | undefined { 20 try { 21 const u = new URL(url) 22 u.search = '' 23 return u.toString().replace(/\/$/, '') 24 } catch { 25 return undefined 26 } 27 } 28 29 /** 30 * Fire-and-forget fetch of the official MCP registry. 31 * Populates officialUrls for isOfficialMcpUrl lookups. 32 */ 33 export async function prefetchOfficialMcpUrls(): Promise<void> { 34 if (process.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC) { 35 return 36 } 37 38 try { 39 const response = await axios.get<RegistryResponse>( 40 'https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial', 41 { timeout: 5000 }, 42 ) 43 44 const urls = new Set<string>() 45 for (const entry of response.data.servers) { 46 for (const remote of entry.server.remotes ?? []) { 47 const normalized = normalizeUrl(remote.url) 48 if (normalized) { 49 urls.add(normalized) 50 } 51 } 52 } 53 officialUrls = urls 54 logForDebugging(`[mcp-registry] Loaded ${urls.size} official MCP URLs`) 55 } catch (error) { 56 logForDebugging(`Failed to fetch MCP registry: ${errorMessage(error)}`, { 57 level: 'error', 58 }) 59 } 60 } 61 62 /** 63 * Returns true iff the given (already-normalized via getLoggingSafeMcpBaseUrl) 64 * URL is in the official MCP registry. Undefined registry → false (fail-closed). 65 */ 66 export function isOfficialMcpUrl(normalizedUrl: string): boolean { 67 return officialUrls?.has(normalizedUrl) ?? false 68 } 69 70 export function resetOfficialMcpUrlsForTesting(): void { 71 officialUrls = undefined 72 }