shutter-mcp
English | Espanol

A local, read-only MCP server that lets AI agents inspect and analyze a photo library. Point Claude (or any MCP client) at a folder of RAW and JPEG files and ask it to scan the library, break down cameras and lenses, find duplicates, flag likely-blurry shots, or generate a cull report, all without a single byte leaving your machine.
Why this exists: photo culling tools that touch your shoots are either paid subscriptions or require uploading client work to someone else's cloud. shutter-mcp is neither. It runs entirely on your filesystem, reads metadata and pixels you already have locally, and returns text, not files. It is the read-only MVP and the first piece of a larger local-first photo culling project (see Roadmap).
Tools
| Tool | Description | Read/write |
|---|---|---|
scan_library |
Recursively count files under a root by extension, size, and mtime range | read-only |
library_stats |
Aggregate EXIF stats across a library: camera, lens, focal length, ISO, year-month | read-only |
read_exif |
Flattened EXIF fields for one file | read-only |
find_duplicates |
Exact (sha256) or visual (perceptual hash) duplicate groups | read-only |
blur_scores |
Laplacian-variance blur ranking for jpg files | read-only |
cull_report |
Markdown report combining all of the above | read-only |
Every tool is read-only. None of them write, move, rename, or delete a file.
Install
Requires Python 3.11+.
With uv (recommended):
uv tool install shutter-mcp
With pipx:
pipx install shutter-mcp
From source, for development:
git clone https://github.com/keivanmalhani/shutter-mcp.git
cd shutter-mcp
uv venv
uv pip install -e ".[dev]"
Usage
shutter-mcp takes one or more --root directories at launch. Every tool call is validated against this allowlist, so the server can only ever see the folders you explicitly grant it.
shutter-mcp --root /path/to/photos --root /path/to/scans
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"shutter-mcp": {
"command": "shutter-mcp",
"args": ["--root", "/Users/you/Pictures/2026-shoot"]
}
}
}
Claude Code
claude mcp add shutter-mcp -- shutter-mcp --root /Users/you/Pictures/2026-shoot
Security model
- Read-only. No tool writes, moves, renames, or deletes user files.
cull_reportreturns markdown text; it never writes a file. - Root allowlist. The server only accepts
--rootdirectories at launch. Every path argument a tool receives is resolved (Path.resolve, symlinks included) and rejected if it falls outside every allowed root, even if a symlink inside the root points elsewhere. - No network calls at runtime. Everything happens on your filesystem.
- No image bytes returned. Tools return paths and metadata only, never base64 or raw pixel data.
- No telemetry, no analytics.
Format support
| Capability | Extensions |
|---|---|
EXIF read (read_exif, library_stats) |
jpg, jpeg, tif, tiff, png, dng, arw, raf, nef, cr2, cr3 (best effort on RAW) |
Pixel decode (blur_scores, visual mode of find_duplicates) |
jpg, jpeg, png, tiff |
RAW pixel decode (blur scoring and visual dedup on ARW/RAF/etc.) is out of scope for this MVP; EXIF reading works today on both. See Roadmap.
Development
uv pip install -e ".[dev]"
uv run pytest
Tests generate their own fixture images with Pillow at run time; no binary fixtures are committed to the repo.
Roadmap
- RAW pixel decode (ARW, RAF, and friends) via rawpy, for blur scoring and visual dedup
- XMP rating/flag writeback so cull decisions show up in Lightroom Classic
- Integration as the agent interface for the local-first photo culling engine
- Publish to PyPI and the MCP registry
Family
shutter-cull is the photo engine andshutter-select the video one.shutter-cull-mcp is the side that doeswrite, with human confirmation and undo. This is the read-only one, and that guarantee isstructural rather than a promise in the docs.
License
MIT, see LICENSE.
Este documento tambien esta en espanol, completo y no abreviado.