gphotos-mcp
Provides read-only access to a personal Google Photos library, allowing listing and searching photos, browsing albums, retrieving media metadata, viewing image previews, and downloading original files.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@gphotos-mcpfind photos of my cat from last month"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
gphotos-mcp
A read-only MCP server that lets Claude list, search, inspect
and look at the photos in one personal Google Photos library. It talks to the same internal
batchexecute RPC that photos.google.com itself uses, through
gpwc, and signs in with a cookies.txt
exported from your browser. Nothing in it can modify the library.
Why the unofficial web API
On 31 March 2025 Google removed the Photos Library API scopes that gave apps access to a user's
existing media (photoslibrary.readonly and friends); what remains is the Picker API and access to
media an app uploaded itself. Reading your whole library programmatically today means using the
web app's private RPC, which is what this server does.
Related MCP server: Google Maps MCP Server
Tools
tool | arguments | returns |
|
| newest-first by taken date: |
|
| same item shape, using Google Photos' own search (people, places, things, text in images, dates) |
| — |
|
|
| album title / count and its items, same item shape |
|
| what the web UI's info panel shows: taken / uploaded time, dimensions, file size, camera (raw), location (decimal degrees + place name), description, albums it belongs to, upload source, favourite / archived flags, storage quality |
|
| the picture as MCP image content (JPEG, longest side ≤ |
|
| saves the original file to disk, returns |
| — |
|
The item shape is identical across list_library, search and get_album. docs/PAYLOADS.md
maps every tool to the RPC behind it.
Requirements
Python 3.11+ and uv — or Docker with Compose v2.
A browser to export cookies from, and (for the claude.ai connector) a way to expose one port, e.g. cloudflared or Tailscale Funnel.
Cookies (authentication)
The server authenticates exactly like your browser does. Export the cookies from a dedicated incognito session that you never open again, as described in the gpwc README:
Install the Get cookies.txt LOCALLY extension and allow it in private / incognito windows.
Open a new incognito window and sign in to Google.
Open photos.google.com and go to your library page.
Open a new empty tab, then close the Google Photos tab.
Click Export All Cookies in the extension and save the file as
cookies.txt.Close the incognito window and do not reopen that session in a browser.
Keep the file private (chmod 600 cookies.txt): it is equivalent to your signed-in browser.
When Google invalidates the session every tool fails with
Google session expired — re-export cookies.txt; export a fresh file to the same path and the
server picks it up on the next call (no restart needed).
If the browser session held several Google accounts, GP_ACCOUNT_INDEX selects one
(the N in photos.google.com/u/N/).
Configuration
All settings are environment variables; copy .env.example to .env.
variable | default | meaning |
| (required) | path to the Netscape |
|
| account within the cookie session |
|
| cache of downscaled JPEGs, keyed by |
|
|
|
|
| bind address in http mode ( |
|
| port in http mode |
| (required in http mode) | ≥ 16 URL-safe characters; |
In http mode the server refuses to start without MCP_AUTH_TOKEN. The token is accepted either as
Authorization: Bearer <token> or as a secret path prefix, https://host/<token>/mcp, for
connector UIs that cannot set headers. Cookies, tokens and request bodies are never logged
(uvicorn's access log is off for that reason).
Running
Native
uv sync
GP_COOKIES_FILE=/path/to/cookies.txt uv run gphotos-mcp # stdio
MCP_TRANSPORT=streamable-http MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
GP_COOKIES_FILE=/path/to/cookies.txt uv run gphotos-mcp # http on 127.0.0.1:8765/mcpDocker compose
cp .env.example .env # set GP_COOKIES_FILE (host path) and MCP_AUTH_TOKEN
docker compose up -d --build # Streamable HTTP on 127.0.0.1:8765
docker compose logs -fThe compose service always runs in http mode, publishes the port on localhost only, mounts the
cookies file read-only at /config/cookies.txt, keeps the image cache in a named volume and maps
./downloads to /downloads. The container runs as uid 1000, so on Linux the cookies file must be
readable by that uid.
For stdio through Docker (Claude Desktop / Claude Code on the same machine):
docker compose run --rm -i -e MCP_TRANSPORT=stdio gphotos-mcpdownload_media then writes inside the container: pass dest_dir=/downloads to land in
./downloads on the host.
Claude Desktop and Claude Code (stdio)
Claude Desktop: add to claude_desktop_config.json
(macOS ~/Library/Application Support/Claude/, Windows %APPDATA%\Claude\).
Claude Code: the same object in .mcp.json (project) or ~/.claude.json (user), or run
claude mcp add gphotos -e GP_COOKIES_FILE=/abs/path/cookies.txt -- uv run --directory /abs/path/google-photos-mcp gphotos-mcp.
{
"mcpServers": {
"gphotos": {
"command": "uv",
"args": ["run", "--directory", "/abs/path/google-photos-mcp", "gphotos-mcp"],
"env": { "GP_COOKIES_FILE": "/abs/path/cookies.txt" }
}
}
}Docker variant (needs .env next to the compose file):
{
"mcpServers": {
"gphotos": {
"command": "docker",
"args": ["compose", "-f", "/abs/path/google-photos-mcp/docker-compose.yml",
"run", "--rm", "-i", "-e", "MCP_TRANSPORT=stdio", "gphotos-mcp"]
}
}
}claude.ai custom connector (http mode)
claude.ai can only reach a public HTTPS URL, so put a tunnel in front of the server. Keep the port bound to localhost and let the tunnel do the exposure; the token is the only access control.
Set
MCP_AUTH_TOKENin.envand start the server:docker compose up -d --build.Expose it, for example:
cloudflared quick tunnel:
cloudflared tunnel --url http://127.0.0.1:8765prints ahttps://….trycloudflare.comhostname (it changes on every run; use a named tunnel for a stable one), orTailscale Funnel:
tailscale funnel 8765giveshttps://<machine>.<tailnet>.ts.net.
In claude.ai open Settings → Connectors → Add custom connector and enter
https://<public-host>/<MCP_AUTH_TOKEN>/mcp. Leave the OAuth client fields empty. Clients that can send headers may instead usehttps://<public-host>/mcpwithAuthorization: Bearer <MCP_AUTH_TOKEN>.
The secret is part of the URL, so it will appear in the tunnel provider's logs and in the
connector settings. Rotate it by changing MCP_AUTH_TOKEN and restarting. download_media is
not offered in http mode because the server's disk is not the client's.
How it treats Google
Google's abuse detection is the main operational risk, so the server is deliberately slow:
exactly one request in flight at a time, at least 300 ms apart, no parallelism anywhere;
429 / 5xx / network errors are retried at most 3 times with jittered exponential backoff;
get_mediaresults are cached on disk, so re-viewing an image costs nothing;list calls cost two RPCs per page (the list itself, then one batch call for filenames);
get_media_infoanddownload_mediabatch their two RPCs into one HTTP request.
Risks
Terms of service. This uses Google's private web API with your own session. It is a grey area; automated access can get an account rate-limited or flagged. Use it for yourself, keep volumes small, and do not run several instances against one account.
It will break. The RPC ids and array layouts are undocumented and change without notice. When that happens the parsers in
src/gphotos_mcp/rpc.pyneed updating;docs/PAYLOADS.mdrecords where every index came from.The cookies are your account. Anyone holding
cookies.txtcan act as you on Google. Store it like a password and export it from a session you do not use for anything else.The http token is a bearer secret. Whoever has the URL has read access to your photos.
Not supported
Anything that writes: delete, trash, move, edit, favourite, album changes, uploads (uploads are gpmc's job). Not even behind a flag.
The official Library / Picker APIs and OAuth.
cover_media_keyfor albums: the web API only exposes the cover as a thumbnail URL.Structured EXIF (aperture, exposure, ISO, focal length): the info RPC carries one opaque
cameravalue whose layout is undocumented; it is passed through as-is.Trash, archive, locked folder, partner sharing, shared links, the video half of Live Photos.
Bulk operations, more than one account per server instance,
download_mediain http mode.
Development
uv sync # includes the dev group (pytest)
uv run pytest # unit tests, no network, recorded fixtures
GP_COOKIES_FILE=cookies.txt uv run python scripts/smoke.py --query dog # live check of every toolLayout: src/gphotos_mcp/client.py is the adapter (plain dataclasses in and out; gpwc is used
for the session and request payloads), rpc.py parses the raw arrays, transport.py holds the
pacing / retry / session-expiry logic, and server.py only maps tools onto the adapter.
tests/fixtures/ are sanitized RPC responses (see the README there).
Dependencies: mcp 2.x (its high-level server class was called FastMCP in 1.x and is
MCPServer in 2.x; same decorator API), gpwc pinned to a git commit, and pillow. httpx is
not used directly: every HTTP call goes through gpwc's requests session so the cookie jar,
timeouts, pacing and retries live in one place (gpwc's own tests download the same way).
Credits
xob0t reverse-engineered the protocol in
google_photos_web_client and
Google-Photos-Toolkit; the recorded fixtures
derive from the latter (MIT). Background on batchexecute:
Deciphering Google's batchexecute.
This server cannot be deployed
Maintenance
Related MCP Connectors
Read and edit GA4, Search Console and Google Tag Manager from any MCP client. 29 tools.
Read-only MCP access to performix.app's public capability guides and book corpus.
Read-only Search Console analytics, URL inspection, indexing diagnostics, and sitemaps.
Provides read access to your GKE and Kubernetes resources.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables read-only access to GitLab projects, files, directories, and merge requests through MCP tools.-
- AlicenseAqualityAmaintenanceProvides hosted, read-only Google Maps tools for MCP clients, enabling place search, full place details, reviews, photos, posts, and contributor history without a Google Cloud project or billing.26503 npm6MIT
- AlicenseNot gradedqualityCmaintenanceEnables read/write access to Google Tasks, including listing, searching, creating, updating, deleting, and clearing tasks, from any MCP-compatible client.MIT
- AlicenseNot gradedqualityCmaintenanceEnables read-only inspection of Google Ads accounts through MCP, including account inventory, reporting, metadata, change history, and safe GAQL queries.Apache 2.0