UNPKG

ultimate-mcp-server

Version:

The definitive all-in-one Model Context Protocol server for AI-assisted coding across 30+ platforms

534 lines (524 loc) 18.1 kB
/** * Browser Automation MCP Tools * * Provides web scraping, automation, and interaction capabilities * using both Playwright and Puppeteer */ import { BrowserManager } from '../browser-automation/browser-manager.js'; const browserManager = new BrowserManager(); /** * Navigate to URL and capture screenshot */ export const capture_webpage_screenshot = { name: 'capture_webpage_screenshot', description: `Capture screenshots of web pages with various options. Features: - Full page screenshots - Viewport screenshots - Automatic tiling for large pages - Multiple browser engines (Chromium, Firefox, WebKit) - Headless or headed mode Use cases: - UI testing and verification - Visual documentation - Design reviews - Bug reporting`, inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to capture' }, fullPage: { type: 'boolean', description: 'Capture full scrollable page', default: true }, tileSize: { type: 'number', description: 'Split large screenshots into tiles (pixels)', default: 1072 }, viewport: { type: 'object', description: 'Viewport dimensions', properties: { width: { type: 'number', default: 1280 }, height: { type: 'number', default: 720 } } }, engine: { type: 'string', enum: ['playwright', 'puppeteer'], description: 'Browser automation engine', default: 'playwright' }, browserType: { type: 'string', enum: ['chromium', 'firefox', 'webkit'], description: 'Browser type (Playwright only)', default: 'chromium' } }, required: ['url'] }, handler: async (args) => { try { await browserManager.initialize({ engine: args.engine, browserType: args.browserType, headless: true }); const screenshots = await browserManager.captureScreenshot(args.url, { fullPage: args.fullPage, tileSize: args.fullPage ? args.tileSize : undefined, viewport: args.viewport }); // Convert buffers to base64 for transport const base64Screenshots = screenshots.map(buffer => buffer.toString('base64')); return { content: [{ type: 'text', text: JSON.stringify({ url: args.url, screenshotCount: screenshots.length, totalSize: screenshots.reduce((sum, buf) => sum + buf.length, 0), screenshots: base64Screenshots }, null, 2) }] }; } catch (error) { throw new Error(`Screenshot capture failed: ${error.message}`); } finally { await browserManager.close(); } } }; /** * Execute browser automation script */ export const execute_browser_automation = { name: 'execute_browser_automation', description: `Execute complex browser automation scripts. Supported actions: - click: Click on elements - type: Type text into inputs - wait: Wait for specified duration - screenshot: Capture screenshots - evaluate: Execute JavaScript Perfect for: - Form filling - UI testing - Web scraping - Automated workflows`, inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'Starting URL' }, actions: { type: 'array', description: 'List of actions to execute', items: { type: 'object', properties: { type: { type: 'string', enum: ['click', 'type', 'wait', 'screenshot', 'evaluate'] }, selector: { type: 'string', description: 'CSS selector (for click/type)' }, text: { type: 'string', description: 'Text to type' }, duration: { type: 'number', description: 'Wait duration in milliseconds' }, code: { type: 'string', description: 'JavaScript code to evaluate' } }, required: ['type'] } }, engine: { type: 'string', enum: ['playwright', 'puppeteer'], default: 'playwright' } }, required: ['url', 'actions'] }, handler: async (args) => { try { await browserManager.initialize({ engine: args.engine, headless: true }); const results = await browserManager.executeAutomation({ url: args.url, actions: args.actions }); return { content: [{ type: 'text', text: JSON.stringify({ url: args.url, actionsExecuted: args.actions.length, results: results.map(r => { if (r.data instanceof Buffer) { return { ...r, data: '[Binary Screenshot Data]' }; } return r; }) }, null, 2) }] }; } catch (error) { throw new Error(`Automation failed: ${error.message}`); } finally { await browserManager.close(); } } }; /** * Extract structured data from web pages */ export const extract_webpage_data = { name: 'extract_webpage_data', description: `Extract structured data from web pages using CSS selectors. Features: - Extract text content - Extract attributes (href, src, etc.) - Handle dynamic content - Multiple data points in one request Use cases: - Web scraping - Data collection - Content monitoring - Research automation`, inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to extract data from' }, selectors: { type: 'object', description: 'Key-value pairs of data to extract', additionalProperties: { type: 'string', description: 'CSS selector' }, examples: [ { title: 'h1', price: '.price-tag', description: 'meta[name="description"]' } ] }, waitForSelector: { type: 'string', description: 'Wait for this selector before extracting', default: 'body' }, engine: { type: 'string', enum: ['playwright', 'puppeteer'], default: 'playwright' } }, required: ['url', 'selectors'] }, handler: async (args) => { try { await browserManager.initialize({ engine: args.engine, headless: true }); const data = await browserManager.extractData(args.url, args.selectors); return { content: [{ type: 'text', text: JSON.stringify({ url: args.url, extractedData: data, timestamp: new Date().toISOString() }, null, 2) }] }; } catch (error) { throw new Error(`Data extraction failed: ${error.message}`); } finally { await browserManager.close(); } } }; /** * Analyze webpage performance */ export const analyze_webpage_performance = { name: 'analyze_webpage_performance', description: `Analyze webpage loading performance and metrics. Metrics collected: - Page load time - DOM content loaded time - Resource timing - JavaScript execution time - Memory usage - Network requests Useful for: - Performance optimization - Debugging slow pages - Monitoring web vitals`, inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to analyze' }, throttling: { type: 'string', enum: ['none', '3G', '4G'], description: 'Network throttling', default: 'none' }, device: { type: 'string', enum: ['desktop', 'mobile', 'tablet'], description: 'Device emulation', default: 'desktop' } }, required: ['url'] }, handler: async (args) => { try { await browserManager.initialize({ headless: true, viewport: args.device === 'mobile' ? { width: 375, height: 812 } : args.device === 'tablet' ? { width: 768, height: 1024 } : { width: 1920, height: 1080 } }); const page = await browserManager.newPage(); // Start performance measurement const startTime = Date.now(); const performanceData = {}; // Navigate and collect metrics await page.goto(args.url); performanceData.loadTime = Date.now() - startTime; // Collect performance metrics performanceData.metrics = await page.evaluate(() => { const navigation = performance.getEntriesByType('navigation')[0]; const resources = performance.getEntriesByType('resource'); return { domContentLoaded: navigation?.domContentLoadedEventEnd - navigation?.domContentLoadedEventStart, loadComplete: navigation?.loadEventEnd - navigation?.loadEventStart, resourceCount: resources.length, totalResourceSize: resources.reduce((sum, r) => sum + (r.transferSize || 0), 0), jsHeapSize: performance.memory?.usedJSHeapSize, documentSize: document.documentElement.innerHTML.length }; }); await page.close(); return { content: [{ type: 'text', text: JSON.stringify({ url: args.url, performance: performanceData, recommendations: generatePerformanceRecommendations(performanceData) }, null, 2) }] }; } catch (error) { throw new Error(`Performance analysis failed: ${error.message}`); } finally { await browserManager.close(); } } }; /** * Test webpage accessibility */ export const test_webpage_accessibility = { name: 'test_webpage_accessibility', description: `Test webpage accessibility using automated checks. Checks include: - Alt text for images - Proper heading structure - Color contrast - ARIA labels - Keyboard navigation - Form labels Standards: - WCAG 2.1 AA compliance - Section 508 - ADA compliance`, inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL to test' }, standard: { type: 'string', enum: ['WCAG2A', 'WCAG2AA', 'WCAG2AAA', 'Section508'], description: 'Accessibility standard', default: 'WCAG2AA' } }, required: ['url'] }, handler: async (args) => { try { await browserManager.initialize({ headless: true }); const page = await browserManager.newPage(); await page.goto(args.url); // Run accessibility checks const accessibilityResults = await page.evaluate(() => { const results = { images: { total: 0, withAlt: 0, missing: [] }, headings: { proper: true, issues: [] }, forms: { labeled: 0, unlabeled: [] }, contrast: { issues: [] }, aria: { proper: 0, issues: [] } }; // Check images const images = document.querySelectorAll('img'); results.images.total = images.length; images.forEach((img, i) => { if (img.alt) { results.images.withAlt++; } else { results.images.missing.push(`Image ${i + 1}: ${img.src}`); } }); // Check heading structure const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6'); let lastLevel = 0; headings.forEach(h => { const level = parseInt(h.tagName[1]); if (level > lastLevel + 1) { results.headings.proper = false; results.headings.issues.push(`Skipped heading level: ${h.tagName} after H${lastLevel}`); } lastLevel = level; }); // Check form labels const inputs = document.querySelectorAll('input, select, textarea'); inputs.forEach((input, i) => { const label = document.querySelector(`label[for="${input.id}"]`) || input.closest('label'); if (label || input.getAttribute('aria-label')) { results.forms.labeled++; } else { results.forms.unlabeled.push(`Input ${i + 1}: ${input.type || 'unknown'}`); } }); return results; }); await page.close(); // Generate accessibility score const score = calculateAccessibilityScore(accessibilityResults); return { content: [{ type: 'text', text: JSON.stringify({ url: args.url, standard: args.standard, score: `${score}%`, results: accessibilityResults, recommendations: generateAccessibilityRecommendations(accessibilityResults) }, null, 2) }] }; } catch (error) { throw new Error(`Accessibility test failed: ${error.message}`); } finally { await browserManager.close(); } } }; /** * Helper functions */ function generatePerformanceRecommendations(data) { const recommendations = []; if (data.loadTime > 3000) { recommendations.push('Page load time exceeds 3 seconds - consider optimization'); } if (data.metrics.resourceCount > 100) { recommendations.push('High number of resources - consider bundling and lazy loading'); } if (data.metrics.totalResourceSize > 5 * 1024 * 1024) { recommendations.push('Large total resource size - optimize images and assets'); } if (data.metrics.jsHeapSize > 50 * 1024 * 1024) { recommendations.push('High JavaScript memory usage - check for memory leaks'); } return recommendations; } function calculateAccessibilityScore(results) { let score = 100; // Deduct points for issues if (results.images.total > 0) { const imageScore = (results.images.withAlt / results.images.total) * 20; score -= (20 - imageScore); } if (!results.headings.proper) { score -= 15; } if (results.forms.unlabeled.length > 0) { score -= results.forms.unlabeled.length * 5; } return Math.max(0, Math.round(score)); } function generateAccessibilityRecommendations(results) { const recommendations = []; if (results.images.missing.length > 0) { recommendations.push(`Add alt text to ${results.images.missing.length} images`); } if (!results.headings.proper) { recommendations.push('Fix heading hierarchy - avoid skipping levels'); } if (results.forms.unlabeled.length > 0) { recommendations.push(`Add labels to ${results.forms.unlabeled.length} form inputs`); } return recommendations; } // Export all browser automation tools export const browserAutomationTools = [ capture_webpage_screenshot, execute_browser_automation, extract_webpage_data, analyze_webpage_performance, test_webpage_accessibility ]; //# sourceMappingURL=browser-automation-tools.js.map