Koskamo

byondr Image Tools MCP

An MCP server provided by byondr that enables AI agents to perform image processing tasks including resizing, compressing, converting formats, cropping, rotating, flipping, and watermarking images. It supports processing single images or zips, with options to strip EXIF metadata, and handles client-side processing where possible while using a server for unsupported formats.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

byondr Image Tools MCP logo, by byondrVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-01Operated by byondr
MCP endpoint (Streamable HTTP)
https://byondr.co/image-tools/mcp
Vendor documentation

What you can do with it

  • Resize images to specific dimensions or aspect ratios
  • Compress photos to reduce file size with minimal quality loss
  • Convert images between JPG, PNG, WebP, and AVIF formats
  • Remove EXIF metadata to protect privacy before sharing

Source and maintenance

Vendor evidenceRegistry 0.1.0

Protocol and capabilities

Server
Image Tools 0.1.0
MCP protocol
2025-06-18
Tools
2 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

byondr Image Tools. Files over 3 MB: call create_upload, PUT the bytes, pass file_id (or pass a public https url). To crop a zip of different sizes, use crop_unit percent or crop_aspect; to resize one, max_side or resize_unit percent. Download links expire after 24 hours. Each output lists the kinds of metadata found and removed, never the values; keep_date and keep_copyright keep only the EXIF date taken and copyright. If a call asks for payment (HTTP 402 or an x402 payment request), ask the user before paying.

Tools

2 advertised

process_images
Advertised by the server during the last successful anonymous check.
Open-world

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.

