Skip to main content
Glama
README.md
# mcp-image-reader

[中文文档](README.zh-CN.md)

An MCP server that lets AI agents actually **see** images — local files and remote URLs alike.

Most agent file-reading tools give up on binary data: they either refuse the file or hand the
model a wall of mojibake. The MCP protocol itself has a native `ImageContent` block (base64 +
mimeType); when a tool returns an `Image`, the client routes it into the model's **vision input**
instead of its text input. This server does exactly that, plus the three things that make it
usable in practice:

- **Re-encodes** anything Pillow can open to JPEG and caps the long edge, so one screenshot
  doesn't eat your whole context window.
- **Sends a plausible `Referer`**, so hotlink-protected CDNs stop answering with 403.
- **Optional directory allowlist**, to narrow "can read any file on disk" down to what you intend.

## Install

```bash
pip install mcp-image-reader
```

Or skip installing and let `uvx` fetch it on demand:

```bash
uvx mcp-image-reader
```

## Wire it into a client

Kiro (`.kiro/settings/mcp.json`), Claude Desktop, Cursor, Windsurf — all use the same stdio shape:

```json
{
  "mcpServers": {
    "image-reader": {
      "command": "uvx",
      "args": ["mcp-image-reader"],
      "autoApprove": ["view_image", "view_images", "list_images", "download_images"]
    }
  }
}
```

If you installed with `pip`, set `"command": "mcp-image-reader"` and leave `args` empty.
Clients that use `disabled`/`env` keys (Kiro) or `alwaysAllow` (some others) can add them as usual.

## Tools

| Tool | What it does |
| --- | --- |
| `view_image(source, max_side=1024, quality=82)` | Read one image: absolute local path or `http(s)` URL |
| `view_images(sources, max_side=1024, quality=82)` | Read a batch; a failed item is reported inline, the rest still come through |
| `list_images(directory, recursive=True)` | List image files with sizes, so the model can pick what to open |
| `download_images(urls, out_dir, prefix="img")` | Save remote images locally, `Referer` included |

`max_side` defaults to 1024, which is plenty for judging composition, palette, lighting and
texture. Push it to ~1600 when you need fine detail such as small text. Values above 4096 are
clamped; `quality` is clamped to 1–95.

## Configuration

| Variable | Default | Purpose |
| --- | --- | --- |
| `IMAGE_READER_ALLOW_DIRS` | unset (no limit) | Allowlist for local reads; separate paths with `;` on Windows, `:` elsewhere |
| `IMAGE_READER_REFERER` | auto-detected | Force a specific `Referer`, overriding the built-in rules |
| `IMAGE_READER_USER_AGENT` | Chrome UA | Custom `User-Agent` |
| `IMAGE_READER_MAX_BYTES` | `20971520` | Per-image download ceiling, in bytes |
| `IMAGE_READER_TIMEOUT` | `30` | HTTP timeout, in seconds |

Built-in `Referer` hints cover a few CDNs that validate against the site rather than their own
host (Xiaohongshu, Weibo, Zhihu, Pixiv, Bilibili, Douyin). Anything else falls back to the image
URL's own origin.

## Security

By default this server can read **any file the process running it can read**, and can issue
requests to arbitrary `http(s)` addresses. That's fine for local personal use. If the client
isn't fully trusted, or the server runs somewhere shared, set an allowlist:

```json
"env": { "IMAGE_READER_ALLOW_DIRS": "/home/me/assets:/home/me/screenshots" }
```

The `Referer` handling exists so that normal access to publicly reachable images isn't broken by
hotlink checks. It is not a way around authentication or authorization. Read only content you're
entitled to read, and respect the terms of the sites you fetch from.

## Development

```bash
pip install -e ".[dev]"
pytest        # fully offline, no network needed
ruff check .
```

## License

MIT