capture_screenshot_url
Capture web page screenshots in JPEG format for visual inspection, analysis, or demonstration. Specify URL and choose between single-screen or full-page capture.
Instructions
Capture high-quality screenshots of web pages in base64 encoded JPEG format. Use this tool when you need to visually inspect a website, take a snapshot for analysis, or show users what a webpage looks like.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | The complete HTTP/HTTPS URL of the webpage to capture (e.g., 'https://example.com') | |
| firstScreenOnly | No | Set to true for a single screen capture (faster), false for full page capture including content below the fold | |
| return_url | No | Set to true to return screenshot URLs instead of downloading images as base64 |
Implementation Reference
- src/tools/jina-tools.ts:116-178 (handler)Core handler function that fetches screenshot from r.jina.ai API using POST request, extracts image URL, optionally downloads and resizes it to base64 JPEG using downloadImages helper, returns image or URL.async ({ url, firstScreenOnly, return_url }: { url: string; firstScreenOnly: boolean; return_url: boolean }) => { try { const props = getProps(); const headers: Record<string, string> = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Return-Format': firstScreenOnly === true ? 'screenshot' : 'pageshot', }; // Add Authorization header if bearer token is available if (props.bearerToken) { headers['Authorization'] = `Bearer ${props.bearerToken}`; } const response = await fetch('https://r.jina.ai/', { method: 'POST', headers, body: JSON.stringify({ url }), }); if (!response.ok) { return handleApiError(response, "Screenshot capture"); } const data = await response.json() as any; // Get the screenshot URL from the response const imageUrl = data.data.screenshotUrl || data.data.pageshotUrl; if (!imageUrl) { throw new Error("No screenshot URL received from API"); } // Prepare response content - always return as list structure for consistency const contentItems: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> = []; if (return_url) { // Return the URL as text contentItems.push({ type: "text" as const, text: imageUrl, }); } else { // Download and process the image (resize to max 800px, convert to JPEG) const processedResults = await downloadImages(imageUrl, 1, 10000); const processedResult = processedResults[0]; if (!processedResult.success) { throw new Error(`Failed to process screenshot: ${processedResult.error}`); } contentItems.push({ type: "image" as const, data: processedResult.data!, mimeType: "image/jpeg", }); } return { content: contentItems, }; } catch (error) { return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`); }
- src/tools/jina-tools.ts:111-115 (schema)Zod input schema defining parameters for the tool: url (required URL), firstScreenOnly (boolean, default false for full page), return_url (boolean, default false for base64 image).{ url: z.string().url().describe("The complete HTTP/HTTPS URL of the webpage to capture (e.g., 'https://example.com')"), firstScreenOnly: z.boolean().default(false).describe("Set to true for a single screen capture (faster), false for full page capture including content below the fold"), return_url: z.boolean().default(false).describe("Set to true to return screenshot URLs instead of downloading images as base64") },
- src/tools/jina-tools.ts:107-181 (registration)Registers the capture_screenshot_url tool on the MCP server using server.tool() if enabled by tool filtering.if (isToolEnabled("capture_screenshot_url")) { server.tool( "capture_screenshot_url", "Capture high-quality screenshots of web pages in base64 encoded JPEG format. Use this tool when you need to visually inspect a website, take a snapshot for analysis, or show users what a webpage looks like.", { url: z.string().url().describe("The complete HTTP/HTTPS URL of the webpage to capture (e.g., 'https://example.com')"), firstScreenOnly: z.boolean().default(false).describe("Set to true for a single screen capture (faster), false for full page capture including content below the fold"), return_url: z.boolean().default(false).describe("Set to true to return screenshot URLs instead of downloading images as base64") }, async ({ url, firstScreenOnly, return_url }: { url: string; firstScreenOnly: boolean; return_url: boolean }) => { try { const props = getProps(); const headers: Record<string, string> = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Return-Format': firstScreenOnly === true ? 'screenshot' : 'pageshot', }; // Add Authorization header if bearer token is available if (props.bearerToken) { headers['Authorization'] = `Bearer ${props.bearerToken}`; } const response = await fetch('https://r.jina.ai/', { method: 'POST', headers, body: JSON.stringify({ url }), }); if (!response.ok) { return handleApiError(response, "Screenshot capture"); } const data = await response.json() as any; // Get the screenshot URL from the response const imageUrl = data.data.screenshotUrl || data.data.pageshotUrl; if (!imageUrl) { throw new Error("No screenshot URL received from API"); } // Prepare response content - always return as list structure for consistency const contentItems: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> = []; if (return_url) { // Return the URL as text contentItems.push({ type: "text" as const, text: imageUrl, }); } else { // Download and process the image (resize to max 800px, convert to JPEG) const processedResults = await downloadImages(imageUrl, 1, 10000); const processedResult = processedResults[0]; if (!processedResult.success) { throw new Error(`Failed to process screenshot: ${processedResult.error}`); } contentItems.push({ type: "image" as const, data: processedResult.data!, mimeType: "image/jpeg", }); } return { content: contentItems, }; } catch (error) { return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`); } }, ); }
- src/utils/image-downloader.ts:19-116 (helper)Helper utility called by the handler to download screenshot URL, resize using Cloudflare Workers image API (max 800px, JPEG 85% quality), encode to base64, with concurrency and timeout support.export async function downloadImages( urls: string | string[], concurrencyLimit: number = 3, timeoutMs: number = 15000 ): Promise<ProcessedImageResult[]> { // Normalize input to always be an array const urlArray = Array.isArray(urls) ? urls : [urls]; if (urlArray.length === 0) { return []; } const results: ProcessedImageResult[] = []; const queue = [...urlArray]; // Create a timeout promise const timeoutPromise = new Promise<ProcessedImageResult[]>((_, reject) => { setTimeout(() => reject(new Error('Download timeout')), timeoutMs); }); // Create the download promise const downloadPromise = (async () => { // Process images in batches while (queue.length > 0) { const batch = queue.splice(0, concurrencyLimit); const batchPromises = batch.map(async (url) => { try { // Skip SVG images as they can't be processed by Cloudflare image transformation if (url.toLowerCase().endsWith('.svg') || url.toLowerCase().includes('.svg?')) { return { url, success: false, mimeType: "image/jpeg", error: "SVG images are not supported for transformation" }; } // Use Cloudflare Workers image transformation // This automatically handles resizing and format conversion const response = await fetch(url, { cf: { image: { fit: 'scale-down', // Never enlarge, only shrink width: 800, // Max width height: 800, // Max height format: 'jpeg', // Convert to JPEG quality: 85, // Good quality with reasonable file size compression: 'fast' // Faster processing } } } as any); if (!response.ok) { return { url, success: false, mimeType: "image/jpeg", error: `HTTP ${response.status}: ${response.statusText}` }; } const arrayBuffer = await response.arrayBuffer(); const base64Image = Buffer.from(arrayBuffer).toString('base64'); return { url, success: true, data: base64Image, mimeType: "image/jpeg" }; } catch (error) { return { url, success: false, mimeType: "image/jpeg", error: error instanceof Error ? error.message : String(error) }; } }); const batchResults = await Promise.all(batchPromises); results.push(...batchResults); } return results; })(); // Race between download completion and timeout try { return await Promise.race([downloadPromise, timeoutPromise]); } catch (error) { if (error instanceof Error && error.message === 'Download timeout') { // Return what we have so far return results; } throw error; } }
- src/index.ts:100-102 (registration)Higher-level registration call to registerJinaTools which includes capture_screenshot_url among other tools, with tool filtering support.registerJinaTools(server, () => currentProps, enabledTools); return server;