UNPKG

rapidapi-mcp-server

Version:

MCP server for discovering and assessing APIs from RapidAPI marketplace

230 lines (229 loc) 10.4 kB
export class SearchResultsExtractor { async extractSearchResults(page, maxResults = 20) { try { // Use the actual selectors found on the page await page.waitForSelector('div[class*="group/card"], div[class*="card"], div[class*="api"]', { timeout: 10000 }); } catch (error) { console.error('No search results found or page structure changed'); return []; } const results = await page.evaluate((max) => { const searchResults = []; // Use the actual selectors from RapidAPI search page const cardSelectors = [ 'div[class*="group/card"]', // Main card selector found 'div[class*="card"]', // General card selector 'div[class*="api"]' // API-specific divs ]; let apiCards = []; for (const selector of cardSelectors) { apiCards = Array.from(document.querySelectorAll(selector)); if (apiCards.length > 0) break; } for (let i = 0; i < Math.min(apiCards.length, max); i++) { const card = apiCards[i]; try { const result = { name: '', description: '', provider: '', url: '' }; // Extract API name const nameEl = card.querySelector('h3, h4, [class*="title"], [class*="name"], a[href*="/api/"]'); if (nameEl) { result.name = nameEl.textContent?.trim() || ''; } // Extract description const descEl = card.querySelector('p, [class*="description"], [class*="summary"]'); if (descEl) { result.description = descEl.textContent?.trim() || ''; } // Extract provider/author const providerEl = card.querySelector('[class*="provider"], [class*="author"], [class*="by"]'); if (providerEl) { result.provider = providerEl.textContent?.replace(/^by\s+/i, '').trim() || ''; } // Extract URL const linkEl = card.querySelector('a[href*="/api/"], a[href*="rapidapi.com"]'); if (linkEl) { result.url = linkEl.href; } // Extract rating if available const ratingEl = card.querySelector('[class*="rating"], [class*="score"], [title*="star"]'); if (ratingEl) { const ratingText = ratingEl.textContent?.trim(); const ratingMatch = ratingText?.match(/(\d+(?:\.\d+)?)/); if (ratingMatch) { result.rating = parseFloat(ratingMatch[1]); } } // Only add if we have essential information if (result.name && result.url) { searchResults.push(result); } } catch (error) { console.error('Error extracting individual search result:', error); } } return searchResults; }, maxResults); return results; } } export class APIDetailExtractor { async extractAPIDetails(page) { // Wait for page content to load await page.waitForSelector('h1, [class*="title"]', { timeout: 10000 }); const apiDetails = await page.evaluate(() => { const assessment = { pricing: {}, endpoints: [] }; // Extract API name const nameEl = document.querySelector('h1, [class*="api-title"], [class*="title"]'); if (nameEl) { assessment.name = nameEl.textContent?.trim() || ''; } // Extract description const descriptionSelectors = [ '[class*="description"]', '[class*="overview"]', 'p:not([class*="metric"]):not([class*="stat"])' ]; for (const selector of descriptionSelectors) { const descEl = document.querySelector(selector); if (descEl && descEl.textContent && descEl.textContent.length > 50) { assessment.description = descEl.textContent.trim(); break; } } // Extract provider const providerEl = document.querySelector('[class*="provider"], [class*="author"], [href*="provider"]'); if (providerEl) { assessment.provider = providerEl.textContent?.replace(/^by\s+/i, '').trim() || ''; } // Extract current URL assessment.url = window.location.href; // Extract rating const ratingEl = document.querySelector('[class*="rating"], [title*="star"]'); if (ratingEl) { const ratingText = ratingEl.textContent?.trim(); const ratingMatch = ratingText?.match(/(\d+(?:\.\d+)?)/); if (ratingMatch) { assessment.rating = parseFloat(ratingMatch[1]); } } // Extract metrics (popularity, service level, latency) const metricSelectors = [ '[class*="popularity"]', '[class*="service"]', '[class*="latency"]', '[title*="Popularity"]', '[title*="Service"]', '[title*="Latency"]' ]; metricSelectors.forEach(selector => { const metricEl = document.querySelector(selector); if (metricEl) { const text = metricEl.textContent?.trim(); if (text) { if (selector.includes('popularity') || selector.includes('Popularity')) { assessment.popularity = text; } else if (selector.includes('service') || selector.includes('Service')) { assessment.serviceLevel = text; } else if (selector.includes('latency') || selector.includes('Latency')) { assessment.latency = text; } } } }); return assessment; }); // Extract pricing information apiDetails.pricing = await this.extractPricing(page); // Extract endpoints apiDetails.endpoints = await this.extractEndpoints(page); return apiDetails; } async extractPricing(page) { return await page.evaluate(() => { const pricing = {}; // Look for pricing sections const pricingSelectors = [ '[class*="pricing"]', '[class*="plan"]', '[class*="tier"]' ]; pricingSelectors.forEach(selector => { const pricingElements = document.querySelectorAll(selector); pricingElements.forEach(element => { const tierName = element.querySelector('[class*="name"], h3, h4')?.textContent?.trim().toLowerCase(); const priceEl = element.querySelector('[class*="price"], [class*="cost"]'); if (tierName && priceEl) { const price = priceEl.textContent?.trim() || ''; pricing[tierName] = { name: tierName, price: price }; } }); }); // Look for specific tier information displayed on page const basicEl = document.querySelector('[class*="BASIC"]'); const megaEl = document.querySelector('[class*="MEGA"]'); const proEl = document.querySelector('[class*="PRO"]'); if (basicEl) { const price = basicEl.closest('[class*="pricing"]')?.querySelector('[class*="price"]')?.textContent?.trim() || '$0.00'; pricing.basic = { name: 'Basic', price }; } if (megaEl) { const price = megaEl.closest('[class*="pricing"]')?.querySelector('[class*="price"]')?.textContent?.trim() || 'Unknown'; pricing.pro = { name: 'Pro', price }; } return pricing; }); } async extractEndpoints(page) { return await page.evaluate(() => { const endpoints = []; // Look for endpoints in sidebar or documentation const endpointSelectors = [ '[class*="endpoint"]', '[class*="method"]', 'nav a[href*="endpoint"]', 'aside a', '[class*="sidebar"] a' ]; endpointSelectors.forEach(selector => { const endpointElements = document.querySelectorAll(selector); endpointElements.forEach(element => { const text = element.textContent?.trim(); if (text && text.includes('API') && !text.includes('Overview')) { // Extract HTTP method if present const methodMatch = text.match(/^(GET|POST|PUT|DELETE|PATCH)\s*(.+)/i); endpoints.push({ name: methodMatch ? methodMatch[2] : text, method: methodMatch ? methodMatch[1].toUpperCase() : 'GET', description: text }); } }); }); // Remove duplicates const seen = new Set(); return endpoints.filter(endpoint => { const key = `${endpoint.method}:${endpoint.name}`; if (seen.has(key)) return false; seen.add(key); return true; }); }); } }