Skip to main content
Glama
README.md
# lightroom-desktop-mcp

An MCP server that lets an AI agent drive **Adobe Lightroom desktop** on macOS. This is the cloud-based Lightroom (bundle id `com.adobe.mas.lightroomCC`), not Lightroom Classic.

Lightroom desktop has no plug-in API, so this works from the outside. A small Swift helper uses the macOS Accessibility API to click menus and native controls and to send keyboard and mouse events. The Edit panel sliders are custom-drawn and invisible to Accessibility, so they are read and set through Vision OCR on window screenshots. In effect it is computer use with Lightroom-specific shortcuts.

For Lightroom Classic, use [Automaat/lightroom-mcp](https://github.com/Automaat/lightroom-mcp) instead. It talks to Classic's plug-in SDK directly and is faster and more reliable.

Tested with Lightroom 9.6 on macOS 27, Simplified Chinese UI.

## Install

Requires Node.js 20+ and the Xcode Command Line Tools (`swiftc`).

```bash
git clone https://github.com/yuanruoyu515/lightroom-desktop-mcp.git
cd lightroom-desktop-mcp
npm install            # also compiles bin/lrax
claude mcp add lightroom-desktop -s user -- "$(which node)" "$PWD/index.js"
```

Grant the app that launches the server (Terminal, Claude desktop, etc.) **Accessibility** and **Screen Recording** permission in System Settings → Privacy & Security.

## Tools

| Tool | What it does |
|---|---|
| `lr_status`, `lr_launch`, `lr_quit` | App state, start, quit |
| `lr_screenshot` | Screenshot of the main window. Later clicks use its pixel coordinates by default |
| `lr_menu_list`, `lr_menu_click` | List or click menu items by path, in the UI language, e.g. `Photo > Set Rating > 5 Stars`. The most reliable way to run commands |
| `lr_read_sliders` | Read visible Edit panel sliders as label → value |
| `lr_set_slider` | Set a slider by its label, e.g. `{"label": "Exposure", "value": 0.7}`, then read it back |
| `lr_find_text` | OCR the window and return text with clickable screen coordinates |
| `lr_ui_tree`, `lr_ui_find`, `lr_ui_action` | Accessibility tree for native buttons, toolbar, search field |
| `lr_click`, `lr_drag`, `lr_scroll`, `lr_key`, `lr_type` | Raw mouse and keyboard fallback |
| `lr_add_photos` | Hand files to Lightroom for import (uploads them to Adobe cloud) |
| `lr_catalog_overview`, `lr_catalog_query` | Read-only SELECT queries on Lightroom's local SQLite index (`Managed Catalog.wfindex`) |

Environment variables: `LIGHTROOM_DESKTOP_READ_ONLY=1` disables every tool that changes the UI or data; `LIGHTROOM_DESKTOP_LIBRARY` / `LIGHTROOM_DESKTOP_INDEX` point to a non-default library; `LR_BUNDLE_ID` overrides the app's bundle id.

## Limitations

- Slider positions come from OCR plus a measured layout offset (the value field's right edge sits 225 pt right of the label). A Lightroom redesign may break it. Only the Chinese UI has been tested.
- Vision does not read a lone "0", so a slider is assumed to be 0 when the value slot has a glyph, nothing to its left, and a slider track below.
- Masks, curves, crop and other drag-heavy tools only work through raw clicks and drags, which is slow and error-prone.
- About 3 seconds per slider. Input events take over the mouse and keyboard while it runs.
- In Local mode edits apply to the files on disk; cloud photos sync to Adobe.

## License

MIT