# Immich Photo Manager (pypi · immich-photo-manager)

Photo management for self-hosted Immich: search, albums, duplicates, faces, PDF reports. 94 tools.

- Trust score: 72/100 (medium)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-20

## Components

- oci · `ghcr.io/drolosoft/immich-photo-manager:2.0.11`: 10/100, [markdown](https://verifymcp.io/servers/drolosoft-immich-photo-manager/ghcr-io-drolosoft-immich-photo-manager-2-0-11.md), [page](https://verifymcp.io/servers/drolosoft-immich-photo-manager/ghcr-io-drolosoft-immich-photo-manager-2-0-11)
- pypi · `immich-photo-manager`: 72/100 (this document), [markdown](https://verifymcp.io/servers/drolosoft-immich-photo-manager/immich-photo-manager.md), [page](https://verifymcp.io/servers/drolosoft-immich-photo-manager/immich-photo-manager)

## Channel facts

- Registry: `pypi`
- Package: `immich-photo-manager`
- Version: `2.0.11`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-20.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it.
  - 2 of 37 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 35/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - License check failed: the license (MIT License) isn't a recognized OSI-approved license.
  - Actively maintained (last published 16 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 72/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 14205 tokens (~151/item across 94 items; 94 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 57/100
  - Stability observed for 17 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 71/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 0% of tool parameters carry a description.
  - Structured output schemas are declared (96% of tools); any adoption earns full credit.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 12 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "revert_asset_edits" implies "remove" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 95 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### How do I install the Immich Photo Manager MCP server?

Immich Photo Manager runs locally as a PyPI package, launched with uvx immich-photo-manager. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add drolosoft-immich-photo-manager -- uvx immich-photo-manager
```

### Cursor

```json
{
  "mcpServers": {
    "drolosoft-immich-photo-manager": {
      "command": "uvx",
      "args": [
        "immich-photo-manager"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "drolosoft-immich-photo-manager": {
      "command": "uvx",
      "args": [
        "immich-photo-manager"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add drolosoft-immich-photo-manager -- uvx immich-photo-manager
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "drolosoft-immich-photo-manager": {
      "type": "local",
      "command": [
        "uvx",
        "immich-photo-manager"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add drolosoft-immich-photo-manager --command uvx --arg immich-photo-manager
```

### Hermes

```yaml
mcp_servers:
  drolosoft-immich-photo-manager:
    command: "uvx"
    args: ["immich-photo-manager"]
```

### Netclaw

```json
{
  "McpServers": {
    "drolosoft-immich-photo-manager": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "immich-photo-manager"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add drolosoft-immich-photo-manager -t stdio -c uvx -a immich-photo-manager
```

### Other

```json
{
  "mcpServers": {
    "drolosoft-immich-photo-manager": {
      "command": "uvx",
      "args": [
        "immich-photo-manager"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-19 (score 72, +1)

No change was recorded against any check on this day. Stability & Change Management went from 50 to 53. That category is still filling its 30-day observation window: 15 days of observed history at the previous scan, 16 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-17 (score 71, +1)

No change was recorded against any check on this day. Stability & Change Management went from 43 to 47. That category is still filling its 30-day observation window: 13 days of observed history at the previous scan, 14 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-15 (score 70, +16)

- [security improvement] Malware scan: unverified → pass

### 2026-09-14 (score 54, −15)

- [security regression] Malware scan: pass → unverified

### 2026-09-13 (score 69, +1)

No change was recorded against any check on this day. Stability & Change Management went from 30 to 33. That category is still filling its 30-day observation window: 9 days of observed history at the previous scan, 10 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-11 (score 68, +4)

- [functional improvement] Stability: unverified → 0.27

### 2026-09-04 (score 64, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-09-03 (score 49)

First indexed and scored.

## MCP tools (94)

### `ping` (~44 tokens)

Check Immich server connectivity. Use this to verify the server is reachable
before running other operations. Read-only.

Returns: JSON with 'server' status ('pong' if healthy).

Output parameters:

- `result` (string)

### `get_server_version` (~45 tokens)

Get the Immich server version. Use this to check compatibility or report
the running server version. Read-only.

Returns: JSON with major, minor, and patch version numbers.

Output parameters:

- `result` (string)

### `get_capabilities` (~154 tokens)

What this Immich server can do: version, feature flags and known quirks.
Use this once at the start of a session to learn whether OCR, smart search or
facial recognition are available before offering them, and which behaviours
differ between Immich 2.x and 3.x. Read-only.

Returns: JSON with server_version, immich_major, features (the server's own
flags: ocr, smartSearch, facialRecognition, map, trash...) and quirks (plain
sentences about version-specific behaviour the caller should know). When the
API key may not read the feature flags, features is empty and a note says so;
the version and the quirks still come back.

Output parameters:

- `result` (string)

### `get_statistics` (~47 tokens)

Get library statistics. Use this for a quick overview of library size
without listing individual assets. Read-only.

Returns: JSON with total photo count, video count, and storage usage in bytes.

Output parameters:

- `result` (string)

### `update_credentials` (~133 tokens)

Update the Immich connection credentials. Use this when the API key has been
rotated or the server URL changed. Validates credentials before applying.
Side effect: persists new credentials to disk and hot-swaps the live connection.

Args:
    base_url: Full Immich server URL including protocol (e.g. 'https://photos.example.com').
    api_key: A valid Immich API key (generated in Immich > User Settings > API Keys).

Returns: JSON with success status, photo/video counts confirming access, and persistence path.

Input parameters:

- `api_key` (string, required)
- `base_url` (string, required)

Output parameters:

- `result` (string)

### `get_asset_info` (~171 tokens)

Get full metadata for a single asset. Use this when you need EXIF details,
GPS coordinates, camera info, or file properties for a known asset ID.
For finding assets, use search_metadata or search_smart instead. Read-only.

Args:
    asset_id: The asset's UUID (from search results, album listings, or list_assets).
    with_notes: Also include the plugin's notes on the asset (past review
        verdicts and recorded actions, see get_asset_notes). One extra request.

Returns: JSON with EXIF data, GPS, dates, dimensions, file size, camera
make/model, and owner; plus a `notes` object (reviews and actions) when
with_notes is true.

Input parameters:

- `asset_id` (string, required)
- `with_notes` (boolean)

Output parameters:

- `result` (string)

### `update_asset_metadata` (~253 tokens)

Update metadata fields on a specific asset. Use this to fix dates, correct GPS,
add descriptions, or change favorite/rating status. Only provided fields are modified.
Side effect: permanently changes asset metadata in Immich.

Args:
    asset_id: The asset's UUID.
    date_time_original: ISO 8601 datetime (e.g. '2019-07-14T15:23:41.000Z').
    latitude: GPS latitude, decimal degrees (-90.0 to 90.0).
    longitude: GPS longitude, decimal degrees (-180.0 to 180.0).
    description: Free-text description/caption for the asset.
    is_favorite: Set favorite status (true/false).
    rating: -1 to reject the photo, or 1 to 5 stars. A rating cannot be
        cleared from here, and 0 is not a rating Immich 3.x accepts.

Returns: JSON with the updated asset object.

Input parameters:

- `asset_id` (string, required)
- `date_time_original` (string)
- `description` (string)
- `is_favorite`
- `latitude`
- `longitude`
- `rating`

Output parameters:

- `result` (string)

### `update_assets_metadata` (~243 tokens)

Update the same metadata fields on many assets in ONE call — the whole
roll of a scanned album gets its real date, a trip's photos get their GPS,
a selection becomes favorites. Same fields as update_asset_metadata; only
the provided ones change. Side effect: permanently changes the metadata of
every listed asset.

Args:
    asset_ids: The assets to update.
    date_time_original: ISO 8601 datetime applied to all of them.
    latitude: GPS latitude, decimal degrees.
    longitude: GPS longitude, decimal degrees.
    description: Description/caption applied to all of them.
    is_favorite: Set favorite status on all of them.
    rating: -1 to reject them, or 1 to 5 stars. A rating cannot be cleared
        from here, and 0 is not a rating Immich 3.x accepts.

Returns: JSON with success and the number of assets updated.

Input parameters:

- `asset_ids` (array, required)
- `date_time_original` (string)
- `description` (string)
- `is_favorite`
- `latitude`
- `longitude`
- `rating`

Output parameters:

- `result` (string)

### `rotate_assets` (~179 tokens)

Apply a non-destructive clockwise rotation to one or more assets. Use this to
fix orientation issues. The original file is never modified — rotation is a display
transform only. Use revert_asset_edits to undo. Provide EITHER asset_ids OR album_id.
Side effect: writes rotation edits to Immich; accumulates with existing rotation.

Args:
    angle: Clockwise degrees, must be a multiple of 90 (90, 180, or 270). Default: 90.
    asset_ids: List of asset UUIDs to rotate. Mutually exclusive with album_id.
    album_id: Rotate all assets in this album. Mutually exclusive with asset_ids.

Returns: JSON with rotated/failed counts and the applied angle.

Input parameters:

- `album_id` (string)
- `angle` (integer)
- `asset_ids`

Output parameters:

- `result` (string)

### `revert_asset_edits` (~126 tokens)

Remove all non-destructive edits (rotation, crop, mirror) from assets, restoring
original appearance. Use this to undo rotate_assets or any other display transforms.
Provide EITHER asset_ids OR album_id. Side effect: deletes all edit records for the assets.

Args:
    asset_ids: List of asset UUIDs to revert. Mutually exclusive with album_id.
    album_id: Revert all assets in this album. Mutually exclusive with asset_ids.

Returns: JSON with reverted/failed counts.

Input parameters:

- `album_id` (string)
- `asset_ids`

Output parameters:

- `result` (string)

### `get_map_markers` (~150 tokens)

Get GPS map markers for all geotagged assets. Use this to discover where photos
were taken or to build travel maps. For searching by city/country name, use
search_metadata instead. Read-only. Returns up to 500 markers.

Args:
    file_created_after: ISO date lower bound (e.g. '2023-01-01').
    file_created_before: ISO date upper bound.
    is_favorite: If true, only return favorites.

Returns: JSON with total count and markers array (each with asset ID, lat, lon).

Input parameters:

- `file_created_after` (string)
- `file_created_before` (string)
- `is_favorite`

Output parameters:

- `result` (string)

### `reverse_geocode` (~109 tokens)

Resolve GPS coordinates to a place name using Immich's own offline geodata.
Use this to name the location of a marker from get_map_markers or of an asset's
EXIF coordinates — no external service is contacted. Read-only.

Args:
    lat: Latitude in decimal degrees.
    lon: Longitude in decimal degrees.

Returns: JSON with total and a places array of {city, state, country} candidates.

Input parameters:

- `lat` (number, required)
- `lon` (number, required)

Output parameters:

- `result` (string)

### `search_metadata` (~444 tokens)

Search assets by EXIF metadata fields. Use this when you know specific criteria
like city, camera model, or date range. For natural language visual queries (e.g.
'sunset at the beach'), use search_smart instead. For browsing without criteria,
use list_assets. Read-only.

Args:
    city: City name from EXIF GPS reverse-geocoding (case-sensitive, e.g. 'Barcelona').
    state: State or region name.
    country: Country name (e.g. 'Spain', 'Egypt').
    make: Camera manufacturer (e.g. 'Apple', 'Canon', 'Sony').
    model: Camera model string (e.g. 'iPhone 14 Pro', 'EOS R5').
    taken_after: ISO date — return only assets captured after this date.
    taken_before: ISO date — return only assets captured before this date.
    is_favorite: If true, only return favorites.
    asset_type: 'IMAGE' or 'VIDEO'. Omit for both.
    ocr: Text recognized inside the image (tickets, signs, documents). Needs
        OCR enabled on the server — check with get_capabilities.
    person_ids: Only assets showing ALL of these people (ids from list_people).
    tag_ids: Only assets carrying these tags (ids from list_tags).
    album_ids: Only assets inside these albums.
    page: Page number, starting from 1 (default 1).
    size: Results per page (1-200, default 50).

Returns: JSON with total match count, current page, and assets array with IDs, filenames, and dates.

Input parameters:

- `album_ids`
- `asset_type` (string)
- `city` (string)
- `country` (string)
- `is_favorite`
- `make` (string)
- `model` (string)
- `ocr` (string)
- `page` (integer)
- `person_ids`
- `size` (integer)
- `state` (string)
- `tag_ids`
- `taken_after` (string)
- `taken_before` (string)

Output parameters:

- `result` (string)

### `search_explore` (~145 tokens)

Overview of what the library contains, grouped by explore field: one
representative asset per city and per detected concept (Immich's Explore page).
Use this to get oriented in an unknown library before searching for anything
specific — it answers 'what is in here?' in one call. A city only appears once
it holds at least 5 assets (Immich's own threshold), so small libraries can
come back empty. Read-only.

Returns: JSON with total (how many fields came back) and a fields array; each
field has its name (e.g. 'exifInfo.city') and items pairing each value with one
representative asset_id.

Output parameters:

- `result` (string)

### `search_cities` (~75 tokens)

Every city that appears in the library, one representative asset each.
Unlike search_explore this has no minimum-asset threshold, so it is the
reliable way to answer 'which places are in this library?'. Read-only.

Returns: JSON with a cities array of {city, country, asset_id, date}.

Output parameters:

- `result` (string)

### `search_places` (~99 tokens)

Look a place name up in Immich's built-in gazetteer (no assets involved).
Use this to resolve a spelling or get coordinates for a place before a
geographic search. Read-only.

Args:
    name: Place name to look for (e.g. 'Lisbon').

Returns: JSON with a places array of {name, admin1name, admin2name, latitude,
longitude}.

Input parameters:

- `name` (string, required)

Output parameters:

- `result` (string)

### `search_suggestions` (~188 tokens)

Distinct values present in the library for one field — the exact spellings
search_metadata expects. Use this before filtering by city or camera to avoid
guessing (e.g. 'iPhone 14 Pro' vs 'iPhone14,3'). Read-only.

Args:
    suggestion_type: One of 'country', 'state', 'city', 'camera-make',
        'camera-model', 'camera-lens-model'.
    country: Narrow city/state suggestions to this country.
    state: Narrow city suggestions to this state.
    make: Narrow model suggestions to this camera make.
    model: Narrow lens suggestions to this camera model.

Returns: JSON with total and a suggestions array of strings.

Input parameters:

- `country` (string)
- `make` (string)
- `model` (string)
- `state` (string)
- `suggestion_type` (string, required)

Output parameters:

- `result` (string)

### `search_random` (~195 tokens)

Random assets from the library, optionally filtered. Use this for sampling —
a quick feel of what a filter matches, a surprise pick for a story, or spot
checks over a big library. Read-only.

Args:
    size: How many random assets to return (default 10, max 100).
    city: Only assets from this city.
    country: Only assets from this country.
    make: Only assets from this camera make.
    model: Only assets from this camera model.
    is_favorite: If true, only favorites.
    ocr: Only assets whose recognized text matches (needs OCR on the server).

Returns: JSON with the matching assets array.

Input parameters:

- `city` (string)
- `country` (string)
- `is_favorite`
- `make` (string)
- `model` (string)
- `ocr` (string)
- `size` (integer)

Output parameters:

- `result` (string)

### `search_statistics` (~295 tokens)

Count how many assets match a filter WITHOUT fetching them. Use this instead
of search_metadata whenever only the number matters ('how many photos from
Spain?', 'how many did I take in 2019?') — it costs one integer instead of
pages of assets. Read-only.

Args:
    city: Count assets from this city.
    country: Count assets from this country.
    state: Count assets from this state/region.
    make: Count assets from this camera make.
    model: Count assets from this camera model.
    is_favorite: If true, count only favorites.
    ocr: Count assets whose recognized text matches (needs OCR on the server).
    created_after: ISO date lower bound on upload date (when it reached Immich).
    created_before: ISO date upper bound on upload date.
    taken_after: ISO date lower bound on capture date (when the photo was taken).
    taken_before: ISO date upper bound on capture date.

Returns: JSON {total}.

Input parameters:

- `city` (string)
- `country` (string)
- `created_after` (string)
- `created_before` (string)
- `is_favorite`
- `make` (string)
- `model` (string)
- `ocr` (string)
- `state` (string)
- `taken_after` (string)
- `taken_before` (string)

Output parameters:

- `result` (string)

### `search_large_assets` (~145 tokens)

The biggest files in the library, largest first. Use this to find what is
eating storage before a cleanup — videos and originals show up immediately.
Read-only.

Args:
    min_size_mb: Only assets at least this many megabytes (0 = no minimum).
    size: How many assets to return (1-200, default 20).
    asset_type: 'IMAGE' or 'VIDEO'. Omit for both.

Returns: JSON with total and an assets array of {asset_id, filename, size_mb,
date}, largest first.

Input parameters:

- `asset_type` (string)
- `min_size_mb` (integer)
- `size` (integer)

Output parameters:

- `result` (string)

### `search_smart` (~351 tokens)

AI-powered visual search using CLIP embeddings. Use this when describing what a
photo looks like in natural language (e.g. 'sunset at the beach', 'dog playing fetch').
For structured criteria (city, camera, date), use search_metadata instead. Requires
Immich ML service with Smart Search enabled. Read-only.

Args:
    query: Natural language description of the visual content to find.
    city: Optional city filter to narrow results geographically.
    state: Optional state/region filter.
    country: Optional country filter.
    taken_after: ISO date — only assets captured after this date.
    taken_before: ISO date — only assets captured before this date.
    ocr: Text recognized inside the image, combined with the visual query.
        Needs OCR enabled on the server — check with get_capabilities.
    person_ids: Only assets showing ALL of these people (ids from list_people).
    tag_ids: Only assets carrying these tags (ids from list_tags).
    album_ids: Only assets inside these albums.
    page: Page number, starting from 1 (default 1).
    size: Results per page (1-200, default 50).

Returns: JSON with total count, page, and assets ranked by visual similarity to the query.

Input parameters:

- `album_ids`
- `city` (string)
- `country` (string)
- `ocr` (string)
- `page` (integer)
- `person_ids`
- `query` (string, required)
- `size` (integer)
- `state` (string)
- `tag_ids`
- `taken_after` (string)
- `taken_before` (string)

Output parameters:

- `result` (string)

### `list_albums` (~92 tokens)

List all albums in the library with summary info. Use this to discover existing
albums before creating new ones or to find an album ID. Read-only.

Args:
    shared: true = only shared albums, false = only non-shared, omit = all albums.

Returns: JSON with total count and albums array (each with id, name, description, assetCount, shared status).

Input parameters:

- `shared`

Output parameters:

- `result` (string)

### `get_album` (~136 tokens)

Get full details for a specific album including all its asset IDs. Use this to
inspect album contents or retrieve asset IDs for further operations (thumbnails,
metadata, rotation). For listing all albums, use list_albums instead. Read-only.

Args:
    album_id: The album's UUID (from list_albums or create_album).

Returns: JSON with album metadata, a flat list of all asset_ids, and an assets array
(id, filename, type, date, recognized people) so "who appears in this album / who
repeats" can be answered without further calls.

Input parameters:

- `album_id` (string, required)

Output parameters:

- `result` (string)

### `create_album` (~123 tokens)

Create a new album, optionally pre-populated with assets. Use this to organize
photos into collections. Side effect: creates a new album in Immich.

Args:
    name: Album display name (e.g. 'Roma, Italia', 'Birthday 2024').
    description: Optional album description text.
    asset_ids: Optional list of asset UUIDs to add immediately on creation.

Returns: JSON with the new album's id, name, and asset count.

Input parameters:

- `asset_ids`
- `description` (string)
- `name` (string, required)

Output parameters:

- `result` (string)

### `update_album` (~108 tokens)

Update an album's name or description. Use this to rename or re-describe an
existing album. Side effect: modifies album metadata in Immich.

Args:
    album_id: The album's UUID.
    name: New album name. Leave empty to keep current name.
    description: New description. Leave empty to keep current description.

Returns: JSON with the updated album object.

Input parameters:

- `album_id` (string, required)
- `description` (string)
- `name` (string)

Output parameters:

- `result` (string)

### `delete_album` (~86 tokens)

Delete an album container. The photos inside are NOT deleted — they remain in
the library. Use this to remove unwanted album groupings. Side effect: permanently
deletes the album (cannot be undone).

Args:
    album_id: The album's UUID to delete.

Returns: JSON with success, the deleted album's id and album_id.

Input parameters:

- `album_id` (string, required)

Output parameters:

- `result` (string)

### `add_assets_to_album` (~105 tokens)

Add existing assets to an album. Use this to curate albums from search results
or other asset lists. Assets can belong to multiple albums simultaneously.
Side effect: modifies album membership.

Args:
    album_id: Target album UUID.
    asset_ids: List of asset UUIDs to add to the album.

Returns: JSON with album_id, count added, and per-asset success/error details.

Input parameters:

- `album_id` (string, required)
- `asset_ids` (array, required)

Output parameters:

- `result` (string)

### `remove_assets_from_album` (~108 tokens)

Remove assets from an album without deleting them. The photos remain in the
library and other albums. Use this to un-curate mistakenly added assets.
Side effect: modifies album membership.

Args:
    album_id: Album UUID to remove assets from.
    asset_ids: List of asset UUIDs to remove from this album.

Returns: JSON with album_id, count removed, and per-asset result details.

Input parameters:

- `album_id` (string, required)
- `asset_ids` (array, required)

Output parameters:

- `result` (string)

### `get_asset_thumbnail` (~141 tokens)

Get a base64-encoded thumbnail image for a single asset. Use this to visually
inspect one photo. For multiple photos, use get_thumbnails_batch (by IDs) or
get_album_thumbnails (by album). Read-only.

Args:
    asset_id: The asset's UUID.
    size: 'thumbnail' (250px, fast) or 'preview' (1440px, higher quality). Default: 'thumbnail'.

Returns: JSON with 'data' (base64 string) and 'type' (MIME type, e.g. 'image/jpeg').

Input parameters:

- `asset_id` (string, required)
- `size` (string)

Output parameters:

- `result` (string)

### `get_album_thumbnails` (~150 tokens)

Get base64-encoded thumbnails for photos in an album. Use this to generate visual
HTML galleries from an existing album. For thumbnails from search results (no album),
use get_thumbnails_batch instead. Read-only.

Args:
    album_id: The album's UUID.
    size: 'thumbnail' (250px) or 'preview' (1440px). Default: 'thumbnail'.
    limit: Max thumbnails to return (1-50, default 20).

Returns: JSON with album info and thumbnails array (each with asset_id, base64 data, filename, date).

Input parameters:

- `album_id` (string, required)
- `limit` (integer)
- `size` (string)

Output parameters:

- `result` (string)

### `get_thumbnails_batch` (~173 tokens)

Get base64-encoded thumbnails for arbitrary asset IDs without needing an album.
Use this to visually display search results or any ad-hoc set of photos. For album-based
thumbnails, use get_album_thumbnails. For a single photo, use get_asset_thumbnail. Read-only.

Args:
    asset_ids: List of asset UUIDs to fetch thumbnails for.
    size: 'thumbnail' (250px) or 'preview' (1440px). Default: 'thumbnail'.
    limit: Max thumbnails to return (1-50, default 20). Only the first N IDs are fetched.

Returns: JSON with thumbnails array (each with asset_id, base64 data, filename, date).

Input parameters:

- `asset_ids` (array, required)
- `limit` (integer)
- `size` (string)

Output parameters:

- `result` (string)

### `get_asset_image` (~140 tokens)

Get a single asset's thumbnail as an image block for inline visual display.
Use this in clients that render images (Open WebUI, Claude Desktop). For HTML
gallery generation with base64 data URIs (Cowork/skills), use get_asset_thumbnail
instead — it returns JSON. Read-only.

Args:
    asset_id: The asset's UUID.
    size: 'thumbnail' (250px, fast) or 'preview' (1440px, higher quality). Default: 'thumbnail'.

Returns: An image block (MCP ImageContent) for visual display.

Input parameters:

- `asset_id` (string, required)
- `size` (string)

### `get_album_images` (~157 tokens)

Get an album's thumbnails as image blocks for inline visual display. Use this
to visually browse an album in clients that render images. For HTML gallery
generation with base64 data URIs (Cowork/skills), use get_album_thumbnails
instead — it returns JSON with filenames and dates. Read-only.

Args:
    album_id: The album's UUID.
    size: 'thumbnail' (250px) or 'preview' (1440px). Default: 'thumbnail'.
    limit: Max thumbnails to return (1-50, default 20).

Returns: A list of image blocks suitable for visual display.

Input parameters:

- `album_id` (string, required)
- `limit` (integer)
- `size` (string)

### `get_images_batch` (~170 tokens)

Get thumbnails for arbitrary asset IDs as image blocks for inline visual
display. Use this to visually show search results in clients that render images.
For HTML gallery generation with base64 data URIs (Cowork/skills), use
get_thumbnails_batch instead — it returns JSON with filenames and dates. Read-only.

Args:
    asset_ids: List of asset UUIDs to fetch thumbnails for.
    size: 'thumbnail' (250px) or 'preview' (1440px). Default: 'thumbnail'.
    limit: Max thumbnails to return (1-50, default 20). Only the first N IDs are fetched.

Returns: A list of image blocks suitable for visual display.

Input parameters:

- `asset_ids` (array, required)
- `limit` (integer)
- `size` (string)

### `get_video_frames` (~428 tokens)

Get frames of a video as image blocks, to "watch" a clip. Immich keeps one
poster per video; this downloads the video and cuts frames locally (PyAV, a
dependency since 1.7.1, or ffmpeg on PATH). Every frame is one
image for the model. Workflow: 6 frames first; to look closer, narrow with
start/end or use interval (down to 1 s). Above 12 frames the tool returns a JSON
plan with frames_planned and estimated_tokens instead of images: show it to the
user and call again with confirm=true only if they agree. Hard cap 120 per call.
For base64 JSON with timestamps use get_video_frames_json. Read-only.

Args:
    asset_id: The video asset's UUID.
    count: Frames evenly spaced over the segment (default 6). Ignored when interval > 0.
    size: 'thumbnail' (250px, ~1.6k tokens per frame) or 'preview' (1440px, ~6.4k). Default 'thumbnail'.
    start: Segment start in seconds (default 0).
    end: Segment end in seconds (0 = to the end).
    interval: One frame every N seconds instead of count (1 = one per second, the maximum granularity).
    confirm: Required (true) when more than 12 frames would be produced; ask the user first.
    sheet: Pack the frames into contact sheets (30 per image, timestamps burned in):
        a long video becomes one or two images instead of dozens, so no
        confirmation is needed. Use it to skim, then cut the moments that matter.

Returns: JPEG image blocks in time order, or JSON (confirmation plan / error).

Input parameters:

- `asset_id` (string, required)
- `confirm` (boolean)
- `count` (integer)
- `end` (number)
- `interval` (number)
- `sheet` (boolean)
- `size` (string)
- `start` (number)

### `get_video_frames_json` (~290 tokens)

Frames of a video as base64 JPEG with timestamps, for HTML galleries and
skills. Same parameters, gate (confirm above 12) and cap (120) as get_video_frames. Read-only.

Args:
    asset_id: The video asset's UUID.
    count: Frames evenly spaced over the segment (default 6). Ignored when interval > 0.
    size: 'thumbnail' (250px, ~1.6k tokens per frame) or 'preview' (1440px, ~6.4k). Default 'thumbnail'.
    start: Segment start in seconds (default 0).
    end: Segment end in seconds (0 = to the end).
    interval: One frame every N seconds instead of count (1 = one per second, the maximum granularity).
    confirm: Required (true) when more than 12 frames would be produced; ask the user first.

Returns: JSON {asset_id, duration, backend, count, frames:[{timestamp, data, type}]},
a confirmation plan {confirm_required, frames_planned, estimated_tokens, ...}, or {"error": ...}.

Input parameters:

- `asset_id` (string, required)
- `confirm` (boolean)
- `count` (integer)
- `end` (number)
- `interval` (number)
- `size` (string)
- `start` (number)

Output parameters:

- `result` (string)

### `list_shared_links` (~64 tokens)

List all shared links (public gallery URLs). Use this to see what's currently
shared publicly or to find a link ID for updates/deletion. Read-only.

Returns: JSON with total count and links array (each with id, key, type, description, album info).

Output parameters:

- `result` (string)

### `create_shared_link` (~148 tokens)

Create a public shared link for an album, making it accessible via URL without
authentication. Use this to publish a gallery for external viewing.
Side effect: creates a publicly accessible URL.

Args:
    album_id: The album UUID to share publicly.
    allow_download: Allow visitors to download original files (default true).
    show_metadata: Show EXIF data to visitors (default true).
    description: Optional human-readable description for the link.

Returns: JSON with link id, key, album_id, and the full shareable URL.

Input parameters:

- `album_id` (string, required)
- `allow_download` (boolean)
- `description` (string)
- `show_metadata` (boolean)

Output parameters:

- `result` (string)

### `get_shared_link` (~81 tokens)

Get full details of a shared link including permissions, expiry, and linked assets.
Use this to inspect a specific link's configuration. Read-only.

Args:
    link_id: The shared link's UUID (from list_shared_links).

Returns: JSON with link details, permissions, expiry date, and associated assets/album.

Input parameters:

- `link_id` (string, required)

Output parameters:

- `result` (string)

### `update_shared_link` (~168 tokens)

Update a shared link's permissions or expiry. Use this to tighten/loosen access
or set an expiration date. Side effect: changes public link behavior immediately.

Args:
    link_id: The shared link's UUID.
    allow_download: Allow visitors to download original files.
    show_metadata: Show EXIF data to visitors.
    allow_upload: Allow visitors to upload photos to the shared album.
    description: Link description. Empty string clears it.
    expiry_at: ISO 8601 expiry datetime. Empty string removes expiry (link never expires).

Returns: JSON with the updated shared link object.

Input parameters:

- `allow_download`
- `allow_upload`
- `description`
- `expiry_at`
- `link_id` (string, required)
- `show_metadata`

Output parameters:

- `result` (string)

### `delete_shared_link` (~73 tokens)

Delete (revoke) a shared link, making the public URL immediately inaccessible.
The album and its photos are unaffected. Side effect: permanently removes the link.

Args:
    link_id: The shared link's UUID to delete.

Returns: JSON with deleted confirmation and link_id.

Input parameters:

- `link_id` (string, required)

Output parameters:

- `result` (string)

### `get_connection_info` (~87 tokens)

Return the Immich base URL and a masked API key. Use this to populate gallery
template placeholders (e.g. {{IMMICH_URL}}). The API key is intentionally masked
for security — thumbnails use base64 data URIs, not direct API calls. Read-only.

Returns: JSON with base_url and api_key_masked (first 8 + last 4 chars only).

Output parameters:

- `result` (string)

### `list_people` (~138 tokens)

List all recognized people (face clusters) in the library. Use this to browse
who appears in the photo library or find a person's ID. For searching by name,
use search_people instead. Read-only.

Args:
    page: Page number, starting from 1 (default 1).
    size: Results per page (default 50).
    with_hidden: Include people marked as hidden (default false).

Returns: JSON with total count, page, and people array (each with id, name, thumbnailPath, photoCount).

Input parameters:

- `page` (integer)
- `size` (integer)
- `with_hidden` (boolean)

Output parameters:

- `result` (string)

### `get_person` (~89 tokens)

Get full details for a specific person including name, birth date, and photo count.
Use this after finding a person via list_people or search_people. Read-only.

Args:
    person_id: The person's UUID (from list_people or search_people).

Returns: JSON with person details (id, name, birthDate, isHidden, photoCount, thumbnailPath).

Input parameters:

- `person_id` (string, required)

Output parameters:

- `result` (string)

### `update_person` (~216 tokens)

Update a person's profile details. Use this to name unnamed faces, set birth dates,
hide clutter faces, or change the representative thumbnail. Only provided fields are
modified. Side effect: changes person metadata in Immich.

Args:
    person_id: The person's UUID.
    name: Display name (e.g. 'John Smith'). Set to name unnamed face clusters.
    birth_date: ISO date (e.g. '1990-05-15').
    is_hidden: Hide from the People view (useful for strangers/clutter).
    is_favorite: Mark as a favorite person.
    feature_face_asset_id: Asset UUID whose face crop becomes the person's thumbnail.
    color: Hex color label for UI grouping.

Returns: JSON with the updated person object.

Input parameters:

- `birth_date` (string)
- `color` (string)
- `feature_face_asset_id` (string)
- `is_favorite`
- `is_hidden`
- `name` (string)
- `person_id` (string, required)

Output parameters:

- `result` (string)

### `merge_people` (~214 tokens)

Merge multiple person clusters into one. Use this when the same real person has
been split into multiple face clusters. DESTRUCTIVE and IRREVERSIBLE: merged persons
are permanently deleted and all their faces transfer to the target. Without
confirm=true nothing happens: the call returns who would be kept and who would
disappear, so the user can check the names before the merge. Side effect: with
confirm=true, permanently deletes the merged persons; without it the call only
previews and changes nothing.

Args:
    person_id: The target person UUID to keep (receives all merged faces).
    merge_ids: List of person UUIDs to absorb into the target. These persons are permanently deleted.
    confirm: Pass true only after the user has seen the preview and agreed.

Returns: JSON with the preview (confirm_required, keep, merge, failed) or the
merge result.

Input parameters:

- `confirm` (boolean)
- `merge_ids` (array, required)
- `person_id` (string, required)

Output parameters:

- `result` (string)

### `search_people` (~100 tokens)

Search for people by name (partial match). Use this when you know the person's
name. For browsing all people, use list_people instead. Read-only.

Args:
    name: Full or partial name to match (case-insensitive).
    with_hidden: Include hidden people in results (default false).

Returns: JSON array of matching people with id, name, and photo count.

Input parameters:

- `name` (string, required)
- `with_hidden` (boolean)

Output parameters:

- `result` (string)

### `get_person_thumbnail` (~81 tokens)

Get a base64-encoded face crop thumbnail for a person. Use this to visually
identify a person before merging or renaming. Read-only.

Args:
    person_id: The person's UUID.

Returns: JSON with 'data' (base64 string of face crop) and 'type' (MIME type).

Input parameters:

- `person_id` (string, required)

Output parameters:

- `result` (string)

### `get_asset_faces` (~87 tokens)

Get all detected faces in a photo with their person assignments. Use this to see
who is in a specific photo or to find face IDs for reassign_face. Read-only.

Args:
    asset_id: The asset's UUID.

Returns: JSON array of face detections (each with face_id, person_id, person_name, bounding box).

Input parameters:

- `asset_id` (string, required)

Output parameters:

- `result` (string)

### `reassign_face` (~120 tokens)

Reassign a detected face to a different person. Use this to correct face recognition
mistakes (e.g. a face wrongly attributed to Person A should be Person B). Get face_id
from get_asset_faces first. Side effect: permanently changes face-to-person mapping.

Args:
    face_id: The face detection UUID (from get_asset_faces results).
    person_id: The correct person UUID to assign this face to.

Returns: JSON with the updated face assignment.

Input parameters:

- `face_id` (string, required)
- `person_id` (string, required)

Output parameters:

- `result` (string)

### `delete_assets` (~129 tokens)

Delete assets (soft-delete to trash or permanent). Use this to remove unwanted
photos/videos. Default is soft-delete (recoverable via restore_assets). With force=true,
deletion is PERMANENT and IRREVERSIBLE. Side effect: moves/deletes assets.

Args:
    asset_ids: List of asset UUIDs to delete.
    force: false (default) = move to trash (recoverable). true = PERMANENTLY delete (no undo).

Returns: JSON with count deleted and whether force was used.

Input parameters:

- `asset_ids` (array, required)
- `force` (boolean)

Output parameters:

- `result` (string)

### `empty_trash` (~76 tokens)

Permanently delete ALL assets currently in trash. DESTRUCTIVE and IRREVERSIBLE.
Use this only after confirming the user wants to purge all trashed items. For
deleting specific assets, use delete_assets instead. Side effect: permanently
destroys all trashed assets and frees storage.

Returns: JSON with success confirmation.

Output parameters:

- `result` (string)

### `restore_trash` (~61 tokens)

Restore ALL trashed assets back to the library. Use this to undo an accidental
bulk deletion. For restoring specific assets only, use restore_assets instead.
Side effect: moves all trashed assets back to the active library.

Returns: JSON with success confirmation.

Output parameters:

- `result` (string)

### `restore_assets` (~86 tokens)

Restore specific assets from trash back to the active library. Use this to
selectively recover accidentally deleted photos. For restoring everything at once,
use restore_trash instead. Side effect: moves specified assets out of trash.

Args:
    asset_ids: List of asset UUIDs currently in trash to restore.

Returns: JSON with count of restored assets.

Input parameters:

- `asset_ids` (array, required)

Output parameters:

- `result` (string)

### `get_duplicates` (~140 tokens)

Get ML-detected duplicate asset groups (same image stored more than once). Use this
to review potential duplicates before resolving them with resolve_duplicates. Requires
Immich ML service. Note: "duplicates" means the same picture, not the same person —
for people use get_album (assets[].people) or get_asset_faces. Read-only.

Args:
    album_id: Optional. Restrict to groups that touch this album; each group then also
        reports which of its assets are inside/outside the album.

Returns: JSON array of duplicate groups (each with duplicateId, assets array, and similarity scores).

Input parameters:

- `album_id` (string)

Output parameters:

- `result` (string)

### `resolve_duplicates` (~106 tokens)

Resolve duplicate groups by choosing which assets to keep and which to trash.
Use this after reviewing results from get_duplicates. Trashed assets can still be
recovered via restore_assets. Side effect: moves rejected duplicates to trash.

Args:
    groups: List of dicts, each with: duplicateId (from get_duplicates), assetIds (UUIDs to KEEP), trashIds (UUIDs to TRASH).

Returns: JSON with count of resolved groups.

Input parameters:

- `groups` (array, required)

Output parameters:

- `result` (string)

### `list_tags` (~57 tokens)

List all tags in the library. Use this to discover existing tags before creating
new ones or to find a tag ID for tagging operations. Read-only.

Returns: JSON with total count and tags array (each with id, name, color).

Output parameters:

- `result` (string)

### `get_tag` (~65 tokens)

Get details for a specific tag. Use this to inspect a tag's properties. Read-only.

Args:
    tag_id: The tag's UUID (from list_tags).

Returns: JSON with tag id, name, color, and usage count.

Input parameters:

- `tag_id` (string, required)

Output parameters:

- `result` (string)

### `create_tag` (~109 tokens)

Create a new tag for categorizing assets. Use list_tags first to avoid duplicates.
Side effect: creates a new tag in Immich.

Args:
    name: Tag display name (e.g. 'Vacation', 'Family', 'Work'). Must be unique.
    color: Optional hex color for the tag (e.g. '#FF5733').

Returns: JSON with the new tag's id, name, and color.

Input parameters:

- `color` (string)
- `name` (string, required)

Output parameters:

- `result` (string)

### `update_tag` (~141 tokens)

Update a tag's color. Side effect: changes apply to all assets using this tag.
Immich's API cannot rename a tag (TagUpdateDto only carries `color`); to rename,
create_tag with the new name, tag_assets, then delete_tag the old one.

Args:
    tag_id: The tag's UUID.
    name: Not supported by Immich — passing it returns an error explaining the workaround.
    color: New hex color (e.g. '#FF5733'). Omit to keep current.

Returns: JSON with the updated tag object.

Input parameters:

- `color`
- `name`
- `tag_id` (string, required)

Output parameters:

- `result` (string)

### `delete_tag` (~72 tokens)

Delete a tag and remove it from all assets. The assets themselves are unaffected.
Side effect: permanently deletes the tag (cannot be undone).

Args:
    tag_id: The tag's UUID to delete.

Returns: JSON with success, the deleted tag's id and tag_id.

Input parameters:

- `tag_id` (string, required)

Output parameters:

- `result` (string)

### `tag_assets` (~111 tokens)

Apply a tag to multiple assets at once. Use this to bulk-categorize photos
(e.g. tag all vacation photos). Side effect: adds tag association to assets.

Args:
    tag_id: The tag UUID to apply (from list_tags or create_tag).
    asset_ids: List of asset UUIDs to tag. Must not be empty.

Returns: JSON with tag_id, count tagged, and per-asset results.

Input parameters:

- `asset_ids` (array, required)
- `tag_id` (string, required)

Output parameters:

- `result` (string)

### `untag_assets` (~104 tokens)

Remove a tag from multiple assets. The tag itself remains; only the association is
removed. Side effect: removes tag-to-asset links.

Args:
    tag_id: The tag UUID to remove from assets.
    asset_ids: List of asset UUIDs to untag. Must not be empty.

Returns: JSON with tag_id, count untagged, and per-asset results.

Input parameters:

- `asset_ids` (array, required)
- `tag_id` (string, required)

Output parameters:

- `result` (string)

### `upload_asset` (~163 tokens)

Upload a local photo or video file to Immich. Use this to ingest new media into
the library. Constraints: max 25MB, allowed types: jpg, jpeg, png, heic, mp4, mov,
gif, webp. Symlinks are rejected for security. The original file is NOT modified or
deleted. Side effect: creates a new asset in Immich.

Args:
    file_path: Absolute path to the local file (e.g. '/tmp/photo.jpg'). Must exist.
    album_id: Optional album UUID to add the uploaded asset to immediately.

Returns: JSON with new asset id, filename, size_mb, and album assignment status if applicable.

Input parameters:

- `album_id` (string)
- `file_path` (string, required)

Output parameters:

- `result` (string)

### `list_assets` (~246 tokens)

List assets with simple filters (no search query needed). Use this to browse
the library by status (favorites, archived, trashed) or type. For finding specific
content, use search_metadata (structured) or search_smart (visual AI). Read-only.

Args:
    is_favorite: true = only favorites, false = only non-favorites, omit = all.
    is_archived: true = only archived, false = only non-archived, omit = all.
    is_trashed: true = only trashed items; false/omit = active library (Immich never mixes both).
    asset_type: 'IMAGE' or 'VIDEO'. Omit for both.
    page: Page number, starting from 1 (default 1).
    size: Results per page (1-200, default 50).

Returns: JSON with total count, current page, and assets array with IDs, filenames, dates, and types.

Input parameters:

- `asset_type` (string)
- `is_archived`
- `is_favorite`
- `is_trashed`
- `page` (integer)
- `size` (integer)

Output parameters:

- `result` (string)

### `get_export_preview` (~220 tokens)

List what export_pdf would include (id, type, filename, date, place, people,
video duration) so you know which assets exist before looking at images and
writing captions. Pass exactly one of album_id / asset_ids. Read-only.

The result also carries `options`: every choice export_pdf accepts, with its
default. When the user asked for a PDF without saying how they want it, show
them these choices and ask (layout, cover pages, which video moments, captions)
before exporting; when they did give specs, or just want "a PDF, defaults are
fine", export directly.

Args:
    album_id: Album UUID, or
    asset_ids: Explicit asset UUIDs (search results, a selection).
    limit: Max assets (1-500, default 100).

Returns: JSON {title, count, assets:[...], warnings:[...]} or {"error": ...}.

Input parameters:

- `album_id` (string)
- `asset_ids` (array)
- `limit` (integer)

Output parameters:

- `result` (string)

### `export_pdf` (~1140 tokens)

Build a PDF (cover, index, places, one section per asset) from an album or a
list of assets, on the machine running this server. Immich metadata (date, place,
camera, people, tags) is always included; pass `captions` {asset_id: text} with
what you saw to add your analysis. Video frames go straight into the PDF and cost
no tokens (up to 120 per video). The PDF never enters the conversation unless
return_base64=True. If the user asked for a PDF without saying how they want
it, call get_export_preview first and ask them about the choices it lists.

Keep the selection coherent: one story per PDF. Never mix unrelated assets
(two videos about different things, photos from different events) just to
show more; if the user's material spans several stories, offer one PDF per
story instead.

Args:
    album_id: Album UUID, or asset_ids: explicit asset UUIDs (exactly one of the two).
    output_path: Where to write (default ~/Desktop/<title>.pdf). Existing files are never overwritten.
    title: Cover title (default: album name or "Immich export <date>").
    captions: {asset_id: text} written after looking at the images.
    layout: 'detail' (one asset per page with its data, default), 'grid' (six per page)
        or 'photobook' (one asset per page, image as large as it fits, caption under
        it; a video with several chosen frames unfolds into one full page per frame).
    frames_per_video: Frames per video, evenly spaced (0-120, default 4; 0 = poster only).
    frame_interval: One frame every N seconds instead of frames_per_video (same 120 cap).
    frame_times: {asset_id: [seconds, ...]} exact moments for specific videos, chosen
        after looking at their frames ("the representative frame"). Wins over
        frames_per_video/frame_interval for the listed videos; others keep the spread.
    frame_captions: {asset_id: [text, ...]} one caption per extracted frame, in frame
        order (photobook prints each on its frame's page; other layouts ignore them).
    imag…

Input parameters:

- `album_id` (string)
- `asset_ids` (array)
- `captions` (object)
- `confirm` (boolean)
- `cover` (boolean)
- `footer` (string)
- `frame_captions` (object)
- `frame_interval` (number)
- `frame_size` (string)
- `frame_times` (object)
- `frames_per_video` (integer)
- `header` (boolean)
- `image_size` (string)
- `index` (boolean)
- `language` (string)
- `layout` (string)
- `limit` (integer)
- `map` (boolean)
- `order` (string)
- `output_path` (string)
- `places` (boolean)
- `return_base64` (boolean)
- `title` (string)
- `videos_position` (string)

Output parameters:

- `result` (string)

### `list_memories` (~199 tokens)

List memories — Immich's "on this day" collections of photos from past years.
Use this to build a 'tal día como hoy' story, album or PDF: each memory carries
the year it looks back to and the assets Immich picked for it. Read-only.

Args:
    for_date: ISO date — return the memories Immich shows on that day
        (e.g. today for the classic on-this-day feed). Omit for all memories.
    is_saved: If true, only memories the user saved; if false, only unsaved.
    size: Maximum memories to return (default 50).

Returns: JSON with total and a memories array; each has id, type, memory_at,
the year it remembers, is_saved, asset_count and a trimmed assets list
(id, filename, date).

Input parameters:

- `for_date` (string)
- `is_saved`
- `size` (integer)

Output parameters:

- `result` (string)

### `create_memory` (~187 tokens)

Create an "on this day" memory from chosen assets. Use this after curating
a set of photos from the same past date (e.g. via search_metadata with a date
range) to make them show up in Immich's memories feed. Side effect: creates
a memory on the server.

Args:
    memory_at: ISO date the memory is shown on (usually today's month and day).
    year: The past year the memory looks back to (required by Immich).
    asset_ids: Assets to include. May be empty, but an empty memory shows nothing.

Returns: JSON with the created memory's id, type, memory_at, the year it
remembers, is_saved, asset_count and a trimmed assets list (id, filename, date).

Input parameters:

- `asset_ids`
- `memory_at` (string, required)
- `year` (integer, required)

Output parameters:

- `result` (string)

### `update_memory` (~127 tokens)

Update a memory: save it for later, move its date, or mark it seen.
Side effect: modifies the memory on the server.

Args:
    memory_id: The memory to update.
    is_saved: True to save the memory, false to unsave it.
    memory_at: New ISO date to show the memory on.
    seen_at: ISO timestamp marking when the user viewed it.

Returns: JSON with the updated memory.

Input parameters:

- `is_saved`
- `memory_at` (string)
- `memory_id` (string, required)
- `seen_at` (string)

Output parameters:

- `result` (string)

### `delete_memory` (~64 tokens)

Delete a memory. The photos stay in the library — only the memory entry
goes away. Side effect: removes the memory from the server.

Args:
    memory_id: The memory to delete.

Returns: JSON confirming the deletion.

Input parameters:

- `memory_id` (string, required)

Output parameters:

- `result` (string)

### `get_timeline_buckets` (~204 tokens)

Month-by-month map of the library: one bucket per month with its asset count.
Use this before fetching assets — it shows in one cheap call which months hold
photos and how many, ideal for finding gaps, busy periods, or navigating a large
library without paging through everything. Read-only.

Args:
    album_id: Only count assets in this album.
    person_id: Only count assets showing this person.
    tag_id: Only count assets carrying this tag.
    is_favorite: If true, only count favorites.
    order: 'desc' for newest month first (the default), 'asc' for oldest first.

Returns: JSON with total_buckets and a buckets array of {timeBucket, count},
newest month first unless order='asc'.

Input parameters:

- `album_id` (string)
- `is_favorite`
- `order` (string)
- `person_id` (string)
- `tag_id` (string)

Output parameters:

- `result` (string)

### `get_timeline_bucket` (~185 tokens)

The assets of one month bucket from get_timeline_buckets. Use the two tools
together to walk a library month by month without expensive searches. Read-only.

Args:
    time_bucket: The bucket key exactly as get_timeline_buckets returned it
        (e.g. '2026-03-01').
    album_id: Only assets in this album.
    person_id: Only assets showing this person.
    tag_id: Only assets carrying this tag.
    is_favorite: If true, only favorites.

Returns: JSON with an assets array; each row has asset_id, date, is_image,
is_favorite, duration, city and country.

Input parameters:

- `album_id` (string)
- `is_favorite`
- `person_id` (string)
- `tag_id` (string)
- `time_bucket` (string, required)

Output parameters:

- `result` (string)

### `get_calendar_heatmap` (~295 tokens)

How many photos per day, over a date range — the data behind a calendar
heatmap. Use this to find gaps (months with nothing), busy periods, or to
check a library's health at a glance without listing assets. Immich 3.x
answers natively; on Immich 2.x the same shape is built from the timeline
(taken dates only), which costs one request per month in the range, so pass
the narrowest range that answers the question. Read-only.

Args:
    from_date: ISO date lower bound (e.g. '2026-01-01'). Omit for the server
        default on Immich 3.x; on 2.x an omitted bound means the last 365 days,
        because an open-ended range would walk every month of the library.
    to_date: ISO date upper bound. Omit for the server default.
    heatmap_type: 'Taken' (capture date, default) or 'Upload' (when it reached
        Immich; 3.x only).

Returns: JSON with source ('immich' or 'timeline'), total and a series of
{date, count} for the days that have activity, oldest first (a day missing
from the series had nothing).

Input parameters:

- `from_date` (string)
- `heatmap_type` (string)
- `to_date` (string)

Output parameters:

- `result` (string)

### `create_stack` (~132 tokens)

Group near-identical assets (a burst, retries of the same shot) into one
stack. The library then shows the stack as a single item fronted by its primary
asset, which keeps every shot without the visual clutter — a gentler cleanup
than deleting. The first id becomes the primary. Side effect: creates the
stack on the server.

Args:
    asset_ids: The assets to group, at least two. Order matters: the first is
        the cover.

Returns: JSON with the new stack's id, primary_asset_id and asset list.

Input parameters:

- `asset_ids` (array, required)

Output parameters:

- `result` (string)

### `list_stacks` (~83 tokens)

List every stack in the library. Use this to see what is already grouped
before creating new stacks or to find a stack's id. Read-only.

Args:
    primary_asset_id: Only the stack fronted by this asset.

Returns: JSON with total and a stacks array (id, primary_asset_id, assets).

Input parameters:

- `primary_asset_id` (string)

Output parameters:

- `result` (string)

### `get_stack` (~82 tokens)

One stack with its assets. Use this after list_stacks to see everything a
group holds before changing its cover or dissolving it, or to check what
create_stack actually grouped. Read-only.

Args:
    stack_id: The stack to fetch.

Returns: JSON with id, primary_asset_id and the asset list.

Input parameters:

- `stack_id` (string, required)

Output parameters:

- `result` (string)

### `update_stack` (~90 tokens)

Change which asset fronts a stack (the one the library shows). Side effect:
updates the stack on the server.

Args:
    stack_id: The stack to update.
    primary_asset_id: The asset that should become the cover. It must already
        belong to the stack.

Returns: JSON with the updated stack.

Input parameters:

- `primary_asset_id` (string, required)
- `stack_id` (string, required)

Output parameters:

- `result` (string)

### `delete_stack` (~64 tokens)

Dissolve a stack. The assets are NOT deleted — they simply show as
individual items again. Side effect: removes the grouping on the server.

Args:
    stack_id: The stack to dissolve.

Returns: JSON confirming the deletion.

Input parameters:

- `stack_id` (string, required)

Output parameters:

- `result` (string)

### `list_users` (~59 tokens)

The users visible on this Immich server. Use this to find the id that
create_partner needs, or to see who could be shared with. Read-only.

Returns: JSON with total and a users array of {id, name, email}.

Output parameters:

- `result` (string)

### `list_partners` (~73 tokens)

Who shares their library with this account, and who this account shares
with. Partner sharing is Immich's family feature: each side keeps its own
library but can see the other's. Read-only.

Returns: JSON with shared_with_me and shared_by_me arrays
(id, name, email, in_timeline).

Output parameters:

- `result` (string)

### `create_partner` (~82 tokens)

Share this account's library with another user on the server. The other
user will see these photos next to their own. Find the id with list_users.
Side effect: grants the user read access to the whole library.

Args:
    user_id: The user to share with.

Returns: JSON with the new partner entry.

Input parameters:

- `user_id` (string, required)

Output parameters:

- `result` (string)

### `update_partner` (~131 tokens)

Show or hide a partner's photos inside the main timeline (they stay
reachable either way). Only works on a partner who shares their library
with this account (someone in shared_with_me), because the flag controls
how THEIR photos appear in THIS timeline. Side effect: updates the setting
on the server.

Args:
    user_id: The partner whose setting changes.
    in_timeline: True to mix their photos into the timeline, false to keep
        them separate.

Returns: JSON with the updated partner entry.

Input parameters:

- `in_timeline` (boolean, required)
- `user_id` (string, required)

Output parameters:

- `result` (string)

### `remove_partner` (~63 tokens)

Stop sharing this account's library with a user. Their own photos are not
touched. Side effect: revokes their access.

Args:
    user_id: The user to unshare with.

Returns: JSON confirming the removal.

Input parameters:

- `user_id` (string, required)

Output parameters:

- `result` (string)

### `list_activities` (~140 tokens)

Comments and likes on a shared album, newest context included. Use this to
read what the people an album is shared with have said about it or about one
of its photos. Read-only.

Args:
    album_id: The album whose activity to read.
    asset_id: Only activity on this asset within the album.
    activity_type: 'comment' or 'like'. Omit for both.

Returns: JSON with total and an activities array (id, type, comment, asset_id,
user name, created_at).

Input parameters:

- `activity_type` (string)
- `album_id` (string, required)
- `asset_id` (string)

Output parameters:

- `result` (string)

### `create_activity` (~139 tokens)

Post a comment (or a like) on a shared album or on one asset in it.
Side effect: the activity appears for everyone the album is shared with.

Args:
    album_id: The album to comment on.
    comment: The comment text. Leave empty when sending a like.
    asset_id: Attach the comment/like to this asset instead of the album.
    like: True to send a like instead of a comment.

Returns: JSON with the created activity's id and type.

Input parameters:

- `album_id` (string, required)
- `asset_id` (string)
- `comment` (string)
- `like` (boolean)

Output parameters:

- `result` (string)

### `delete_activity` (~53 tokens)

Remove one comment or like. Side effect: deletes it for everyone.

Args:
    activity_id: The activity to remove (from list_activities).

Returns: JSON confirming the deletion.

Input parameters:

- `activity_id` (string, required)

Output parameters:

- `result` (string)

### `get_download_info` (~114 tokens)

How big the zip of an album or selection would be, BEFORE building it.
Use this to warn the user about the size (originals and videos add up fast)
and then decide whether to call download_archive. Read-only.

Args:
    album_id: Size the whole album.
    asset_ids: Or size just these assets.

Returns: JSON with total_size_mb, asset_count and the number of archives
Immich would split the download into.

Input parameters:

- `album_id` (string)
- `asset_ids`

Output parameters:

- `result` (string)

### `download_archive` (~153 tokens)

Download an album or a selection as one zip of the original files, written
to a local path. Use get_download_info first when the size matters. The file
is streamed to disk (safe for big albums) and an existing file is never
overwritten. Side effect: writes a file on the machine running the server.

Args:
    output_path: Where to write the zip (an existing file is refused).
    album_id: Download the whole album.
    asset_ids: Or download just these assets.

Returns: JSON with path, bytes written and how many assets went in, or an error.

Input parameters:

- `album_id` (string)
- `asset_ids`
- `output_path` (string, required)

Output parameters:

- `result` (string)

### `review_assets` (~230 tokens)

Remember a review verdict on assets, with the reason, so a later session
does not redo the analysis and the why survives. Use this after deciding what
to do with a photo in a cleanup or duplicate pass — together with a tag when
the user must see the state in Immich (tags are visible there, notes are not).
Side effect: writes the plugin's metadata key on each asset; other apps' keys
are untouched.

Args:
    asset_ids: The assets the verdict applies to.
    verdict: One of 'keep', 'delete_candidate', 'duplicate_of', 'needs_check'.
    reason: Free text explaining the verdict (e.g. 'near-identical to IMG_6367,
        keep that one'). Short and concrete beats long.

Returns: JSON with success, the number of assets reviewed, the verdict, and a
failed array of {asset_id, error} for any asset that could not be written.
Success is true only when nothing failed.

Input parameters:

- `asset_ids` (array, required)
- `reason` (string)
- `verdict` (string, required)

Output parameters:

- `result` (string)

### `record_action` (~193 tokens)

Remember something the plugin did to assets and why, for audit or undo:
which album they went into and from what prompt, what date they had before a
fix, why they were rotated. Side effect: writes the plugin's metadata key on
each asset; other apps' keys are untouched.

Args:
    asset_ids: The assets the action touched.
    action: Short verb-like label (e.g. 'added_to_album', 'date_fixed', 'rotated').
    detail: Free text with the context worth keeping (album name, previous
        value, the user's request).

Returns: JSON with success, the number of assets recorded, the action, and a
failed array of {asset_id, error} for any asset that could not be written.
Success is true only when nothing failed.

Input parameters:

- `action` (string, required)
- `asset_ids` (array, required)
- `detail` (string)

Output parameters:

- `result` (string)

### `get_asset_notes` (~88 tokens)

The plugin's notes on one asset: past review verdicts with reasons and
recorded actions, newest last. Empty lists when it was never annotated.
Read-only.

Args:
    asset_id: The asset to read.

Returns: JSON with asset_id, reviews [{at, verdict, reason}] and
actions [{at, action, detail}].

Input parameters:

- `asset_id` (string, required)

Output parameters:

- `result` (string)

### `get_assets_notes` (~167 tokens)

Which of these assets already carry notes, and their last verdict — the
call that lets a cleanup pass skip what an earlier session reviewed. Immich
cannot search this metadata, so the server is asked once per asset (no
tokens spent on the ones without notes). Read-only.

Args:
    asset_ids: The candidates to check (an album's assets, a search result).

Returns: JSON with checked (how many were asked), annotated (one compact row
per asset that has notes — asset_id, last_verdict, last_reason, last_review_at,
and the reviews/actions counts) and a failed array of {asset_id, error} for the
assets that could not be read. Success is true only when nothing failed.

Input parameters:

- `asset_ids` (array, required)

Output parameters:

- `result` (string)

### `clear_asset_notes` (~99 tokens)

Forget the plugin's notes on assets (reviews and actions). Only the
plugin's own key is removed; metadata other apps stored stays. Side effect:
deletes the notes on the server.

Args:
    asset_ids: The assets to clear.

Returns: JSON with success, how many assets were cleared, and a failed array
of {asset_id, error}. Success is true only when nothing failed.

Input parameters:

- `asset_ids` (array, required)

Output parameters:

- `result` (string)

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/drolosoft-immich-photo-manager/immich-photo-manager#diagnostics

## Score history

- 2026-09-20: 72
- 2026-09-19: 72
- 2026-09-18: 71
- 2026-09-17: 71
- 2026-09-16: 70
- 2026-09-15: 70
- 2026-09-14: 54
- 2026-09-13: 69
- 2026-09-12: 68
- 2026-09-11: 68
- 2026-09-10: 64
- 2026-09-09: 64
- 2026-09-08: 64
- 2026-09-07: 64
- 2026-09-06: 64
- 2026-09-05: 64
- 2026-09-04: 64
- 2026-09-03: 49

## Common questions

### What is the Immich Photo Manager MCP server?

Immich Photo Manager is an MCP server listed in the public MCP registry as io.github.drolosoft/immich-photo-manager. Photo management for self-hosted Immich: search, albums, duplicates, faces, PDF reports. 94 tools. This page covers its PyPI package (immich-photo-manager).

### Is the Immich Photo Manager MCP server safe to use?

Immich Photo Manager scores 72 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Immich Photo Manager MCP server expose?

Immich Photo Manager exposes 94 tools: ping, get_server_version, get_capabilities, get_statistics, update_credentials, and 89 more. Their descriptions and schemas cost roughly 14,186 tokens of context every time the server is loaded.

### Is the Immich Photo Manager MCP server still maintained?

Immich Photo Manager is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- PyPI project: https://pypi.org/project/immich-photo-manager/
- Socket report: https://socket.dev/pypi/package/immich-photo-manager
- Repository: https://github.com/drolosoft/immich-photo-manager
- Changelog RSS feed: https://verifymcp.io/servers/drolosoft-immich-photo-manager/immich-photo-manager.xml
- Changelog JSON feed: https://verifymcp.io/servers/drolosoft-immich-photo-manager/immich-photo-manager.json
- HTML version of this page: https://verifymcp.io/servers/drolosoft-immich-photo-manager/immich-photo-manager
