Skip to main content
Glama

ToolBox MCP Server

image_tool.ts7.2 kB
import sharp from 'sharp'; import fs from 'fs'; import path from 'path'; import { promisify } from 'util'; const stat = promisify(fs.stat); const readdir = promisify(fs.readdir); const mkdir = promisify(fs.mkdir); export const schema = { name: "image_tool", description: "A powerful image processing tool that supports format conversion, resizing, quality compression, and can batch process directories.", type: "object", properties: { sourcePath: { type: "string", description: "Source file or directory path" }, outputPath: { type: "string", description: "Output directory path. Defaults to a new file (e.g., 'source.processed.jpg') or a new directory (e.g., 'source_processed')." }, quality: { type: "number", description: "Compression quality for JPEG/WebP/AVIF/TIFF (1-100, defaults to 80)", minimum: 1, maximum: 100, default: 80 }, compressionLevel: { type: "number", description: "PNG compression level (0-9, defaults to 6)", minimum: 0, maximum: 9, default: 6 }, resize: { type: "object", description: "Resize options", properties: { width: { type: "number", description: "Width" }, height: { type: "number", description: "Height" } } }, format: { type: "string", enum: ["jpeg", "png", "webp", "avif", "tiff", "gif"], description: "Output format (optional, keeps original if not specified)" }, recursive: { type: "boolean", description: "Process subdirectories recursively", default: false }, backupDir: { type: "string", description: "Backup directory path (optional)" } }, required: ["sourcePath"] }; async function processImage(inputPath: string, outputPath: string, options: any) { let tempFilePath: string | null = null; try { const { quality, compressionLevel, resize, format, backupDir } = options; if (backupDir) { const backupPath = path.join(backupDir, path.basename(inputPath)); await mkdir(backupDir, { recursive: true }); fs.copyFileSync(inputPath, backupPath); } let sharpImage = sharp(inputPath); if (resize) { sharpImage = sharpImage.resize(resize.width, resize.height); } const targetFormat = format || path.extname(inputPath).substring(1); switch (targetFormat) { case 'jpeg': sharpImage = sharpImage.jpeg({ quality: quality || 80 }); break; case 'png': sharpImage = sharpImage.png({ compressionLevel: compressionLevel || 6 }); break; case 'webp': sharpImage = sharpImage.webp({ quality: quality || 80 }); break; case 'avif': sharpImage = sharpImage.avif({ quality: quality || 80 }); break; case 'tiff': sharpImage = sharpImage.tiff({ quality: quality || 80 }); break; case 'gif': sharpImage = sharpImage.gif(); break; } // Ensure output directory exists await mkdir(path.dirname(outputPath), { recursive: true }); if (inputPath === outputPath) { // This case should be avoided by the new default path logic, but kept for safety tempFilePath = path.join(path.dirname(inputPath), `temp_${path.basename(inputPath)}`); await sharpImage.toFile(tempFilePath); fs.renameSync(tempFilePath, inputPath); } else { await sharpImage.toFile(outputPath); } return { success: true, input: inputPath, output: outputPath }; } catch (error) { console.error(`Error processing image ${inputPath}:`, error); if (tempFilePath && fs.existsSync(tempFilePath)) { fs.unlinkSync(tempFilePath); } const errorMessage = error instanceof Error ? error.message : String(error); return { success: false, input: inputPath, error: errorMessage }; } } async function processDirectory(sourcePath: string, outputPath: string, options: any) { try { const { recursive, backupDir, format } = options; await mkdir(outputPath, { recursive: true }); const files = await readdir(sourcePath); const results = []; for (const file of files) { const filePath = path.join(sourcePath, file); const fileStat = await stat(filePath); if (fileStat.isFile() && /\.(jpg|jpeg|png|webp|avif|tiff|gif)$/i.test(file)) { const parsedPath = path.parse(file); const outputFileName = format ? `${parsedPath.name}.${format}` : file; const outputFilePath = path.join(outputPath, outputFileName); if (backupDir) { const backupPath = path.join(backupDir, file); await mkdir(path.dirname(backupPath), { recursive: true }); fs.copyFileSync(filePath, backupPath); } const result = await processImage(filePath, outputFilePath, options); results.push(result); } else if (fileStat.isDirectory() && recursive) { const subDirectoryName = path.basename(file); const subDirectoryOutputPath = path.join(outputPath, subDirectoryName); const subDirectoryResults = await processDirectory(filePath, subDirectoryOutputPath, options); results.push(...subDirectoryResults); } } return results; } catch (error) { console.error(`Error processing directory ${sourcePath}:`, error); const errorMessage = error instanceof Error ? error.message : String(error); return [{ success: false, input: sourcePath, error: errorMessage }]; } } export default async function (request: any) { try { const { sourcePath, outputPath: userOutputPath, quality, compressionLevel, resize, format, recursive, backupDir } = request.params.arguments; const sourcePathStat = await stat(sourcePath); let outputPath; if (userOutputPath) { outputPath = userOutputPath; } else { if (sourcePathStat.isFile()) { const parsedPath = path.parse(sourcePath); const outputFormat = format || parsedPath.ext.slice(1); outputPath = path.join(parsedPath.dir, `${parsedPath.name}.processed.${outputFormat}`); } else { // is directory outputPath = `${sourcePath}_processed`; } } const options = { quality, compressionLevel, resize, format, recursive, backupDir }; let results; if (sourcePathStat.isFile()) { results = [await processImage(sourcePath, outputPath, options)]; } else if (sourcePathStat.isDirectory()) { results = await processDirectory(sourcePath, outputPath, options); } else { throw new Error("Source path must be a file or directory"); } return { content: [ { type: "text", text: JSON.stringify(results, null, 2) } ] }; } catch (error) { return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` } ], isError: true }; } } export async function destroy() { console.log("Destroy image_tool"); }

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/xiaoguomeiyitian/ToolBox'

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