Skip to content

The bridge protocol

darkray-mcp and darkray_bridge both speak one small protocol to DarkRay, the bridge. This page is for writing a client in another language; from Python, use the Python client instead.

While DarkRay runs, it keeps a discovery file at ~/.darkray/bridge.json (on Windows, C:\Users\<you>\.darkray\bridge.json). With MCP on:

{"protocol": 1, "enabled": true, "pid": 4120, "app_version": "0.9.0", "port": 51234, "token": "<64 hex characters>"}

With MCP off, there is no port or token:

{"protocol": 1, "enabled": false, "pid": 4120, "app_version": "0.9.0"}
Key Holds
protocol The highest bridge protocol this DarkRay speaks
enabled Whether MCP is on
port The TCP port on 127.0.0.1
token The secret the handshake must present; new each time MCP turns on
pid DarkRay’s process id
app_version DarkRay’s version

Read it fresh before each connection. No file means DarkRay isn’t running; a file whose pid is no longer running was left by a DarkRay that quit. DarkRay writes the file atomically (a temporary file renamed over it), readable only by you on macOS and Linux, and removes it when it quits. The DARKRAY_BRIDGE_FILE environment variable points the Python client and darkray-mcp at another file.

Open a TCP connection to 127.0.0.1 on port. Messages in both directions are JSON-RPC 2.0, one per line: compact UTF-8 JSON ending in \n, the same framing as MCP’s stdio transport. A message over 64 MB closes the connection.

The first request must be bridge.hello, with the token:

{"jsonrpc": "2.0", "id": 1, "method": "bridge.hello", "params": {"token": "…", "protocol": 1, "client": "my-tool 0.1"}}
{"jsonrpc": "2.0", "id": 1, "result": {"protocol": 1, "app_version": "0.9.0"}}

client names your program on the Console’s MCP card and in the In use tooltip. Any other first request, or a wrong token, gets error -32001 and the connection closes. A protocol that isn’t a whole number of 1 or more gets error -32600 and the connection closes. Tokens are compared in constant time.

Each MCP tool has a bridge method; the MCP tool reference lists it under each tool (“Bridge method”), for example search_photos is photos.search. A request’s params are the tool’s arguments, unchanged:

{"jsonrpc": "2.0", "id": 2, "method": "photos.search", "params": {"query": "rating:>=4", "limit": 2}}
{"jsonrpc": "2.0", "id": 2, "result": {"total": 318, "photos": [{"id": 5120, "filename": "DSCF2210.RAF", "…": "…"}]}}
  • Several requests can be in flight on one connection. Replies may come out of order (a preview finishes after a later quick request), so match them by id.
  • DarkRay checks every request against its method’s schema (unknown and missing parameters, types, ranges, choices, list limits) before running it, and fills in defaults.
  • Protocol 1 has no notifications. DarkRay ignores any you send, and sends none: follow jobs by polling jobs.list.
  • A preview’s jpeg is the JPEG file in base64.
  • Several clients may connect at once; their requests take turns.

An error reply carries code, message (a sentence saying what to do next) and sometimes data:

Code Name When data
-32700 parse error Not JSON
-32600 invalid request Not JSON-RPC 2.0, or no protocol in common
-32601 unknown method Not a method of this DarkRay method
-32602 invalid params A missing, unknown or mistyped parameter param
-32603 internal error Treated as failed by the Python client
-32001 unauthorized No hello, or a wrong token
-32002 not found A catalog, photo, collection, preset, look or job kind, ids
-32003 out of range A value outside its range, or an unknown choice field with min and max, or choices
-32004 too many Past a limit: 1,000 photos per call, 500 results per page limit, param
-32005 not allowed Can’t run now or here reason
-32006 failed The work started and failed reason

A call over many photos checks every id and value before changing anything; when it fails, nothing changed. The Python client turns each code into an exception (see Errors and timeouts).

protocol is a whole number. Adding a method, an optional parameter or a result field doesn’t change it; removing something or changing what it means does. DarkRay answers the hello with the lower of its protocol and yours, and that is the protocol for the connection. A method this DarkRay doesn’t have yet gets -32601.

Technical details: how darkray-mcp maps MCP onto the bridge

darkray-mcp lists the MCP tools with tools/list, from the same table as the Python client (darkray_bridge/methods.py), and turns each tools/call into one bridge request, passing arguments as params. A get_preview result becomes MCP image content (image/jpeg) plus a text item with its size, region and preparation; every other result is one text item holding the JSON. Bridge errors become tool results with isError: true holding the message; only an unknown tool or a malformed request is a JSON-RPC error. notifications/cancelled stops waiting for a call.

It accepts MCP’s legacy handshake (initialize, versions 2024-11-05, 2025-03-26, 2025-06-18 and 2025-11-25) and the 2026-07-28 version, which sends the protocol version and client capabilities in each request’s _meta and may begin with server/discover. An unsupported version gets error -32022 with the supported list.