Web Content MCP Server

by amotivv
Verified
import puppeteer from '@cloudflare/puppeteer'; /** * Cloudflare Worker with Browser Rendering binding * * This worker demonstrates how to use the Browser Rendering binding * with the @cloudflare/puppeteer package. */ // Define the allowed origins for CORS const ALLOWED_ORIGINS = [ 'https://example.com', 'http://localhost:3000', ]; /** * Handle CORS preflight requests */ function handleOptions(request) { const origin = request.headers.get('Origin'); const isAllowedOrigin = ALLOWED_ORIGINS.includes(origin); return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': isAllowedOrigin ? origin : ALLOWED_ORIGINS[0], 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 'Access-Control-Max-Age': '86400', }, }); } /** * Add CORS headers to a response */ function addCorsHeaders(response, request) { const origin = request.headers.get('Origin'); const isAllowedOrigin = ALLOWED_ORIGINS.includes(origin); const headers = new Headers(response.headers); headers.set('Access-Control-Allow-Origin', isAllowedOrigin ? origin : ALLOWED_ORIGINS[0]); return new Response(response.body, { status: response.status, statusText: response.statusText, headers, }); } /** * Handle the /content endpoint */ async function handleContent(request, env) { try { // Parse the request body const body = await request.json(); if (!body.url) { return new Response(JSON.stringify({ error: 'URL is required' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Launch a browser using the binding const browser = await puppeteer.launch(env.browser); try { // Create a new page const page = await browser.newPage(); // Set viewport size await page.setViewport({ width: 1280, height: 800, }); // Set request rejection patterns if provided if (body.rejectResourceTypes && Array.isArray(body.rejectResourceTypes)) { await page.setRequestInterception(true); page.on('request', (req) => { if (body.rejectResourceTypes.includes(req.resourceType())) { req.abort(); } else { req.continue(); } }); } // Navigate to the URL await page.goto(body.url, { waitUntil: body.waitUntil || 'networkidle0', timeout: body.timeout || 30000, }); // Get the page content const content = await page.content(); // Return the content return new Response(JSON.stringify({ content }), { headers: { 'Content-Type': 'application/json' }, }); } finally { // Always close the browser to avoid resource leaks await browser.close(); } } catch (error) { return new Response(JSON.stringify({ error: error.message, stack: error.stack }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } } /** * Handle the /screenshot endpoint */ async function handleScreenshot(request, env) { try { // Parse the request body const body = await request.json(); if (!body.url) { return new Response(JSON.stringify({ error: 'URL is required' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }); } // Launch a browser using the binding const browser = await puppeteer.launch(env.browser); try { // Create a new page const page = await browser.newPage(); // Set viewport size await page.setViewport({ width: body.width || 1280, height: body.height || 800, }); // Navigate to the URL await page.goto(body.url, { waitUntil: body.waitUntil || 'networkidle0', timeout: body.timeout || 30000, }); // Take a screenshot const screenshot = await page.screenshot({ fullPage: body.fullPage || false, type: 'png', encoding: 'base64', }); // Return the screenshot return new Response(JSON.stringify({ screenshot }), { headers: { 'Content-Type': 'application/json' }, }); } finally { // Always close the browser to avoid resource leaks await browser.close(); } } catch (error) { return new Response(JSON.stringify({ error: error.message, stack: error.stack }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } } /** * Main worker handler */ export default { async fetch(request, env, ctx) { // Handle CORS preflight requests if (request.method === 'OPTIONS') { return handleOptions(request); } // Get the URL pathname const url = new URL(request.url); const path = url.pathname.toLowerCase(); // Route the request to the appropriate handler let response; try { if (path === '/content' && request.method === 'POST') { response = await handleContent(request, env); } else if (path === '/screenshot' && request.method === 'POST') { response = await handleScreenshot(request, env); } else if (path === '/info') { // Return information about the environment response = new Response(JSON.stringify({ env: Object.keys(env), hasBrowser: 'browser' in env, puppeteer: { version: puppeteer.version, }, }, null, 2), { headers: { 'Content-Type': 'application/json' }, }); } else { response = new Response(JSON.stringify({ error: 'Endpoint not found', availableEndpoints: ['/content', '/screenshot', '/info'], method: request.method, }), { status: 404, headers: { 'Content-Type': 'application/json' }, }); } } catch (error) { response = new Response(JSON.stringify({ error: error.message, stack: error.stack, }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } // Add CORS headers to the response return addCorsHeaders(response, request); }, };