Repository navigation
feat(adapters): add read-only Immich adapter (#1238) - #1329
Merged
Merged
Conversation
Eight GET-only tools on the Immich 3.x REST API with an x-api-key key that needs only user.read, asset.read, asset.statistics, album.read and person.read, no admin account. Responses drop file paths, checksums, thumbhashes, OAuth ids and license keys. Updates the catalog, adapter counts and llms.txt.
…#1238) Static checks pin auth, read-only tools, URLs and the response mappings. Live checks, verified against Immich v3.3.5, cover the default trash and archive counts, the monthly bucket format, the 400/401/403 codes and log the raw and mapped response sizes.
GallaGit
requested review from
D3nisty,
helpcodeai and
mirkopoloni
as code owners
October 29, 2026 12:43
helpcodeai
approved these changes
Oct 29, 2026
helpcodeai
left a comment
Contributor
There was a problem hiding this comment.
Thanks @GallaGit, this follows everything we agreed on in #1238: license, oauthId, thumbnailPath, profileImagePath, thumbhash, originalPath and checksum are excluded, all 8 tools are GET, the key is marked secret, the instructions say no admin account is needed and list the 5 permissions, and the Simple Icons logo is included. Security check: no script or event attributes in the SVG, and the live suite only runs behind RUN_IMMICH_LIVE=1. Merging.
7 of 14 tasks
helpcodeai
enabled auto-merge (squash)
October 29, 2026 15:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a read-only adapter for Immich, the self-hosted photo and video library, as asked in #1238. An AI can count the photos and videos, list albums and who they are shared with, read one asset's dates, EXIF, GPS, people and tags, search people and places, and get the number of assets per month. All 8 tools are
GETrequests on the Immich 3.x REST API ({{IMMICH_URL}}/api), authenticated with an API key in thex-api-keyheader.As you suggested, the setup does not need an admin account. The instructions list the 5 key permissions (
user.read,asset.read,asset.statistics,album.read,person.read) as the way to keep access small. The live run used a non-admin user's key with exactly those 5 permissions.Tested against Immich v3.3.5 (
GET /api/server/versionreturns3.3.5). It ran locally with the official v3.3.5docker-compose.yml. The test library had 22 JPEGs: 1 in the trash, 1 archived, 1 tag, 1 owned album and 1 album shared by another user, plus 1 recognised and named person.Changes
packages/backend/src/adapters/intl/immich.json: 8 GET tools, instructions,envVarMetaforIMMICH_URL/IMMICH_API_KEY,verifyHints(401/403/404), probeimmich_get_my_user, healthcheck/server/ping.immich_get_my_userGET /users/melicense,oauthId,profileImagePathimmich_get_asset_statisticsGET /assets/statisticsimmich_list_albumsGET /albumsid, albumName, description, assetCount, shared, startDate, endDate, albumThumbnailAssetId,owner(name),members(name, role)immich_get_albumGET /albums/{id}albumUsers[*].user.profileImagePathimmich_get_assetGET /assets/{id}originalPath,checksum,thumbhash,owner.profileImagePath,people[*].thumbnailPathimmich_search_peopleGET /search/person[*].thumbnailPathimmich_get_timeline_bucketsGET /timeline/bucketstagIdkept; the id comes fromimmich_get_asset().tags[].id)immich_search_placesGET /search/placesimmich.live.spec.ts: 21 static tests and 9 live tests, gated byRUN_IMMICH_LIVE=1. The fake key is built at runtime, so there are no secrets in fixtures.content/guides/en/immich-to-mcp.mdx: the setup guide.packages/frontend/public/logos/connectors/immich.svg: Simple Icons 16.36.0 (CC0), withfill="#4250AF", following AGENTS.md step 6.I regenerated
catalog.ts, raised the adapter counts from 562 to 563, and regeneratedllms.txt/llms-full.txt.Decisions taken from the live run and reflected in the descriptions and instructions:
visibilityenum istimeline | archive | hidden.lockedis left out because an API key gets401 Elevated permission is requiredfor it.400 Not found or no asset.read accessfor an unknown asset or album id, and the instructions say so.readOnlyHintoverride)Type
Testing
node scripts/validate-adapters.mjs --warnimmich.jsonnode --test scripts/validate-adapters.test.mjsnode scripts/regenerate-catalog.mjsnode scripts/adapter-count.mjs --checknode scripts/generate-llms-txt.mjsllms.txt,llms-full.txtregeneratedpackages/backendnpx jest src/adapters/intl/immichpackages/backendRUN_IMMICH_LIVE=1 IMMICH_URL=http://localhost:2283 IMMICH_API_KEY=... IMMICH_API_KEY_NO_PERSON=... IMMICH_PERSON_NAME=Alex npx jest src/adapters/intl/immichpackages/backendnpx jest --maxWorkers=3(full backend suite)Baseline failures. These 4 failing suites fail the same way on a clean
upstream/main(aae3cc3) in my environment (7 failed, 42 passed). None of them involves this change:sentry-scrub.spec.tsandsecret-guard.flows.spec.ts: TypeScript errors onSpanJSON.name/.attributeswith my installed@sentrytypes.ssrf.util.spec.ts: "a name that does not resolve" depends on my DNS resolver.cloudflare.live.spec.ts: its live gate picked up a Cloudflare variable from my shell.Live behaviour measured on v3.3.5:
/assets/statisticswithout filtersvisibility=timeline/archive/isTrashed=true/timeline/bucketswithout filters{timeBucket, count}per month, newest first,timeBucket=YYYY-MM-01(e.g.2008-10-01).personId,albumIdandtagIdfilters work.visibility=lockedwith an API key401 Elevated permission is required401 Authentication required/401 Invalid API keyperson.readon/search/person403 Missing required permission: person.read403 Forbidden(the key is not admin)400 Validation failed(Invalid UUID)400 Not found or no asset.read access/no album.read access(not 404)/search/personor/search/placeswithoutname400 Validation failedIMMICH_URLending in/api404 Cannot GET /api/api/users/me/albums/{id}assetsarray (members,assetCount, date range only)/server/pingwithout a key{"res":"pong"}Response sizes, raw vs. after the response mapping (small test library):
immich_get_my_userimmich_list_albums(2 albums)immich_get_albumimmich_get_assetimmich_search_people(1 person)immich_get_timeline_buckets(10 months)immich_search_places("Berlin", 20 results)/albumsgrows with every album and every member, so it is the only list I shaped with JMESPath. The other answers are a single object or already small.Checklist
npm test): all pass except the 4 baseline suites above, which also fail on cleanmainRelated Issues
Closes #1238. Part of #997.
Open option for the maintainer
Without filters,
immich_get_asset_statisticsandimmich_get_timeline_bucketsmirror the Immich API: trashed assets are excluded but archived ones are counted. I left that behaviour unchanged and documented it in the instructions. If you prefer the counts to match what the Immich app timeline shows, I can defaultvisibilitytotimelinein a follow-up commit.