process_images input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "fit": {
      "enum": [
        "scale-down",
        "contain",
        "cover",
        "pad"
      ],
      "type": "string",
      "default": "scale-down",
      "description": "How to fit into width x height: scale-down never enlarges; cover fills and crops; pad adds borders"
    },
    "url": {
      "type": "string",
      "format": "uri",
      "description": "Or a public https URL of an image or zip"
    },
    "flip": {
      "enum": [
        "none",
        "h",
        "v",
        "hv"
      ],
      "type": "string",
      "default": "none",
      "description": "Flip: h = horizontal, v = vertical, hv = both"
    },
    "width": {
      "type": "integer",
      "maximum": 12000,
      "minimum": 1,
      "description": "Width in pixels"
    },
    "format": {
      "enum": [
        "keep",
        "jpeg",
        "png",
        "webp",
        "avif"
      ],
      "type": "string",
      "default": "keep",
      "description": "Output format (keep turns HEIC into JPEG and GIF into PNG)"
    },
    "height": {
      "type": "integer",
      "maximum": 12000,
      "minimum": 1,
      "description": "Height in pixels"
    },
    "rotate": {
      "enum": [
        0,
        90,
        180,
        270
      ],
      "type": "number",
      "default": 0,
      "description": "Rotate clockwise (degrees)"
    },
    "file_id": {
      "type": "string",
      "description": "Image or zip file (up to 25 MB on this page)"
    },
    "quality": {
      "type": "integer",
      "default": 82,
      "maximum": 100,
      "minimum": 1,
      "description": "Quality 1-100 for JPEG, WebP and AVIF; lower is smaller. Ignored with target_bytes"
    },
    "crop_top": {
      "type": "number",
      "maximum": 12000,
      "minimum": 0,
      "description": "Crop: top edge (px, or % of the height with crop_unit percent)"
    },
    "filename": {
      "type": "string",
      "maxLength": 200,
      "minLength": 1,
      "description": "Name for the output when the source has none"
    },
    "max_side": {
      "type": "integer",
      "maximum": 12000,
      "minimum": 1,
      "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"
    },
    "crop_left": {
      "type": "number",
      "maximum": 12000,
      "minimum": 0,
      "description": "Crop: left edge (px, or % of the width with crop_unit percent)"
    },
    "crop_unit": {
      "enum": [
        "px",
        "percent"
      ],
      "type": "string",
      "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)"
    },
    "keep_date": {
      "type": "boolean",
      "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"
    },
    "background": {
      "type": "string",
      "default": "#ffffff",
      "pattern": "^#[0-9a-fA-F]{6}$",
      "description": "JPEG only: colour under transparent areas, #rrggbb"
    },
    "crop_width": {
      "type": "number",
      "maximum": 12000,
      "description": "Crop: width (px, or % of the width with crop_unit percent)",
      "exclusiveMinimum": 0
    },
    "crop_aspect": {
      "enum": [
        "1:1",
        "4:5",
        "5:4",
        "4:3",
        "3:4",
        "3:2",
        "2:3",
        "16:9",
        "9:16"
      ],
      "type": "string",
      "description": "Instead of the four crop fields: the largest centered crop of this width:height ratio, per image"
    },
    "crop_height": {
      "type": "number",
      "maximum": 12000,
      "description": "Crop: height (px, or % of the height with crop_unit percent)",
      "exclusiveMinimum": 0
    },
    "data_base64": {
      "type": "string",
      "description": "Base64 bytes of an image or zip, up to 3 MB decoded; use create_upload (up to 100 MB) or url above that"
    },
    "resize_unit": {
      "enum": [
        "px",
        "percent"
      ],
      "type": "string",
      "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"
    },
    "watermark_x": {
      "type": "number",
      "maximum": 100,
      "minimum": 0,
      "description": "With watermark_position custom: the centre of the text, % of each image's width from the left (0-100)"
    },
    "watermark_y": {
      "type": "number",
      "maximum": 100,
      "minimum": 0,
      "description": "With watermark_position custom: the centre of the text, % of each image's height from the top (0-100)"
    },
    "target_bytes": {
      "type": "integer",
      "maximum": 50000000,
      "minimum": 10000,
      "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"
    },
    "keep_copyright": {
      "type": "boolean",
      "default": false,
      "description": "With strip_metadata: keep the EXIF Copyright tag. XMP and IPTC rights are not kept"
    },
    "strip_metadata": {
      "type": "boolean",
      "default": true,
      "description": "Remove EXIF, GPS location and camera data"
    },
    "watermark_size": {
      "type": "number",
      "maximum": 400,
      "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",
      "exclusiveMinimum": 0
    },
    "watermark_text": {
      "type": "string",
      "maxLength": 100,
      "minLength": 1,
      "description": "Watermark text"
    },
    "watermark_color": {
      "type": "string",
      "default": "#ffffff",
      "pattern": "^#[0-9a-fA-F]{6}$",
      "description": "Watermark color, #rrggbb"
    },
    "watermark_image": {
      "type": "string",
      "minLength": 1,
      "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"
    },
    "watermark_opacity": {
      "type": "number",
      "default": 0.6,
      "maximum": 1,
      "minimum": 0.05,
      "description": "Watermark opacity, 0.05 to 1"
    },
    "watermark_position": {
      "enum": [
        "bottom-right",
        "bottom-left",
        "top-right",
        "top-left",
        "center",
        "tile",
        "custom"
      ],
      "type": "string",
      "default": "bottom-right",
      "description": "Watermark position; custom places the centre of the text at watermark_x, watermark_y"
    },
    "watermark_size_unit": {
      "enum": [
        "px",
        "percent"
      ],
      "type": "string",
      "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)"
    },
    "watermark_image_size": {
      "type": "number",
      "maximum": 50,
      "minimum": 2,
      "description": "Logo width, % of each image's width (2-50). Default 15"
    }
  }
}
process_images output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "kind",
    "download_url",
    "expires_at",
    "content_type",
    "bytes",
    "megapixels",
    "items",
    "skipped"
  ],
  "properties": {
    "kind": {
      "enum": [
        "image",
        "zip"
      ],
      "type": "string"
    },
    "bytes": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": -9007199254740991
    },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "width",
          "height",
          "format",
          "bytes",
          "megapixels"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "bytes": {
            "type": "integer",
            "maximum": 9007199254740991,
            "minimum": -9007199254740991
          },
          "width": {
            "type": "integer",
            "maximum": 9007199254740991,
            "minimum": -9007199254740991
          },
          "format": {
            "type": "string"
          },
          "height": {
            "type": "integer",
            "maximum": 9007199254740991,
            "minimum": -9007199254740991
          },
          "quality": {
            "type": "integer",
            "maximum": 9007199254740991,
            "minimum": -9007199254740991
          },
          "metadata": {
            "type": "object",
            "required": [
              "readable",
              "found",
              "remaining",
              "color_profile"
            ],
            "properties": {
              "found": {
                "anyOf": [
                  {
                    "type": "array",
                    "items": {
                      "enum": [
                        "location",
                        "date",
                        "saved",
                        "camera",
                        "lens",
                        "owner",
                        "copyright",
                        "serial",
                        "software",
                        "description",
                        "comment",
                        "thumbnail",
                        "settings",
                        "makernote",
                        "xmp",
                        "iptc",
                        "text",
                        "extra",
                        "other"
                      ],
                      "type": "string"
                    }
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Kinds of metadata in the input, never their values; null when not readable"
              },
              "readable": {
                "type": "boolean",
                "description": "Whether the input's metadata could be read (false for GIF and damaged headers)"
              },
              "remaining": {
                "anyOf": [
                  {
                    "type": "array",
                    "items": {
                      "enum": [
                        "location",
                        "date",
                        "saved",
                        "camera",
                        "lens",
                        "owner",
                        "copyright",
                        "serial",
                        "software",
                        "description",
                        "comment",
                        "thumbnail",
                        "settings",
                        "makernote",
                        "xmp",
                        "iptc",
                        "text",
                        "extra",
                        "other"
                      ],
                      "type": "string"
                    }
                  },
                  {
                    "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": {
                "enum": [
                  "kept",
                  "converted_to_srgb",
                  "none",
                  "unknown"
                ],
                "type": "string",
                "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"
              }
            },
            "description": "The kinds of metadata found in the input and left in the output; never the values",
            "additionalProperties": false
          },
          "megapixels": {
            "type": "number"
          },
          "over_target": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      }
    },
    "skipped": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "reason"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          }
        },
        "additionalProperties": false
      }
    },
    "expires_at": {
      "type": "string"
    },
    "megapixels": {
      "type": "number"
    },
    "content_type": {
      "type": "string"
    },
    "download_url": {
      "type": "string"
    }
  },
  "additionalProperties": false
}
process_images annotations
{
  "title": "Process images",
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
create_upload
Advertised by the server during the last successful anonymous check.

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.

create_upload input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "bytes"
  ],
  "properties": {
    "bytes": {
      "type": "integer",
      "maximum": 104857600,
      "minimum": 1,
      "description": "Exact size of the file you will upload, in bytes (at most 100 MB)"
    },
    "filename": {
      "type": "string",
      "maxLength": 200,
      "minLength": 1,
      "description": "Original file name, used to name the output"
    }
  }
}
create_upload output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "file_id",
    "upload_url",
    "method",
    "expires_at",
    "max_bytes"
  ],
  "properties": {
    "method": {
      "type": "string",
      "const": "PUT"
    },
    "file_id": {
      "type": "string"
    },
    "max_bytes": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": -9007199254740991
    },
    "expires_at": {
      "type": "string"
    },
    "upload_url": {
      "type": "string"
    }
  },
  "additionalProperties": false
}
create_upload annotations
{
  "title": "Create upload URL",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
1234 ms
95th percentile latency
1234 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 1234 ms · metadata changed · 2 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http byondr-image-tools-mcp https://byondr.co/image-tools/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add byondr-image-tools-mcp --url https://byondr.co/image-tools/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable byondr-image-tools-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "byondr-image-tools-mcp": {
      "url": "https://byondr.co/image-tools/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "byondr-image-tools-mcp": {
      "type": "streamableHttp",
      "url": "https://byondr.co/image-tools/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start byondr-image-tools-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "byondr-image-tools-mcp": {
      "type": "http",
      "url": "https://byondr.co/image-tools/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http byondr-image-tools-mcp https://byondr.co/image-tools/mcp

Claude Desktop

Merge this into claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), then restart Claude Desktop.
Claude Desktop configuration
{
  "mcpServers": {
    "byondr-image-tools-mcp": {
      "url": "https://byondr.co/image-tools/mcp"
    }
  }
}

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "byondr-image-tools-mcp": {
      "serverUrl": "https://byondr.co/image-tools/mcp"
    }
  }
}

Let your agent find its tools

Find servers like byondr Image Tools MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run byondr Image Tools MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing