Skip to main content
Glama
enhanced_filesystem_tools.js4.41 kB
const path = require('path'); const fs = require('fs'); const fetch = require('node-fetch'); const logger = require('../logger'); // Try to load sharp, but don't fail if it's not available let sharp = null; try { sharp = require('sharp'); } catch (e) { logger.warn('Sharp module not available - screenshot features will be disabled'); } /** * Enhanced readFile that supports both local files and URLs */ async function readFileEnhanced(filePath, allowedDirs, options = {}) { try { // Check if it's a URL if (options.isUrl || (typeof filePath === 'string' && (filePath.startsWith('http://') || filePath.startsWith('https://')))) { return await readFileFromUrl(filePath, options); } // Otherwise, use the original readFile from filesystem_tools const filesystemTools = require('../filesystem_tools'); return filesystemTools.readFile(filePath, allowedDirs); } catch (error) { logger.error(`Error in readFileEnhanced: ${error.message}`); return { success: false, message: error.message }; } } /** * Read file content from a URL */ async function readFileFromUrl(url, options = {}) { try { // Validate URL let validUrl; try { validUrl = new URL(url); } catch (error) { return { success: false, message: `Invalid URL: ${url}` }; } // Set timeout (default 30 seconds) const timeout = options.timeout || 30000; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { // Fetch the content const response = await fetch(url, { signal: controller.signal, headers: { 'User-Agent': 'ACF-MCP/1.0' } }); clearTimeout(timeoutId); if (!response.ok) { return { success: false, message: `HTTP error ${response.status}: ${response.statusText}` }; } // Get content type const contentType = response.headers.get('content-type') || 'text/plain'; const isText = contentType.startsWith('text/') || contentType.includes('json') || contentType.includes('xml') || contentType.includes('javascript'); // Handle different content types if (contentType.startsWith('image/')) { // Handle images const buffer = await response.buffer(); // Convert to a format that can be displayed let processedBuffer = buffer; let processedType = contentType; // If it's a format that might not be supported, convert to JPEG if (!['image/jpeg', 'image/png', 'image/gif', 'image/webp'].includes(contentType.split(';')[0])) { try { processedBuffer = await sharp(buffer).jpeg().toBuffer(); processedType = 'image/jpeg'; } catch (sharpError) { logger.warn(`Could not process image with sharp: ${sharpError.message}`); } } return { success: true, content: processedBuffer.toString('base64'), mimeType: processedType, isText: false, isUrl: true, url: url, size: processedBuffer.length }; } else if (isText) { // Handle text content const text = await response.text(); return { success: true, content: text, mimeType: contentType, isText: true, isUrl: true, url: url, size: text.length }; } else { // Handle binary content const buffer = await response.buffer(); return { success: true, content: buffer.toString('base64'), mimeType: contentType, isText: false, isUrl: true, url: url, size: buffer.length }; } } catch (fetchError) { if (fetchError.name === 'AbortError') { return { success: false, message: `Request timeout after ${timeout}ms` }; } throw fetchError; } } catch (error) { logger.error(`Error reading from URL: ${error.message}`); return { success: false, message: `Failed to read from URL: ${error.message}` }; } } module.exports = { readFileEnhanced };

Latest Blog Posts

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/FutureAtoms/agentic-control-framework'

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