shutter-mcp
README.md
# shutter-mcp
[](https://github.com/keivanmalhani/shutter-mcp/actions/workflows/ci.yml)



Point Claude, or any [MCP](https://modelcontextprotocol.io) client, at a folder of RAW and JPEG files. It can scan the library, break down which cameras and lenses actually got used, find duplicates, flag the shots that are probably blurry, or write a full cull report. It never gets a tool that can touch a file: read-only means read-only, all the way down.
Why this exists: culling tools that touch your shoots ask for a subscription or your client's files on somebody else's cloud. This asks for neither. It reads the metadata and pixels already sitting on your disk and answers in text, not files, and it is deliberately the read-only first step of a bigger local-first project (see Roadmap; the write-capable half is shutter-cull-mcp).
## 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](https://docs.astral.sh/uv/) (recommended):
```bash
uv tool install shutter-mcp
```
With pipx:
```bash
pipx install shutter-mcp
```
From source, for development:
```bash
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.
```bash
shutter-mcp --root /path/to/photos --root /path/to/scans
```
### Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"shutter-mcp": {
"command": "shutter-mcp",
"args": ["--root", "/Users/you/Pictures/2026-shoot"]
}
}
}
```
### Claude Code
```bash
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_report` returns markdown text; it never writes a file.
- **Root allowlist.** The server only accepts `--root` directories 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
```bash
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](https://github.com/keivanmalhani/shutter-cull) is the photo engine and
[shutter-select](https://github.com/keivanmalhani/shutter-select) the video one.
[shutter-cull-mcp](https://github.com/keivanmalhani/shutter-cull-mcp) is the side that does
write, with human confirmation and undo. This is the read-only one, and that guarantee is
structural rather than a promise in the docs.
## License
MIT, see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues