darktable-mcp

darktable-mcp

Enables Claude to edit photos in darktable via natural language, adjusting sliders, applying styles, and organizing the library in real time through the MCP protocol.

Category
访问服务器

README

dark-table-mcp

Python 3.10+ License: MIT darktable 5.6.0

Tell Claude how you want a photo to look — and watch darktable change it live.

dark-table-mcp is a Model Context Protocol server that turns Claude into a hands-on photo-editing and library assistant for darktable. You describe the look you're after in plain language — "open the barn photo, make it warmer and moodier, and lift the shadows" — and Claude drives the real sliders in your live darktable darkroom. The image re-renders in front of you as it works. You never have to know which module or slider does what; that knowledge lives in the server.

Beyond editing, Claude can browse and organize your library: rate and label photos, add tags and metadata, geotag, apply and save styles, batch a look across a whole selection, and export finished files.

Validated against darktable 5.6.0 (Lua API 9.7.0) on Windows.


See it in action

Claude edited this barn-under-the-northern-lights shot live — a single instruction to reveal the near-black foreground while keeping the aurora vivid. It lifted the shadows to bring out the barn and field, protected the intense red highlights, and added contrast and vibrance for pop, checking its own get_preview along the way.

Before After
Barn under the aurora, before editing — the barn and foreground are crushed to near-black silhouette The same photo after Claude's live edit — the barn and field are revealed with detail while the red-and-green aurora stays vivid

Straight-out-of-camera raw (left) vs. Claude's live darktable edit (right).


Highlights

  • Live, conversational editing. Claude nudges exposure, white balance, contrast, saturation, vibrance, shadows, highlights and more on the photo open in your darkroom — you see every change land in real time.
  • Semantic "looks." One-word vibes like moody, cinematic, golden, vivid, or faded move several controls together for a coherent starting point, then Claude fine-tunes to taste.
  • See-the-result preview. get_preview renders the current edit to an actual image Claude can look at, so it edits with its eyes open — make a change, check it, decide the next move.
  • Styles, including imported packs. Apply, create, export, and delete darktable styles. Import downloaded .dtstyle files — including LUT-based look packs — and apply them live.
  • A ready-made starter pack. build_starter_styles bakes 21 curated MCP - … styles straight from the live pipeline, so Claude has a rich palette of looks to reach for.
  • Full library organizing. Star ratings, color labels, keyword tags, metadata (title/creator/…), read-only EXIF, and GPS geotagging.
  • Batch over a selection. Most organizing tools and apply_style accept all_selected=true to act on the entire lighttable selection at once. copy_edit clones one good edit onto a burst.
  • Real exports. Write the edited photo out to a genuine JPEG / PNG / TIFF at full or capped resolution.
  • Robust, diagnosable errors. Tools never crash the conversation — they return structured results, and a dedicated diagnose tool troubleshoots the connection when something's off.

How it works

Claude calls an MCP tool; the Python bridge shells out to darktable's own bundled gdbus.exe to invoke the DBus Remote.Lua method, which runs a small companion Lua helper inside your live darktable session. That helper moves the real GUI sliders (via darktable's action system), and the darkroom re-renders. Results come back as JSON.

  You ──▶ Claude ──▶ MCP tool (server.py)
                          │
                          ▼
                   bridge.py  ──▶  gdbus.exe  ──▶  darktable DBus
                          │                        org.darktable.service.Remote.Lua
                          │                              │
                          │                              ▼
                          │                   dtmcp.lua helper  (in the LIVE session)
                          │                              │
                          │                              ▼
                          │                   darkroom re-renders  🖼️  (you see it change)
                          ▼                              │
                     JSON reply  ◀───────────────────────┘

The Lua helper (darktable_mcp/lua/dtmcp.lua) is auto-injected over DBus on first use — you do not need to edit luarc or install anything into darktable. Because everything runs inside the one already-open session, there's no second process and no database-lock contention.

For the full architecture and design rationale, see docs/plans/2026-07-05-darktable-mcp-design.md.


Requirements

  • darktable 5.x with Lua support enabled (LuaEnabled = true) and the GUI running. This bridge drives a live session; darktable-cli does not expose it.
  • Python 3.10+
  • Windows. Paths default to C:\Program Files\darktable\bin (the folder containing gdbus.exe and darktable.exe); override with the DARKTABLE_BIN_DIR environment variable if darktable is installed elsewhere.

Installation

Clone the repo, then install it (editable) into your environment:

git clone https://github.com/yourname/dark-table-mcp.git
cd dark-table-mcp

# with uv (recommended)
uv venv
uv pip install -e .

# …or with plain pip
pip install -e .

This installs the darktable-mcp entry point (defined in pyproject.toml).

uv note: uv pip install needs a virtual environment. Run uv venv first (as above), then uv pip install -e . installs into that venv.


Configuration for an MCP client

Point your MCP client at the installed darktable-mcp entry point. On Windows this lives in your venv's Scripts directory.

Claude Desktop — add to claude_desktop_config.json:

{
  "mcpServers": {
    "darktable": {
      "command": "C:\\path\\to\\dark-table-mcp\\.venv\\Scripts\\darktable-mcp.exe",
      "env": {
        "DARKTABLE_BIN_DIR": "C:\\Program Files\\darktable\\bin",
        "DARKTABLE_MCP_TIMEOUT": "30"
      }
    }
  }
}

Both env entries are optional — omit them to use the defaults (bin dir C:\Program Files\darktable\bin, timeout 30 seconds).

Claude Code — register the same command as an MCP server, e.g.:

claude mcp add darktable -- "C:\path\to\dark-table-mcp\.venv\Scripts\darktable-mcp.exe"

Environment variables

Variable Default Purpose
DARKTABLE_BIN_DIR C:\Program Files\darktable\bin darktable's bin folder — where gdbus.exe (and darktable.exe) live. The bundled gdbus is preferred so the DBus session bus matches the one darktable registered on.
DARKTABLE_MCP_TIMEOUT 30 Default per-call timeout, in seconds. Raise it for slow full-resolution exports of large raws.

Usage — the live loop

Open darktable first. The server talks to a running GUI session, so launch darktable and let it finish loading before you start.

Then just talk to Claude. A realistic exchange:

You: Open the barn photo and make it warmer and moodier.

Claude: calls open_in_darkroom("barn"), then apply_look("moody"), then get_preview to see the result — "Here's the barn with a moodier grade: cooler-then-warm balance, deeper shadows, a touch less saturation. Want it warmer still, or should I open the shadows back up?"

You: A bit warmer, and lift the shadows so the barn wall isn't so dark.

Claude: calls adjust("warmth", "up", 2) and adjust("shadows", "up", 3), previews again — "Warmer and the wall reads better now. Happy with this? I can save it as a reusable style."

You: Love it — save it and give it 4 stars.

Claude: create_style_from_current("Barn Dusk"), set_rating(4) — "Saved and rated."

The rhythm is: open → apply a look → preview → nudge → preview → save/rate/export. Claude reads the built-in darktable_guide to translate your words ("moody", "faded", "punchier") into the right controls.


Tools reference

The server exposes 33 tools. All editing/organizing tools act on the photo currently open in the darkroom by default; those noted below accept all_selected=true to act on the entire lighttable selection instead.

Editing

Tool Parameters Description
open_in_darkroom query Open the first photo whose filename contains query in the darkroom, so edits are visible.
get_current_image Report which photo is open in the darkroom (the one edits apply to).
adjust control, direction, amount=3 Nudge one control and watch it change live. control: brightness/exposure, warmth/temperature, tint, contrast, saturation, vibrance, shadows, highlights, filmic_contrast, whites, blacks. direction: up/down. amount: 1 (tiny) – 10 (large).
reset_current Discard all edits on the current photo and start over.

Looks & styles

Tool Parameters Description
list_looks List the one-word semantic looks available to apply_look.
apply_look look Apply a one-word semantic look (e.g. moody, golden) by nudging several controls together.
list_styles List the saved styles (looks) available in darktable.
apply_style name, all_selected=false Apply a saved style to the current photo — or the whole selection.
create_style_from_current name, description="" Save the current photo's edit as a reusable named style.
build_starter_styles image_query Build the 21 MCP - … starter styles on the given photo and save them (the photo's edits are reset afterward, leaving it untouched).
import_style path Import a .dtstyle file (e.g. a downloaded style or LUT-based look pack).
export_style name, directory Export a saved style to a .dtstyle file to share or back up.
delete_style name Delete a saved style by name.
copy_edit from_query, all_selected=false Copy the full edit from one photo (matched by filename substring) onto the current photo or the whole selection.

Preview

Tool Parameters Description
get_preview max_size=1024 Render the current darkroom edit to a JPEG (long edge capped at max_size, 64–4096) so Claude can see the result.

Organizing

Tool Parameters Description
set_rating rating, all_selected=false Set the star rating: -1 reject, 0 none, 15 stars.
get_labels Get the current photo's star rating and color labels.
set_color_label color, on=true, all_selected=false Toggle a color label: red/yellow/green/blue/purple.
add_tag tag, all_selected=false Attach a keyword tag, creating it if needed.
remove_tag tag, all_selected=false Detach a tag from the current photo (or the selection).
get_tags List the tags attached to the current photo.
set_metadata field, value Set a metadata field: title/creator/publisher/rights/description.
get_metadata Get the current photo's metadata, key EXIF (camera, lens, ISO, aperture, exposure), and GPS.
set_location latitude, longitude, elevation=None Geotag the current photo with GPS coordinates (elevation in metres, optional).

Library & export

Tool Parameters Description
list_images filter="", limit=50 List photos in the library, optionally filtered by a filename substring.
list_collection limit=100 List photos in darktable's current collection (the active lighttable filter).
get_selection List the photos currently selected in the lighttable.
duplicate_image Create a virtual copy of the current photo with its own independent edits.
import_images path Import a photo file or a whole folder into the library.
export_image path, format="jpeg", max_size=0 Export the current edited photo to a real file. format: jpeg/png/tiff. max_size: cap the long edge in pixels (0 = full resolution). The parent folder must already exist.

Diagnostics

Tool Parameters Description
dt_status Check whether darktable is running; report version, Lua API, and image count.
diagnose Troubleshoot the connection: gdbus path, whether darktable is running, version, config directory, and any stale lock files. Call this first when other tools fail.
darktable_guide Return the intent→control guide Claude uses to translate plain-language requests. (Also exposed as the darktable://guide MCP resource.)

Control vocabulary & semantic looks

Controls (all validated live against darktable 5.6.0)

Friendly names you can ask Claude to adjust, and the darktable module/slider each maps to:

Friendly name darktable action path
brightness / exposure iop/exposure/exposure
warmth / temperature iop/temperature/temperature
tint iop/temperature/tint
contrast iop/colorbalancergb/contrast
saturation iop/colorbalancergb/global saturation
vibrance iop/velvia/strength
shadows iop/shadhi/shadows
highlights iop/shadhi/highlights
filmic_contrast iop/filmicrgb/contrast
whites iop/filmicrgb/white relative exposure
blacks iop/filmicrgb/black relative exposure

Semantic looks (apply_look)

warmer · cooler · brighter · darker · punchy · vivid · muted · faded · moody · golden · cinematic · soft

Each is a curated sequence of nudges — for example, moody cools the white balance, dims the image, drops saturation, adds contrast, and deepens the shadows.

The MCP - … starter style pack (build_starter_styles)

Running build_starter_styles bakes 21 reusable styles from the live pipeline:

Warm Punch · Golden Hour · Moody · Vivid Pop · Faded Film · Cinematic · Teal & Orange · Nordic Cool · Vintage Matte · Bright & Airy · Low Key · Autumn Warmth · Pastel Portrait · Landscape Pop · Sunset Glow · Clean Neutral · Stormy Sky · Retro Film · Punchy Street · Dreamy Soft · Rich & Deep

They're saved in darktable prefixed with MCP - (e.g. MCP - Golden Hour) so list_styles and apply_style can find them.


Troubleshooting

Start with diagnose. When any tool misbehaves, ask Claude to run diagnose — it reports the gdbus path (and whether it exists), whether darktable is running, its version, the config directory, the current timeout, and any stale lock files, all without assuming darktable is up.

Symptom Cause & fix
"darktable is not running" The GUI isn't open. This bridge drives a live session; darktable-cli does not expose it. Open the darktable GUI and try again.
"lost connection to darktable" after it was working darktable crashed or was closed. Reopen it. If a crash left stale library.db.lock / data.db.lock files in the config dir (%LOCALAPPDATA%\darktable), they'll block the next launch — delete them. diagnose lists their exact paths.
"gdbus not found" The bridge couldn't find gdbus.exe. Set DARKTABLE_BIN_DIR to your darktable bin folder (the one with gdbus.exe and darktable.exe).
Timeout, especially on a full-resolution export Large raws render slowly. export_image gets a generous 300s budget, but you can raise the general timeout via DARKTABLE_MCP_TIMEOUT (seconds) or export at a capped max_size (e.g. 2560). A mid-render abort can crash darktable, so the bridge reports the timeout rather than retrying.

Limitations / not yet supported

  • Relative nudges, not absolute values. darktable's sliders read back in mixed units (some normalized, some native), so Claude adjusts controls up/down rather than setting exact numbers. This suits the conversational, watch-it-change loop.
  • A live GUI session is required. This is not a headless renderer; it drives the running darktable GUI. (Headless export via darktable-cli is a possible later addition.)
  • Crop / rotate are deferred. The geometry modules are reachable via the action system but produced no change in the exported render — they appear to need the interactive crop commit the action bridge doesn't trigger. Left out rather than shipped as no-ops.
  • LUTs only via styles. There's no direct LUT tool; import a LUT-based .dtstyle look pack with import_style, then apply it.
  • No delete or move. Removing images from the library or moving files on disk is intentionally omitted to keep the server safe to hand to an assistant.

Development

Project layout

dark-table-mcp/
├── darktable_mcp/
│   ├── server.py         # the MCP tools (FastMCP) — the source of truth for the tool list
│   ├── bridge.py         # DBus Remote.Lua transport via gdbus; error types; diagnose()
│   ├── controls.py       # control registry, semantic looks, starter styles, the intent guide
│   ├── config.py         # binary/gdbus location, DBus constants, timeout
│   ├── extra_styles.py   # 15 additional starter-style recipes (merged into the pack)
│   └── lua/
│       └── dtmcp.lua     # companion Lua helper, auto-injected into the live session
├── docs/plans/2026-07-05-darktable-mcp-design.md
├── tests/
└── pyproject.toml        # deps, entry point (darktable-mcp), Python 3.10+

Running the tests

pytest -m "not integration"     # unit tests — mocked gdbus, no darktable needed
pytest -m integration           # smoke tests — require a running darktable GUI

Unit tests cover bridge reply-parsing and error mapping and assert that the Python and Lua control registries stay in sync. The integration marker is opt-in (skipped in CI) because it needs a live session.


License

MIT. See the license field in pyproject.toml.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选