add-watermark
Apply a watermark to images with customizable position and opacity using the 'add-watermark' tool in the MCP Media Processing Server.
Instructions
Add watermark to image
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| inputPath | Yes | Absolute path to input image file | |
| opacity | No | Watermark opacity (0-100) | |
| outputFilename | No | Output filename (only used if outputPath is not provided) | |
| outputPath | No | Optional absolute path for output file. If not provided, file will be saved in Downloads folder | |
| position | No | Position of watermark | southeast |
| watermarkPath | Yes | Absolute path to watermark image file |
Implementation Reference
- src/index.ts:475-510 (handler)The handler function that adds a watermark to an image using ImageMagick. It resolves absolute paths, normalizes opacity, constructs and executes the composite command with specified gravity position, and handles output path generation.async ({ inputPath, watermarkPath, position, opacity, outputPath, outputFilename }) => { try { await checkImageMagick(); const absoluteInputPath = await getAbsolutePath(inputPath); const absoluteWatermarkPath = await getAbsolutePath(watermarkPath); const inputFileName = absoluteInputPath.split('/').pop()?.split('.')[0] || 'output'; const extension = absoluteInputPath.split('.').pop() || 'png'; const defaultFilename = outputFilename || `${inputFileName}_watermarked.${extension}`; const finalOutputPath = await getOutputPath(outputPath, defaultFilename); // Convert opacity from 0-100 to 0-1 for ImageMagick const normalizedOpacity = opacity / 100; const command = `convert "${absoluteInputPath}" \\( "${absoluteWatermarkPath}" -alpha set -channel A -evaluate multiply ${normalizedOpacity} \\) -gravity ${position} -composite "${finalOutputPath}"`; await execSync(command); return { content: [ { type: "text", text: `Watermark successfully added and saved to: ${finalOutputPath}`, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error adding watermark: ${errorMessage}`, }, ], }; } }
- src/index.ts:467-474 (schema)Zod input schema for the add-watermark tool defining parameters like inputPath, watermarkPath, position (ImageMagick gravity enum), opacity, and optional output paths.{ inputPath: z.string().describe("Absolute path to input image file"), watermarkPath: z.string().describe("Absolute path to watermark image file"), position: z.enum(['northwest', 'north', 'northeast', 'west', 'center', 'east', 'southwest', 'south', 'southeast']).default('southeast').describe("Position of watermark"), opacity: z.number().min(0).max(100).default(50).describe("Watermark opacity (0-100)"), outputPath: z.string().optional().describe("Optional absolute path for output file. If not provided, file will be saved in Downloads folder"), outputFilename: z.string().optional().describe("Output filename (only used if outputPath is not provided)") },
- src/index.ts:464-511 (registration)The server.tool registration call for the add-watermark tool, including name, description, input schema, and inline handler function.server.tool( "add-watermark", "Add watermark to image", { inputPath: z.string().describe("Absolute path to input image file"), watermarkPath: z.string().describe("Absolute path to watermark image file"), position: z.enum(['northwest', 'north', 'northeast', 'west', 'center', 'east', 'southwest', 'south', 'southeast']).default('southeast').describe("Position of watermark"), opacity: z.number().min(0).max(100).default(50).describe("Watermark opacity (0-100)"), outputPath: z.string().optional().describe("Optional absolute path for output file. If not provided, file will be saved in Downloads folder"), outputFilename: z.string().optional().describe("Output filename (only used if outputPath is not provided)") }, async ({ inputPath, watermarkPath, position, opacity, outputPath, outputFilename }) => { try { await checkImageMagick(); const absoluteInputPath = await getAbsolutePath(inputPath); const absoluteWatermarkPath = await getAbsolutePath(watermarkPath); const inputFileName = absoluteInputPath.split('/').pop()?.split('.')[0] || 'output'; const extension = absoluteInputPath.split('.').pop() || 'png'; const defaultFilename = outputFilename || `${inputFileName}_watermarked.${extension}`; const finalOutputPath = await getOutputPath(outputPath, defaultFilename); // Convert opacity from 0-100 to 0-1 for ImageMagick const normalizedOpacity = opacity / 100; const command = `convert "${absoluteInputPath}" \\( "${absoluteWatermarkPath}" -alpha set -channel A -evaluate multiply ${normalizedOpacity} \\) -gravity ${position} -composite "${finalOutputPath}"`; await execSync(command); return { content: [ { type: "text", text: `Watermark successfully added and saved to: ${finalOutputPath}`, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: `Error adding watermark: ${errorMessage}`, }, ], }; } } );
- src/index.ts:30-44 (helper)Helper function to resolve input paths to absolute paths, checking existence; used for both input and watermark paths.async function getAbsolutePath(inputPath: string): Promise<string> { if (isAbsolute(inputPath)) { return inputPath; } // FIXME: But it's not working, because the server is running in a different directory const absolutePath = resolve(process.cwd(), inputPath); try { await fs.access(absolutePath); return absolutePath; } catch (error) { throw new Error(`Input file not found: ${inputPath}`); } }
- src/index.ts:69-76 (helper)Helper function to verify ImageMagick installation by running 'convert -version'; called at start of handler.async function checkImageMagick() { try { execSync('convert -version'); return true; } catch (error) { throw new Error('ImageMagick is not installed. Please install it first.'); } }