Skip to main content
Glama

MCP Orchestration Server

process-image.js7.38 kB
// Netlify function to process images without requiring Tesseract OCR const fetch = require('node-fetch'); const FormData = require('form-data'); const busboy = require('busboy'); const { Readable } = require('stream'); // The URL of the Render backend const RENDER_BACKEND_URL = process.env.RENDER_BACKEND_URL || 'https://blackhole-core-api.onrender.com'; // Parse multipart form data const parseMultipartForm = event => { return new Promise((resolve, reject) => { try { // Check if the body is base64 encoded let buffer; if (event.isBase64Encoded) { buffer = Buffer.from(event.body, 'base64'); } else { buffer = Buffer.from(event.body); } // Log the size of the request body console.log(`Request body size: ${buffer.length} bytes`); // Create a readable stream from the buffer const stream = Readable.from([buffer]); // Create a new FormData object const formData = new FormData(); // Get the content type and boundary const contentType = event.headers['content-type'] || event.headers['Content-Type'] || ''; console.log(`Content-Type: ${contentType}`); // Parse the multipart form data with higher limits const bb = busboy({ headers: { 'content-type': contentType }, limits: { fieldSize: 10 * 1024 * 1024, // 10MB fields: 10, fileSize: 50 * 1024 * 1024, // 50MB files: 5 } }); // Handle file fields bb.on('file', (name, file, info) => { const { filename, encoding, mimeType } = info; console.log(`Processing file: ${filename}, mimetype: ${mimeType}`); const chunks = []; let fileSize = 0; file.on('data', data => { chunks.push(data); fileSize += data.length; console.log(`Received ${data.length} bytes, total: ${fileSize} bytes`); }); file.on('end', () => { console.log(`File processing complete: ${filename}, total size: ${fileSize} bytes`); const fileBuffer = Buffer.concat(chunks); formData.append(name, fileBuffer, { filename, contentType: mimeType }); }); file.on('limit', () => { console.warn(`File size limit reached for ${filename}`); }); }); // Handle non-file fields bb.on('field', (name, value) => { console.log(`Processing field: ${name}=${value}`); formData.append(name, value); }); // Handle the end of parsing bb.on('finish', () => { console.log('Form data parsing complete'); resolve(formData); }); // Handle errors bb.on('error', err => { console.error('Error parsing form data:', err); reject(new Error(`Error parsing form data: ${err.message}`)); }); // Pipe the stream to busboy stream.pipe(bb); } catch (error) { console.error('Exception in parseMultipartForm:', error); reject(error); } }); }; exports.handler = async function(event, context) { // Handle OPTIONS requests for CORS if (event.httpMethod === 'OPTIONS') { return { statusCode: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type, Accept, Authorization', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', 'Access-Control-Max-Age': '86400', }, body: '', }; } // Only handle POST requests if (event.httpMethod !== 'POST') { return { statusCode: 405, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', }, body: JSON.stringify({ error: 'Method not allowed', message: 'Only POST requests are allowed' }), }; } try { // Parse the multipart form data console.log('Parsing multipart form data...'); const formData = await parseMultipartForm(event); console.log('Form data parsed successfully'); // Try to forward the request to the Render backend first try { console.log('Forwarding request to Render backend...'); // Set a timeout for the fetch request (10 seconds) const controller = new AbortController(); const timeout = setTimeout(() => { controller.abort(); }, 10000); try { // Make the request to the Render backend const response = await fetch(`${RENDER_BACKEND_URL}/api/process-image`, { method: 'POST', body: formData, signal: controller.signal }); // Clear the timeout clearTimeout(timeout); // If the request was successful, return the response if (response.ok) { console.log('Render backend processed the image successfully'); const responseBody = await response.text(); return { statusCode: response.status, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', }, body: responseBody, }; } // If the request failed, fall back to the mock implementation console.log('Render backend failed to process the image, falling back to mock implementation'); } catch (error) { // Clear the timeout clearTimeout(timeout); console.log('Error forwarding request to Render backend:', error.message); console.log('Falling back to mock implementation'); } } catch (error) { console.log('Error forwarding request to Render backend:', error.message); console.log('Falling back to mock implementation'); } // Mock implementation - return a response without requiring Tesseract OCR console.log('Using mock implementation for image processing'); // Create a mock response const mockResponse = { extracted_text: "Image text extraction is not available in this environment. Tesseract OCR is required for text extraction but is not installed on the server.", agent_result: { agent: "ArchiveSearchAgent", input: { document_text: "Image text extraction is not available in this environment." }, metadata: { source_file: "mock_response.txt", processed_at: new Date().toISOString() }, output: "This is a mock response. The image was received successfully, but text extraction is not available in this environment.", timestamp: new Date().toISOString() } }; return { statusCode: 200, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', }, body: JSON.stringify(mockResponse), }; } catch (error) { console.error('Error processing image:', error); return { statusCode: 500, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', }, body: JSON.stringify({ error: 'Error processing image', message: error.message, stack: error.stack }), }; } };

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/Nisarg-123-web/MCP2'

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