Content
<h1 align="center">immich-mcp</h1>
<p align="center"><strong>An MCP server that puts your self-hosted Immich photo and video library behind LLM tool calls, so an AI client can search, curate, and clean it up in plain language.</strong></p>
<p align="center">
<a href="https://www.npmjs.com/package/immich-mcp"><img src="https://img.shields.io/npm/v/immich-mcp?style=for-the-badge&label=npm&color=2563EB" alt="npm version" /></a>
<a href="https://github.com/lidless-labs/immich-mcp/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/lidless-labs/immich-mcp/ci.yml?branch=master&style=for-the-badge&label=ci" alt="CI status" /></a>
<img src="https://img.shields.io/badge/Node.js-20%2B-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js 20+" />
<img src="https://img.shields.io/badge/MCP-1.x-7C3AED?style=for-the-badge" alt="MCP 1.x" />
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-2EA043?style=for-the-badge" alt="MIT License" /></a>
</p>
<p align="center">
<a href="https://lidless.dev/immich-mcp"><strong>Website</strong></a>
·
<a href="https://www.npmjs.com/package/immich-mcp">npm</a>
·
<a href="https://immich.app">Immich</a>
</p>
**What it is.** `immich-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) server for [Immich](https://immich.app), the self-hosted photo and video library. It exposes the Immich API as typed MCP tools so any MCP client, like Claude Desktop, Claude Code, OpenClaw, or Codex CLI, can browse and search your photo library, manage albums and tags, recognize people, surface memories, and resolve duplicates in plain language.
**Why use it.** Immich already holds your whole life in photos, but the web UI is built for clicking, not for asking. Wiring it through MCP lets you say "find every sunset photo from last summer and put them in an album" or "show me what is taking up the most space in trash" and have an agent do the work through audited tool calls instead of hand-clicking.
**How it differs.** It is the broadest Immich MCP surface available: 74 tools across 16 domains, including memories, duplicate detection with checksum-safe resolution, trash auditing, and motion-photo stacks that other Immich MCP servers do not cover. Writes are off by default and destructive calls demand explicit confirmation, so an agent cannot quietly delete your photos.
## What it does
`immich-mcp` connects a self-hosted Immich photo library to an LLM through the Model Context Protocol. It turns Immich's REST API into 74 typed, schema-validated tool calls grouped into 16 domains, so an MCP-compatible AI client can drive your photo and video library directly:
- **Search** your photo library by natural language (CLIP / smart search), by metadata (date, location, camera, people, tags), or by a server-side discovery feed.
- **Curate** albums, tags, shared links, and stacks (motion photos, bracketed shots, RAW + JPEG pairs).
- **Recognize people**: list recognized faces, rename them, merge duplicate face clusters, suggest names for unnamed clusters.
- **Surface memories**: "on this day" lanes, a today digest, and recent-upload summaries suitable for a daily cron drop.
- **Clean up duplicates and trash**: categorize duplicate groups, find byte-identical or CLIP-grouped dupes, audit the active library and trash for reclaimable space, and resolve with a configurable keep strategy.
- **Manage assets**: read metadata and EXIF, favorite, archive, rate, geotag, soft-delete (trash), restore, and upload from a confined local directory.
Reads always work. Writes and deletes only appear when you opt in, and the most destructive ones require a per-call `confirm: true`.
## Quickstart
```bash
# 1. Get an Immich API key: Account Settings > API Keys > New API Key in the Immich web UI.
# 2. Run it directly with npx (no install needed):
IMMICH_BASE_URL=https://photos.example.com/api \
IMMICH_API_KEY=your_key_here \
npx -y immich-mcp
```
The server speaks MCP over stdio, so it is meant to be launched by an MCP client rather than used interactively. Wire it into your client with the config below.
## MCP client config
Copy this into your MCP client. It uses `npx -y immich-mcp` so there is nothing to install globally:
```json
{
"mcpServers": {
"immich-mcp": {
"command": "npx",
"args": ["-y", "immich-mcp"],
"env": {
"IMMICH_BASE_URL": "https://photos.example.com/api",
"IMMICH_API_KEY": "YOUR_KEY",
"IMMICH_ALLOW_WRITES": "false"
}
}
}
}
```
- **Claude Desktop**: add the block above to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows), then restart Claude.
- **Claude Code**:
```bash
claude mcp add immich-mcp \
-e IMMICH_BASE_URL=https://photos.example.com/api \
-e IMMICH_API_KEY=YOUR_KEY \
-e IMMICH_ALLOW_WRITES=false \
-- npx -y immich-mcp
```
Add `--scope user` to make it available from any directory.
- **OpenClaw**: add the same `command` / `args` / `env` under `mcps.entries.immich-mcp` in `~/.openclaw/openclaw.json`, then `systemctl --user restart openclaw-gateway` and `openclaw mcp list` to confirm.
- **Codex CLI**: add a `[mcp_servers.immich-mcp]` block to `~/.codex/config.toml`:
```toml
[mcp_servers.immich-mcp]
command = "npx"
args = ["-y", "immich-mcp"]
env = { IMMICH_BASE_URL = "https://photos.example.com/api", IMMICH_API_KEY = "YOUR_KEY", IMMICH_ALLOW_WRITES = "false" }
```
Prefer a global install or a source checkout? See [Install](#install).
## Configuration
Set these environment variables in your MCP client config:
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `IMMICH_BASE_URL` | yes | - | Base URL of your Immich API, e.g. `https://photos.example.com/api` (or `http://192.0.2.10:2283/api` for a LAN host). |
| `IMMICH_API_KEY` | yes | - | API key from Account Settings > API Keys in the Immich web UI. |
| `IMMICH_ALLOW_WRITES` | no | `false` | Set to `true` to expose write and delete tools. Reads always work. |
| `IMMICH_VERIFY_SSL` | no | `true` | Set to `false` to accept self-signed certs. The relaxed TLS check is scoped to the Immich client only (a dedicated request dispatcher); it does **not** disable certificate validation process-wide. |
| `IMMICH_UPLOAD_BASE_DIR` | no | - | Absolute directory that `immich_upload_asset_from_path` is confined to. When unset, path-based upload is refused. Files must resolve (after following symlinks) inside this directory. |
### Getting an API key
1. Log into Immich.
2. Account Settings > API Keys > New API Key.
3. Name it (e.g. `mcp`) and copy the value.
## Tools
74 tools across 16 domains. Tools marked *(requires `confirm: true`)* are destructive and refuse to run without an explicit confirm flag on the call; all write and delete tools additionally require `IMMICH_ALLOW_WRITES=true`.
### System (5)
- `immich_ping` - verify connectivity, return server pong
- `immich_get_server_info` - server config (login page, OAuth, theme)
- `immich_get_server_statistics` - photo, video, and user counts plus per-user storage
- `immich_get_capabilities` - enabled features (search, ML, OAuth)
- `immich_get_storage` - storage used, available, total, and template
### Assets (11)
- `immich_list_assets` - paginated list/search with filters (favorite, archived, date, type, person, album, tag)
- `immich_get_asset` - full metadata for one asset
- `immich_get_asset_exif` - EXIF / IPTC block for an asset
- `immich_get_asset_statistics` - per-user image and video counts
- `immich_download_asset_original` - Immich URL path for the original file
- `immich_download_asset_thumbnail` - Immich URL path for the thumbnail
- `immich_upload_asset_from_path` - upload a local file *(confined to `IMMICH_UPLOAD_BASE_DIR`; refused when unset)*
- `immich_update_asset` - description, favorite, archive, rating, date, geotag
- `immich_bulk_update_assets` - bulk favorite / archive / visibility *(requires `confirm: true`)*
- `immich_delete_asset` - soft delete (trash) by default; `permanent: true` bypasses trash *(permanent requires `confirm: true`)*
- `immich_restore_from_trash` - restore previously trashed assets
### Search (3)
- `immich_search_smart` - CLIP / semantic search by natural-language query
- `immich_search_metadata` - structured search (date, location, camera, people, tags, albums)
- `immich_search_explore` - random sample for discovery / "explore" lanes
### Albums (8)
- `immich_list_albums`
- `immich_get_album`
- `immich_get_album_statistics`
- `immich_create_album`
- `immich_update_album` - rename, set cover, set description
- `immich_add_assets_to_album`
- `immich_remove_assets_from_album`
- `immich_delete_album` - assets are NOT deleted *(requires `confirm: true`)*
### Album flows (1)
- `immich_search_then_album` - run a smart or metadata search, then create an album from the matches *(writes-gated)*
### People (7)
- `immich_list_people`
- `immich_get_person`
- `immich_get_person_assets` - photos and videos a face appears in
- `immich_update_person` - name, birthdate, visibility, favorite, feature face
- `immich_hide_person` - exclude from default lists without deleting
- `immich_merge_people` - fold duplicate face clusters into one *(requires `confirm: true`)*
- `immich_suggest_face_names` - top unnamed people by face count, for triage
### Tags (7)
- `immich_list_tags`
- `immich_get_tag`
- `immich_create_tag`
- `immich_update_tag` - rename or recolor
- `immich_delete_tag` *(requires `confirm: true`)*
- `immich_add_tag_to_assets`
- `immich_remove_tag_from_assets`
### Shared links (5)
- `immich_list_shared_links`
- `immich_get_shared_link`
- `immich_create_shared_link`
- `immich_update_shared_link`
- `immich_delete_shared_link` *(requires `confirm: true`)*
### Activities (4)
- `immich_list_activities` - comments and likes on albums or assets
- `immich_get_activity_statistics`
- `immich_create_activity`
- `immich_delete_activity` *(requires `confirm: true`)*
### Memories (2)
- `immich_list_memories` - memory lanes ("years ago", etc.)
- `immich_get_memory`
### Memory flows (2)
- `immich_memories_today` - today's "X years ago" lanes, chat-formatted
- `immich_daily_digest` - server stats + today's memories + recent uploads as a markdown drop
### Duplicates (2)
- `immich_list_duplicates` - duplicate groups detected by Immich's hashing pass
- `immich_resolve_duplicates` - keep[] / discard[], dry-run by default *(`delete: true` + `confirm: true` to remove)*
### Duplicate flows (8)
- `immich_categorize_duplicates` - bin duplicate groups (checksum-exact, name-size, resolution variants, bursts, edits)
- `immich_find_byte_dupes` - ready-to-trash candidates with a `safetyMode` (strict-checksum default)
- `immich_resolve_with_keep_strategy` - end-to-end dedupe, dry-run by default, album-aware
- `immich_explain_duplicate_group` - per-asset detail and a recommended keeper with rationale
- `immich_find_clip_dupes` - visual-but-not-byte-identical CLIP-grouped pairs (read-only)
- `immich_compare_assets` - side-by-side metadata diff of 2-10 assets with a recommendation
- `immich_audit_active` - reclaimable duplicate candidates still in the active library
- `immich_audit_trash` - cross-check trashed assets against the active library for orphans
### Stacks (4)
- `immich_list_stacks` - stack groups (motion photos, bracketed shots, RAW + JPEG pairs)
- `immich_create_stack`
- `immich_update_stack` - reassign the primary asset
- `immich_delete_stack` - assets stay *(requires `confirm: true`)*
### Trash (3)
- `immich_list_trash` - paginated trashed assets, scoped to trash
- `immich_restore_by_query` - restore trashed assets matching a filter *(no filter requires `confirm: true`)*
- `immich_empty_trash` - permanently delete EVERYTHING in trash, NOT REVERSIBLE *(requires `confirm: true`)*
### Jobs (2)
- `immich_list_jobs` - queue status for background jobs (thumbnails, ML, metadata, sidecar)
- `immich_run_job` - send a command to a background job *(destructive commands require `confirm: true`)*
## Example prompts
> Show me memories from this week.
Calls `immich_memories_today` / `immich_list_memories` and filters by the current week.
> Find duplicate photos and tell me which to delete (don't delete yet).
Calls `immich_categorize_duplicates` / `immich_list_duplicates` and returns clusters as a dry run, without resolving anything.
> Group these motion photos into a stack.
Calls `immich_create_stack` with the asset IDs and a primary pick.
> Make an album of every beach photo from last August.
Calls `immich_search_then_album` to search and create the album in one writes-gated step.
## Safety model
`immich-mcp` is designed to be safe to hand to an autonomous agent:
- **Reads are always allowed. Writes are opt-in.** Write and delete tools only register when `IMMICH_ALLOW_WRITES=true`.
- **Destructive calls require an explicit `confirm: true`.** Bulk updates, permanent deletes, merges, emptying trash, and similar refuse to run otherwise, even with writes enabled.
- **Dry-run by default for dedupe.** Resolution tools report what they would do before they touch anything.
- **Path-based upload is confined.** `immich_upload_asset_from_path` only reads inside `IMMICH_UPLOAD_BASE_DIR` (after resolving symlinks), and is refused entirely when that variable is unset.
- **Relaxed TLS is scoped.** `IMMICH_VERIFY_SSL=false` only relaxes the Immich client's dispatcher; it does not weaken certificate validation for the rest of the process.
## Install
The quickstart above uses `npx -y immich-mcp`, which needs no install. To install globally instead:
```bash
npm install -g immich-mcp
```
Or from source:
```bash
git clone https://github.com/lidless-labs/immich-mcp.git
cd immich-mcp
npm install
npm run build
```
When running from a source checkout, point your client's `command` at `node` and `args` at the absolute path to `dist/index.js`.
## Why not the other options?
- **The Immich web UI** is built for clicking through a grid. It is great for browsing and terrible for "do X across thousands of photos." This server is for the bulk, query-driven, agent-assisted work the UI does not make easy.
- **The Immich CLI** uploads and manages assets from scripts, but it is not an MCP server and an LLM cannot call it as typed tools. `immich-mcp` is the tool-call surface for an agent.
- **Other Immich MCP servers** tend to cover the basics (browse, search, albums). This one is broader: 74 tools spanning memories, motion-photo stacks, checksum-safe duplicate resolution, trash auditing, and job control, with a two-tier write-protection model built in.
- **Writing your own glue around the Immich REST API** works, but you would re-implement schema validation, pagination, write-gating, confirmation guards, and the dedupe safety logic that this server already ships and tests.
## What immich-mcp is not
- It is **not** a replacement for Immich. You still run your own Immich server; this connects an AI client to it.
- It is **not** a hosted service. It runs locally on stdio, launched by your MCP client. Nothing is sent anywhere except to the Immich server you point it at.
- It is **not** a photo editor or an ML model. It calls Immich's existing search, recognition, and dedup features; it does not run its own.
- It does **not** delete or modify anything unless you set `IMMICH_ALLOW_WRITES=true`, and the destructive tools still demand a per-call `confirm: true`.
- It is **not** an official Immich project. It is a community MCP server that uses the public Immich API.
## License
MIT - see [LICENSE](LICENSE).
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available