Image Tools

Resize, compress, convert, crop, rotate and watermark images, one at a time or in a zip.

Should I use this

Quality & Safety

A
Description quality
100%
Schema completeness
95%
Naming quality
100%
Poisoning risk
100%
Permission match
100%
Protocol compliance
100%

Based on automated analysis of tool definitions and protocol compliance.

Context Cost

~2,872Tokens (tool definitions)
~14.2 KBTypical response size
Significant attention impact (2.24% of 128k context)

This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.

Install

One-Click Install

Add this to your `claude_desktop_config.json` file:

{
  "mcpServers": {
    "image-tools": {
      "url": "https://byondr.co/image-tools/mcp"
    }
  }
}

Remote endpoints

https://byondr.co/image-tools/mcpstreamable-http

What it can do

Tool inventory

Tools (2)

🟢 Read-only🟡 Write🔴 Delete⚪ Unknown
🟡process_images(file_id, url, data_base64, filename, width, ...)

Resize, crop, rotate, flip, compress (to a file size with target_bytes), convert (jpeg, png, webp, avif), strip EXIF and GPS metadata from, and watermark (text, logo or both) one image or a zip of up to 50 images (up to 500 files per zip; files that are not supported images are skipped and listed). Crops are in pixels by default; for a zip of different sizes use crop_unit "percent" or crop_aspect, which are worked out per image. Give exactly one source: file_id (from create_upload), url (public https) or data_base64 (up to 3 MB inline; use create_upload, up to 100 MB, or url above that). One image in returns one image; a zip returns a zip. AVIF output is scaled down to fit 1,200 px on the longest side and WebP output to 36 megapixels. Returns a 24-hour download_url with the size and format of every output, and for each output the kinds of metadata found in the input and left in the output (location, date, camera, serial number, ...) and whether the colour profile was kept; kinds only, never the values. keep_date and keep_copyright write back the EXIF date taken and copyright after stripping. Large zips can take up to a minute; use a client timeout of at least 120 s. Free. Daily quotas are counted per caller in output megapixels; see start.md.

Input Schema

