Skip to main content
Glama
dns-lookup.ts•4.83 kB
/** * DNS-based SCP endpoint discovery */ import { Resolver } from 'dns/promises'; import type { SCPCapabilities } from '../types.js'; import { getCachedEndpoint, cacheEndpoint } from "../storage/database-sqljs.js"; import { checkTestEndpoint, loadConfig } from '../config.js'; const DNS_QUERY_PREFIX = '_scp._tcp.'; /** * Discover SCP endpoint for a domain */ export async function discoverSCPEndpoint(domain: string): Promise<string | null> { // Check for test endpoint override first const testEndpoint = checkTestEndpoint(domain); if (testEndpoint) { return testEndpoint; } // Check for demo mode const config = loadConfig(); if (config.demo_mode) { const demoEndpoint = config.demo_endpoint || 'https://demo.shoppercontextprotocol.io/v1'; console.error(`[SCP] Demo mode enabled - using ${demoEndpoint} for all domains`); return demoEndpoint; } // Check cache first const cached = await getCachedEndpoint(domain); if (cached) { return cached.endpoint; } // Try DNS TXT record const dnsEndpoint = await tryDNSDiscovery(domain); if (dnsEndpoint) { // Cache the result await cacheEndpoint({ domain, endpoint: dnsEndpoint, capabilities: null, discovered_at: Date.now(), ttl: 86400 // 24 hours }); return dnsEndpoint; } // Try fallback methods const fallbackEndpoint = await tryFallbackDiscovery(domain); if (fallbackEndpoint) { // Cache the result await cacheEndpoint({ domain, endpoint: fallbackEndpoint, capabilities: null, discovered_at: Date.now(), ttl: 86400 // 24 hours }); return fallbackEndpoint; } return null; } /** * Try DNS TXT record discovery */ async function tryDNSDiscovery(domain: string): Promise<string | null> { const resolver = new Resolver(); const query = `${DNS_QUERY_PREFIX}${domain}`; try { const records = await resolver.resolveTxt(query); for (const record of records) { // TXT records are returned as array of strings, join them const txt = record.join(''); // Check if this is a SCP record if (txt.startsWith('v=scp1')) { // Extract endpoint URL const match = txt.match(/endpoint=(https:\/\/[^\s]+)/); if (match) { return match[1]; } } } return null; } catch (error: any) { // DNS lookup failed (NXDOMAIN, NODATA, etc.) if (error.code === 'ENOTFOUND' || error.code === 'ENODATA') { return null; } throw error; } } /** * Try fallback discovery methods */ async function tryFallbackDiscovery(domain: string): Promise<string | null> { // Try .well-known URI const wellKnownUrl = `https://${domain}/.well-known/customer-context-protocol`; try { const response = await fetch(wellKnownUrl, { method: 'GET', headers: { 'Accept': 'application/json' }, signal: AbortSignal.timeout(5000) }); if (response.ok) { const data = await response.json() as any; if (data.endpoint && typeof data.endpoint === 'string') { return data.endpoint; } } } catch (error) { // .well-known failed, continue to next method } // Try HTTP header try { const response = await fetch(`https://${domain}/`, { method: 'HEAD', signal: AbortSignal.timeout(5000) }); const scpEndpoint = response.headers.get('SCP-Endpoint'); if (scpEndpoint) { return scpEndpoint; } } catch (error) { // Header check failed } return null; } /** * Fetch capabilities from SCP endpoint */ export async function fetchCapabilities(endpoint: string): Promise<SCPCapabilities> { const capabilitiesUrl = `${endpoint}/capabilities`; const response = await fetch(capabilitiesUrl, { method: 'GET', headers: { 'Accept': 'application/json' }, signal: AbortSignal.timeout(10000) }); if (!response.ok) { throw new Error(`Failed to fetch capabilities: ${response.status} ${response.statusText}`); } const capabilities = await response.json() as SCPCapabilities; return capabilities; } /** * Discover endpoint and fetch capabilities */ export async function discoverWithCapabilities(domain: string): Promise<{ endpoint: string; capabilities: SCPCapabilities; } | null> { const endpoint = await discoverSCPEndpoint(domain); if (!endpoint) { return null; } try { const capabilities = await fetchCapabilities(endpoint); // Update cache with capabilities await cacheEndpoint({ domain, endpoint, capabilities, discovered_at: Date.now(), ttl: 86400 }); return { endpoint, capabilities }; } catch (error) { // Capabilities fetch failed, but we have endpoint return { endpoint, capabilities: null as any }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/shopper-context-protocol/scp-mcp-wrapper'

If you have feedback or need assistance with the MCP directory API, please join our Discord server