{
  "type": "object",
  "properties": {
    "file_id": {
      "description": "Image or zip file (up to 25 MB on this page)",
      "type": "string"
    },
    "url": {
      "description": "Or a public https URL of an image or zip",
      "type": "string",
      "format": "uri"
    },
    "data_base64": {
      "description": "Base64 bytes of an image or zip, up to 3 MB decoded; use create_upload (up to 100 MB) or url above that",
      "type": "string"
    },
    "filename": {
      "description": "Name for the output when the source has none",
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "width": {
      "description": "Width in pixels",
      "type": "integer",
      "minimum": 1,
      "maximum": 12000
    },
    "height": {
      "description": "Height in pixels",
      "type": "integer",
      "minimum": 1,
      "maximum": 12000
    },
    "resize_unit": {
      "default": "px",
      "description": "Unit of width and height: px, or percent (1-1000) of each image's own size, so a zip of different sizes is scaled alike. Over 100 enlarges only with fit contain",
      "type": "string",
      "enum": [
        "px",
        "percent"
      ]
    },
    "max_side": {
      "description": "Instead of width and height: the longest side in pixels, per image, so portrait and landscape images both get it on their long edge. Never enlarges unless fit is contain",
      "type": "integer",
      "minimum": 1,
      "maximum": 12000
    },
    "fit": {
      "default": "scale-down",
      "description": "How to fit into width x height: scale-down never enlarges; cover fills and crops; pad adds borders",
      "type": "string",
      "enum": [
        "scale-down",
        "contain",
        "cover",
        "pad"
      ]
    },
    "crop_left": {
      "description": "Crop: left edge (px, or % of the width with crop_unit percent)",
      "type": "number",
      "minimum": 0,
      "maximum": 12000
    },
    "crop_top": {
      "description": "Crop: top edge (px, or % of the height with crop_unit percent)",
      "type": "number",
      "minimum": 0,
      "maximum": 12000
    },
    "crop_width": {
      "description": "Crop: width (px, or % of the width with crop_unit percent)",
      "type": "number",
      "exclusiveMinimum": 0,
      "maximum": 12000
    },
    "crop_height": {
      "description": "Crop: height (px, or % of the height with crop_unit percent)",
      "type": "number",
      "exclusiveMinimum": 0,
      "maximum": 12000
    },
    "crop_unit": {
      "default": "px",
      "description": "Unit of the four crop fields: px (whole pixels) or percent (0-100 of each image, so a zip of different sizes gets the same part of every image)",
      "type": "string",
      "enum": [
        "px",
        "percent"
      ]
    },
    "crop_aspect": {
      "description": "Instead of the four crop fields: the largest centered crop of this width:height ratio, per image",
      "type": "string",
      "enum": [
        "1:1",
        "4:5",
        "5:4",
        "4:3",
        "3:4",
        "3:2",
        "2:3",
        "16:9",
        "9:16"
      ]
    },
    "format": {
      "default": "keep",
      "description": "Output format (keep turns HEIC into JPEG and GIF into PNG)",
      "type": "string",
      "enum": [
        "keep",
        "jpeg",
        "png",
        "webp",
        "avif"
      ]
    },
    "quality": {
      "default": 82,
      "description": "Quality 1-100 for JPEG, WebP and AVIF; lower is smaller. Ignored with target_bytes",
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    },
    "target_bytes": {
      "description": "Instead of quality: the most bytes each output may have (10,000-50,000,000; 1 MB = 1,000,000), for upload forms with a size limit. Uses the highest quality from 45 to 92 that fits; if even 45 is too big, the image is scaled down too. Each item then reports the quality used, and over_target when it could not get under",
      "type": "integer",
      "minimum": 10000,
      "maximum": 50000000
    },
    "background": {
      "default": "#ffffff",
      "description": "JPEG only: colour under transparent areas, #rrggbb",
      "type": "string",
      "pattern": "^#[0-9a-fA-F]{6}$"
    },
    "strip_metadata": {
      "default": true,
      "description": "Remove EXIF, GPS location and camera data",
      "type": "boolean"
    },
    "keep_date": {
      "default": false,
      "description": "With strip_metadata: keep the EXIF date taken (DateTimeOriginal and DateTimeDigitized, with their time zone and fraction of a second) so photos still sort by date. A date and time taken found only in XMP or PNG text is written as the EXIF DateTimeOriginal; IPTC dates are not kept",
      "type": "boolean"
    },
    "keep_copyright": {
      "default": false,
      "description": "With strip_metadata: keep the EXIF Copyright tag. XMP and IPTC rights are not kept",
      "type": "boolean"
    },
    "rotate": {
      "default": 0,
      "description": "Rotate clockwise (degrees)",
      "type": "number",
      "enum": [
        0,
        90,
        180,
        270
      ]
    },
    "flip": {
      "default": "none",
      "description": "Flip: h = horizontal, v = vertical, hv = both",
      "type": "string",
      "enum": [
        "none",
        "h",
        "v",
        "hv"
      ]
    },
    "watermark_text": {
      "description": "Watermark text",
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "watermark_position": {
      "default": "bottom-right",
      "description": "Watermark position; custom places the centre of the text at watermark_x, watermark_y",
      "type": "string",
      "enum": [
        "bottom-right",
        "bottom-left",
        "top-right",
        "top-left",
        "center",
        "tile",
        "custom"
      ]
    },
    "watermark_opacity": {
      "default": 0.6,
      "description": "Watermark opacity, 0.05 to 1",
      "type": "number",
      "minimum": 0.05,
      "maximum": 1
    },
    "watermark_color": {
      "default": "#ffffff",
      "description": "Watermark color, #rrggbb",
      "type": "string",
      "pattern": "^#[0-9a-fA-F]{6}$"
    },
    "watermark_x": {
      "description": "With watermark_position custom: the centre of the text, % of each image's width from the left (0-100)",
      "type": "number",
      "minimum": 0,
      "maximum": 100
    },
    "watermark_y": {
      "description": "With watermark_position custom: the centre of the text, % of each image's height from the top (0-100)",
      "type": "number",
      "minimum": 0,
      "maximum": 100
    },
    "watermark_size": {
      "description": "Watermark font size: px (8-400, whole numbers) or, with watermark_size_unit percent, % of each image's width (0.5-20, giving 12-1000 px). Default 4% of the width",
      "type": "number",
      "exclusiveMinimum": 0,
      "maximum": 400
    },
    "watermark_size_unit": {
      "default": "px",
      "description": "Unit of watermark_size: px, or percent of each image's width (so a zip of different sizes gets the same relative size)",
      "type": "string",
      "enum": [
        "px",
        "percent"
      ]
    },
    "watermark_image": {
      "description": "Logo for the watermark, with or instead of watermark_text: a file_id from create_upload, a public https url, or data:image/png;base64,... (up to 1 MB inline; an inline image and logo share the 3 MB inline limit). PNG with transparency is best; WebP and JPEG work. Up to 2 MB and 2048 px on the longest side; rasterise SVG to PNG first. With text, the logo sits left of the text and both are placed as one",
      "type": "string",
      "minLength": 1
    },
    "watermark_image_size": {
      "description": "Logo width, % of each image's width (2-50). Default 15",
      "type": "number",
      "minimum": 2,
      "maximum": 50
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}

Output Schema

{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "image",
        "zip"
      ]
    },
    "download_url": {
      "type": "string"
    },
    "expires_at": {
      "type": "string"
    },
    "content_type": {
      "type": "string"
    },
    "bytes": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "megapixels": {
      "type": "number"
    },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "width": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "height": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "format": {
            "type": "string"
          },
          "bytes": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "megapixels": {
            "type": "number"
          },
          "quality": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "over_target": {
            "type": "boolean"
          },
          "metadata": {
            "type": "object",
            "properties": {
              "readable": {
                "type": "boolean",
                "description": "Whether the input's metadata could be read (false for GIF and damaged headers)"
              },
              "found": {
                "anyOf": [
                  {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "location",
                        "date",
                        "saved",
                        "camera",
                        "lens",
                        "owner",
                        "copyright",
                        "serial",
                        "software",
                        "description",
                        "comment",
                        "thumbnail",
                        "settings",
                        "makernote",
                        "xmp",
                        "iptc",
                        "text",
                        "extra",
                        "other"
                      ]
                    }
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Kinds of metadata in the input, never their values; null when not readable"
              },
              "remaining": {
                "anyOf": [
                  {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "location",
                        "date",
                        "saved",
                        "camera",
                        "lens",
                        "owner",
                        "copyright",
                        "serial",
                        "software",
                        "description",
                        "comment",
                        "thumbnail",
                        "settings",
                        "makernote",
                        "xmp",
                        "iptc",
                        "text",
                        "extra",
                        "other"
                      ]
                    }
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Kinds of metadata still in the output: [] after strip_metadata, or only date and copyright with keep_date and keep_copyright; null when the output could not be read"
              },
              "color_profile": {
                "type": "string",
                "enum": [
                  "kept",
                  "converted_to_srgb",
                  "none",
                  "unknown"
                ],
                "description": "kept: the input's colour profile is in the output; converted_to_srgb: the profile was not kept and the encoder converted the colours to sRGB; none: the input had none; unknown: the input could not be read"
              }
            },
            "required": [
              "readable",
              "found",
              "remaining",
              "color_profile"
            ],
            "additionalProperties": false,
            "description": "The kinds of metadata found in the input and left in the output; never the values"
          }
        },
        "required": [
          "name",
          "width",
          "height",
          "format",
          "bytes",
          "megapixels"
        ],
        "additionalProperties": false
      }
    },
    "skipped": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "reason"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "kind",
    "download_url",
    "expires_at",
    "content_type",
    "bytes",
    "megapixels",
    "items",
    "skipped"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false
}
🟡create_upload(bytes, filename)

Get a one-hour signed upload URL for an image or a zip that is too large to send inline (over 3 MB; up to 100 MB here) or that already sits on disk. PUT the raw bytes to upload_url with a Content-Length header, then pass file_id to process_images. Free.

Input Schema

{
  "type": "object",
  "properties": {
    "bytes": {
      "type": "integer",
      "minimum": 1,
      "maximum": 104857600,
      "description": "Exact size of the file you will upload, in bytes (at most 100 MB)"
    },
    "filename": {
      "description": "Original file name, used to name the output",
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "bytes"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}

Output Schema

{
  "type": "object",
  "properties": {
    "file_id": {
      "type": "string"
    },
    "upload_url": {
      "type": "string"
    },
    "method": {
      "type": "string",
      "const": "PUT"
    },
    "expires_at": {
      "type": "string"
    },
    "max_bytes": {
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "file_id",
    "upload_url",
    "method",
    "expires_at",
    "max_bytes"
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false
}

Community

Rate this Server

Evidence

Recent observations

verifiedversion not recorded2 tools