@pired/lightroom-mcp 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/LightroomMCP.lrplugin/HandlerAI.lua +466 -0
  4. package/dist/LightroomMCP.lrplugin/HandlerAIMasks.lua +549 -0
  5. package/dist/LightroomMCP.lrplugin/HandlerCatalog.lua +256 -0
  6. package/dist/LightroomMCP.lrplugin/HandlerCollections.lua +337 -0
  7. package/dist/LightroomMCP.lrplugin/HandlerDevelop.lua +1668 -0
  8. package/dist/LightroomMCP.lrplugin/HandlerExport.lua +149 -0
  9. package/dist/LightroomMCP.lrplugin/HandlerImport.lua +108 -0
  10. package/dist/LightroomMCP.lrplugin/HandlerLocalAdjustments.lua +330 -0
  11. package/dist/LightroomMCP.lrplugin/HandlerMetadata.lua +169 -0
  12. package/dist/LightroomMCP.lrplugin/HandlerOrganization.lua +783 -0
  13. package/dist/LightroomMCP.lrplugin/HandlerPreview.lua +189 -0
  14. package/dist/LightroomMCP.lrplugin/HandlerSearch.lua +185 -0
  15. package/dist/LightroomMCP.lrplugin/HandlerSelection.lua +302 -0
  16. package/dist/LightroomMCP.lrplugin/HandlerSpots.lua +313 -0
  17. package/dist/LightroomMCP.lrplugin/HandlerWatermark.lua +59 -0
  18. package/dist/LightroomMCP.lrplugin/Info.lua +25 -0
  19. package/dist/LightroomMCP.lrplugin/JSON.lua +270 -0
  20. package/dist/LightroomMCP.lrplugin/Log.lua +92 -0
  21. package/dist/LightroomMCP.lrplugin/MenuShowStatus.lua +22 -0
  22. package/dist/LightroomMCP.lrplugin/PhotoLookup.lua +39 -0
  23. package/dist/LightroomMCP.lrplugin/PluginInfoProvider.lua +865 -0
  24. package/dist/LightroomMCP.lrplugin/PluginInit.lua +48 -0
  25. package/dist/cli.d.ts +6 -0
  26. package/dist/cli.d.ts.map +1 -0
  27. package/dist/cli.js +31 -0
  28. package/dist/cli.js.map +1 -0
  29. package/dist/configs/claude-code.mcp.json +9 -0
  30. package/dist/configs/claude-desktop.json +12 -0
  31. package/dist/configs/generic-stdio-client.json +10 -0
  32. package/dist/configs/mcp-proxy-http.json +9 -0
  33. package/dist/create-server.d.ts +12 -0
  34. package/dist/create-server.d.ts.map +1 -0
  35. package/dist/create-server.js +17 -0
  36. package/dist/create-server.js.map +1 -0
  37. package/dist/deepseek-harness/README.md +112 -0
  38. package/dist/deepseek-harness/deepseek-mcp-harness.mjs +292 -0
  39. package/dist/dispatcher.d.ts +34 -0
  40. package/dist/dispatcher.d.ts.map +1 -0
  41. package/dist/dispatcher.js +72 -0
  42. package/dist/dispatcher.js.map +1 -0
  43. package/dist/heartbeat.d.ts +17 -0
  44. package/dist/heartbeat.d.ts.map +1 -0
  45. package/dist/heartbeat.js +36 -0
  46. package/dist/heartbeat.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +255 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/install-plugin.d.ts +16 -0
  52. package/dist/install-plugin.d.ts.map +1 -0
  53. package/dist/install-plugin.js +125 -0
  54. package/dist/install-plugin.js.map +1 -0
  55. package/dist/instance-lock.d.ts +5 -0
  56. package/dist/instance-lock.d.ts.map +1 -0
  57. package/dist/instance-lock.js +77 -0
  58. package/dist/instance-lock.js.map +1 -0
  59. package/dist/list-tools-handler.d.ts +16 -0
  60. package/dist/list-tools-handler.d.ts.map +1 -0
  61. package/dist/list-tools-handler.js +50 -0
  62. package/dist/list-tools-handler.js.map +1 -0
  63. package/dist/plugin-liveness.d.ts +48 -0
  64. package/dist/plugin-liveness.d.ts.map +1 -0
  65. package/dist/plugin-liveness.js +75 -0
  66. package/dist/plugin-liveness.js.map +1 -0
  67. package/dist/plugin-socket.d.ts +32 -0
  68. package/dist/plugin-socket.d.ts.map +1 -0
  69. package/dist/plugin-socket.js +97 -0
  70. package/dist/plugin-socket.js.map +1 -0
  71. package/dist/ports.d.ts +3 -0
  72. package/dist/ports.d.ts.map +1 -0
  73. package/dist/ports.js +22 -0
  74. package/dist/ports.js.map +1 -0
  75. package/dist/token.d.ts +3 -0
  76. package/dist/token.d.ts.map +1 -0
  77. package/dist/token.js +23 -0
  78. package/dist/token.js.map +1 -0
  79. package/dist/tool-contracts.d.ts +13 -0
  80. package/dist/tool-contracts.d.ts.map +1 -0
  81. package/dist/tool-contracts.js +1422 -0
  82. package/dist/tool-contracts.js.map +1 -0
  83. package/dist/tool-handler.d.ts +23 -0
  84. package/dist/tool-handler.d.ts.map +1 -0
  85. package/dist/tool-handler.js +96 -0
  86. package/dist/tool-handler.js.map +1 -0
  87. package/dist/validate-args.d.ts +7 -0
  88. package/dist/validate-args.d.ts.map +1 -0
  89. package/dist/validate-args.js +37 -0
  90. package/dist/validate-args.js.map +1 -0
  91. package/dist/version.d.ts +2 -0
  92. package/dist/version.d.ts.map +1 -0
  93. package/dist/version.js +4 -0
  94. package/dist/version.js.map +1 -0
  95. package/dist/wait-until.d.ts +7 -0
  96. package/dist/wait-until.d.ts.map +1 -0
  97. package/dist/wait-until.js +15 -0
  98. package/dist/wait-until.js.map +1 -0
  99. package/package.json +73 -0
@@ -0,0 +1,77 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ function pidIsAlive(pid) {
5
+ try {
6
+ process.kill(pid, 0);
7
+ return true;
8
+ }
9
+ catch (err) {
10
+ return err.code === "EPERM";
11
+ }
12
+ }
13
+ function readPid(pidFile) {
14
+ try {
15
+ const raw = fs.readFileSync(pidFile, "utf8").trim();
16
+ const parsed = Number(raw);
17
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ export function acquireInstanceLock(requestPort, responsePort, baseDir = path.join(os.homedir(), ".config", "lightroom-mcp")) {
24
+ fs.mkdirSync(baseDir, { recursive: true, mode: 0o700 });
25
+ const lockFile = path.join(baseDir, `bridge-${requestPort}-${responsePort}.lock`);
26
+ while (true) {
27
+ let fd = null;
28
+ try {
29
+ fd = fs.openSync(lockFile, "wx", 0o600);
30
+ fs.writeFileSync(fd, `${process.pid}\n`, { encoding: "utf8" });
31
+ break;
32
+ }
33
+ catch (err) {
34
+ const code = err.code;
35
+ if (code !== "EEXIST") {
36
+ throw err;
37
+ }
38
+ const existingPid = readPid(lockFile);
39
+ if (existingPid && pidIsAlive(existingPid)) {
40
+ throw new Error(`Another Lightroom MCP bridge is already running for ports ${requestPort}/${responsePort} (pid ${existingPid})`);
41
+ }
42
+ try {
43
+ fs.unlinkSync(lockFile);
44
+ }
45
+ catch (unlinkErr) {
46
+ if (unlinkErr.code !== "ENOENT")
47
+ throw unlinkErr;
48
+ }
49
+ }
50
+ finally {
51
+ if (fd !== null)
52
+ fs.closeSync(fd);
53
+ }
54
+ }
55
+ let released = false;
56
+ const exitHandler = () => release();
57
+ const signalHandler = () => {
58
+ release();
59
+ process.exit(0);
60
+ };
61
+ const release = () => {
62
+ if (released)
63
+ return;
64
+ released = true;
65
+ process.off("exit", exitHandler);
66
+ process.off("SIGINT", signalHandler);
67
+ process.off("SIGTERM", signalHandler);
68
+ if (readPid(lockFile) === process.pid) {
69
+ fs.unlinkSync(lockFile);
70
+ }
71
+ };
72
+ process.once("exit", exitHandler);
73
+ process.once("SIGINT", signalHandler);
74
+ process.once("SIGTERM", signalHandler);
75
+ return { release };
76
+ }
77
+ //# sourceMappingURL=instance-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-lock.js","sourceRoot":"","sources":["../src/instance-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare const TOOL_DEFINITIONS: Tool[];
3
+ /**
4
+ * Server-side tool filtering, primarily for token-constrained MCP clients
5
+ * (e.g., a DeepSeek harness with function calling): set LIGHTROOM_MCP_TOOLS
6
+ * to a comma-separated list of tool names to expose only those, or "all"
7
+ * (the default) for the full set. Unknown names are reported on stderr and
8
+ * skipped rather than failing the server — a typo should not take the
9
+ * bridge down, and the mistake stays visible in the client's logs.
10
+ */
11
+ export declare const TOOL_FILTER_ENV_VAR = "LIGHTROOM_MCP_TOOLS";
12
+ export declare function selectToolDefinitions(filterRaw: string | undefined, onWarning?: (message: string) => void): Tool[];
13
+ export declare function listToolsHandler(toolFilterRaw?: string, onWarning?: (message: string) => void): {
14
+ tools: Tool[];
15
+ };
16
+ //# sourceMappingURL=list-tools-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-tools-handler.d.ts","sourceRoot":"","sources":["../src/list-tools-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG/D,eAAO,MAAM,gBAAgB,EAAE,IAAI,EAMlC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAYzD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACpC,IAAI,EAAE,CAwBR;AAED,wBAAgB,gBAAgB,CAC9B,aAAa,CAAC,EAAE,MAAM,EACtB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GACpC;IAAE,KAAK,EAAE,IAAI,EAAE,CAAA;CAAE,CAEnB"}
@@ -0,0 +1,50 @@
1
+ import { TOOL_CONTRACTS } from "./tool-contracts.js";
2
+ export const TOOL_DEFINITIONS = TOOL_CONTRACTS.map(({ name, description, inputSchema }) => ({
3
+ name,
4
+ description,
5
+ inputSchema,
6
+ }));
7
+ /**
8
+ * Server-side tool filtering, primarily for token-constrained MCP clients
9
+ * (e.g., a DeepSeek harness with function calling): set LIGHTROOM_MCP_TOOLS
10
+ * to a comma-separated list of tool names to expose only those, or "all"
11
+ * (the default) for the full set. Unknown names are reported on stderr and
12
+ * skipped rather than failing the server — a typo should not take the
13
+ * bridge down, and the mistake stays visible in the client's logs.
14
+ */
15
+ export const TOOL_FILTER_ENV_VAR = "LIGHTROOM_MCP_TOOLS";
16
+ function parseToolFilter(raw) {
17
+ if (raw === undefined || raw.trim() === "" || raw.trim().toLowerCase() === "all") {
18
+ return null;
19
+ }
20
+ return raw
21
+ .split(",")
22
+ .map((name) => name.trim())
23
+ .filter((name) => name !== "");
24
+ }
25
+ export function selectToolDefinitions(filterRaw, onWarning) {
26
+ const filter = parseToolFilter(filterRaw);
27
+ if (filter === null)
28
+ return TOOL_DEFINITIONS;
29
+ const byName = new Map(TOOL_DEFINITIONS.map((tool) => [tool.name, tool]));
30
+ const selected = [];
31
+ for (const name of filter) {
32
+ const tool = byName.get(name);
33
+ if (tool) {
34
+ selected.push(tool);
35
+ }
36
+ else if (onWarning) {
37
+ onWarning(`[tools] LIGHTROOM_MCP_TOOLS: unknown tool '${name}' (ignored); ` +
38
+ `run the server with --help or list tools to see valid names`);
39
+ }
40
+ }
41
+ if (selected.length === 0 && onWarning) {
42
+ onWarning("[tools] LIGHTROOM_MCP_TOOLS matched no tools; exposing the full tool set instead");
43
+ return TOOL_DEFINITIONS;
44
+ }
45
+ return selected;
46
+ }
47
+ export function listToolsHandler(toolFilterRaw, onWarning) {
48
+ return { tools: selectToolDefinitions(toolFilterRaw, onWarning) };
49
+ }
50
+ //# sourceMappingURL=list-tools-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-tools-handler.js","sourceRoot":"","sources":["../src/list-tools-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,CAAC,MAAM,gBAAgB,GAAW,cAAc,CAAC,GAAG,CACxD,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;IACvC,IAAI;IACJ,WAAW;IACX,WAAW;CACZ,CAAC,CACH,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAEzD,SAAS,eAAe,CAAC,GAAuB;IAC9C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK,EAAE,CAAC;QACjF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,SAA6B,EAC7B,SAAqC;IAErC,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,gBAAgB,CAAC;IAE7C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1E,MAAM,QAAQ,GAAW,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,IAAI,EAAE,CAAC;YACT,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;aAAM,IAAI,SAAS,EAAE,CAAC;YACrB,SAAS,CACP,8CAA8C,IAAI,eAAe;gBAC/D,6DAA6D,CAChE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC;QACvC,SAAS,CACP,kFAAkF,CACnF,CAAC;QACF,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,aAAsB,EACtB,SAAqC;IAErC,OAAO,EAAE,KAAK,EAAE,qBAAqB,CAAC,aAAa,EAAE,SAAS,CAAC,EAAE,CAAC;AACpE,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Whether the plugin is actually answering, as opposed to merely accepting TCP
3
+ * connections.
4
+ *
5
+ * The plugin serves one client per port. A second bridge still connects
6
+ * successfully -- the kernel accepts, the plugin never reads -- so socket state
7
+ * alone reports "ready" while every request sits unanswered until it times out
8
+ * (issue 215). Pings settle that question, so readiness follows the pings:
9
+ *
10
+ * unknown no ping has settled yet. A tool call waits for the in-flight
11
+ * probe rather than racing it, so no call is ever dispatched
12
+ * into a connection nobody is reading.
13
+ * responsive a ping came back.
14
+ * unresponsive a ping timed out. Tool calls fail immediately with an
15
+ * explanation instead of burning the full request timeout each.
16
+ */
17
+ export type PluginLivenessState = "unknown" | "responsive" | "unresponsive";
18
+ export declare const SHADOW_BRIDGE_MESSAGE: string;
19
+ export declare class PluginLiveness {
20
+ private state;
21
+ private pending;
22
+ private resolvePending;
23
+ private generation;
24
+ markResponsive(): void;
25
+ markUnresponsive(): void;
26
+ reset(): void;
27
+ /**
28
+ * Claims the right to probe. Returns a token to hand back to settleProbe, or
29
+ * null when a probe is already running: without this the recovery interval
30
+ * could stack probes whose verdicts then land in arbitrary order, letting a
31
+ * slow timeout overwrite a fast success.
32
+ */
33
+ beginProbe(): number | null;
34
+ /**
35
+ * Records a probe verdict, ignoring one that belongs to a connection already
36
+ * torn down (reset bumps the generation). Returns whether it was applied.
37
+ */
38
+ settleProbe(token: number, responsive: boolean): boolean;
39
+ /**
40
+ * Resolves once the current probe has settled. Returns immediately when the
41
+ * state is already known or no probe is running.
42
+ */
43
+ settled(): Promise<void>;
44
+ current(): PluginLivenessState;
45
+ isUsable(): boolean;
46
+ private release;
47
+ }
48
+ //# sourceMappingURL=plugin-liveness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-liveness.d.ts","sourceRoot":"","sources":["../src/plugin-liveness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,GAAG,cAAc,CAAC;AAE5E,eAAO,MAAM,qBAAqB,QAKF,CAAC;AAEjC,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAkC;IAC/C,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,UAAU,CAAK;IAEvB,cAAc,IAAI,IAAI,CAGrB;IAED,gBAAgB,IAAI,IAAI,CAGvB;IAED,KAAK,IAAI,IAAI,CAIZ;IAED;;;;;OAKG;IACH,UAAU,IAAI,MAAM,GAAG,IAAI,CAM1B;IAED;;;OAGG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAQvD;IAED;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7B;IAED,OAAO,IAAI,mBAAmB,CAE7B;IAED,QAAQ,IAAI,OAAO,CAElB;IAED,OAAO,CAAC,OAAO;CAMhB"}
@@ -0,0 +1,75 @@
1
+ export const SHADOW_BRIDGE_MESSAGE = "Connected to the Lightroom plugin's ports, but it is not answering. " +
2
+ "The plugin serves one client at a time, so another lightroom-mcp process " +
3
+ "is most likely holding the connection. Quit any other MCP client or stale " +
4
+ "bridge process (pgrep -fl lightroom-mcp), or restart the plugin from " +
5
+ "Lightroom's Plug-in Manager.";
6
+ export class PluginLiveness {
7
+ state = "unknown";
8
+ pending = null;
9
+ resolvePending = null;
10
+ generation = 0;
11
+ markResponsive() {
12
+ this.state = "responsive";
13
+ this.release();
14
+ }
15
+ markUnresponsive() {
16
+ this.state = "unresponsive";
17
+ this.release();
18
+ }
19
+ reset() {
20
+ this.state = "unknown";
21
+ this.generation += 1;
22
+ this.release();
23
+ }
24
+ /**
25
+ * Claims the right to probe. Returns a token to hand back to settleProbe, or
26
+ * null when a probe is already running: without this the recovery interval
27
+ * could stack probes whose verdicts then land in arbitrary order, letting a
28
+ * slow timeout overwrite a fast success.
29
+ */
30
+ beginProbe() {
31
+ if (this.pending)
32
+ return null;
33
+ this.pending = new Promise((resolve) => {
34
+ this.resolvePending = resolve;
35
+ });
36
+ return this.generation;
37
+ }
38
+ /**
39
+ * Records a probe verdict, ignoring one that belongs to a connection already
40
+ * torn down (reset bumps the generation). Returns whether it was applied.
41
+ */
42
+ settleProbe(token, responsive) {
43
+ if (token !== this.generation)
44
+ return false;
45
+ if (responsive) {
46
+ this.markResponsive();
47
+ }
48
+ else {
49
+ this.markUnresponsive();
50
+ }
51
+ return true;
52
+ }
53
+ /**
54
+ * Resolves once the current probe has settled. Returns immediately when the
55
+ * state is already known or no probe is running.
56
+ */
57
+ async settled() {
58
+ if (this.state !== "unknown" || !this.pending)
59
+ return;
60
+ await this.pending;
61
+ }
62
+ current() {
63
+ return this.state;
64
+ }
65
+ isUsable() {
66
+ return this.state !== "unresponsive";
67
+ }
68
+ release() {
69
+ const resolve = this.resolvePending;
70
+ this.pending = null;
71
+ this.resolvePending = null;
72
+ resolve?.();
73
+ }
74
+ }
75
+ //# sourceMappingURL=plugin-liveness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-liveness.js","sourceRoot":"","sources":["../src/plugin-liveness.ts"],"names":[],"mappings":"AAkBA,MAAM,CAAC,MAAM,qBAAqB,GAChC,sEAAsE;IACtE,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,8BAA8B,CAAC;AAEjC,MAAM,OAAO,cAAc;IACjB,KAAK,GAAwB,SAAS,CAAC;IACvC,OAAO,GAAyB,IAAI,CAAC;IACrC,cAAc,GAAwB,IAAI,CAAC;IAC3C,UAAU,GAAG,CAAC,CAAC;IAEvB,cAAc;QACZ,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC;QAC1B,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,gBAAgB;QACd,IAAI,CAAC,KAAK,GAAG,cAAc,CAAC;QAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACvB,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED;;;;;OAKG;IACH,UAAU;QACR,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC3C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,KAAa,EAAE,UAAmB;QAC5C,IAAI,KAAK,KAAK,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAC5C,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QACtD,MAAM,IAAI,CAAC,OAAO,CAAC;IACrB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,KAAK,cAAc,CAAC;IACvC,CAAC;IAEO,OAAO;QACb,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,32 @@
1
+ export interface PluginSocketOptions {
2
+ port: number;
3
+ label: string;
4
+ host?: string;
5
+ reconnectDelayMs?: number;
6
+ onLine?: (line: string) => void;
7
+ onConnect?: () => void;
8
+ onDisconnect?: () => void;
9
+ log?: (msg: string) => void;
10
+ }
11
+ export declare class PluginSocket {
12
+ private socket;
13
+ private connected;
14
+ private buffer;
15
+ private reconnectTimer;
16
+ private stopped;
17
+ private readonly port;
18
+ private readonly host;
19
+ private readonly label;
20
+ private readonly reconnectDelayMs;
21
+ private readonly onLine?;
22
+ private readonly onConnect?;
23
+ private readonly onDisconnect?;
24
+ private readonly log;
25
+ constructor(opts: PluginSocketOptions);
26
+ connect(): void;
27
+ private scheduleReconnect;
28
+ send(line: string): boolean;
29
+ isConnected(): boolean;
30
+ stop(): void;
31
+ }
32
+ //# sourceMappingURL=plugin-socket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-socket.d.ts","sourceRoot":"","sources":["../src/plugin-socket.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAa;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAwB;IAE5C,YAAY,IAAI,EAAE,mBAAmB,EASpC;IAED,OAAO,IAAI,IAAI,CAwCd;IAED,OAAO,CAAC,iBAAiB;IAQzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAI1B;IAED,WAAW,IAAI,OAAO,CAErB;IAED,IAAI,IAAI,IAAI,CAWX;CACF"}
@@ -0,0 +1,97 @@
1
+ import net from "node:net";
2
+ export class PluginSocket {
3
+ socket = null;
4
+ connected = false;
5
+ buffer = "";
6
+ reconnectTimer = null;
7
+ stopped = false;
8
+ port;
9
+ host;
10
+ label;
11
+ reconnectDelayMs;
12
+ onLine;
13
+ onConnect;
14
+ onDisconnect;
15
+ log;
16
+ constructor(opts) {
17
+ this.port = opts.port;
18
+ this.label = opts.label;
19
+ this.host = opts.host ?? "127.0.0.1";
20
+ this.reconnectDelayMs = opts.reconnectDelayMs ?? 1000;
21
+ this.onLine = opts.onLine;
22
+ this.onConnect = opts.onConnect;
23
+ this.onDisconnect = opts.onDisconnect;
24
+ this.log = opts.log ?? ((msg) => console.error(msg));
25
+ }
26
+ connect() {
27
+ if (this.stopped || this.socket)
28
+ return;
29
+ const sock = new net.Socket();
30
+ sock.setEncoding("utf8");
31
+ this.socket = sock;
32
+ sock.on("connect", () => {
33
+ this.connected = true;
34
+ this.log(`[${this.label}] connected to ${this.host}:${this.port}`);
35
+ this.onConnect?.();
36
+ });
37
+ sock.on("data", (chunk) => {
38
+ if (!this.onLine)
39
+ return;
40
+ this.buffer += chunk;
41
+ let idx;
42
+ while ((idx = this.buffer.indexOf("\n")) !== -1) {
43
+ const line = this.buffer.slice(0, idx).trim();
44
+ this.buffer = this.buffer.slice(idx + 1);
45
+ if (line)
46
+ this.onLine(line);
47
+ }
48
+ });
49
+ sock.on("error", (err) => {
50
+ if (err.code !== "ECONNREFUSED") {
51
+ this.log(`[${this.label}] error: ${err.message}`);
52
+ }
53
+ });
54
+ sock.on("close", () => {
55
+ const wasConnected = this.connected;
56
+ this.connected = false;
57
+ this.socket = null;
58
+ this.buffer = "";
59
+ if (wasConnected)
60
+ this.log(`[${this.label}] disconnected, reconnecting`);
61
+ if (wasConnected)
62
+ this.onDisconnect?.();
63
+ this.scheduleReconnect();
64
+ });
65
+ sock.connect(this.port, this.host);
66
+ }
67
+ scheduleReconnect() {
68
+ if (this.stopped || this.reconnectTimer)
69
+ return;
70
+ this.reconnectTimer = setTimeout(() => {
71
+ this.reconnectTimer = null;
72
+ this.connect();
73
+ }, this.reconnectDelayMs);
74
+ }
75
+ send(line) {
76
+ if (!this.socket || !this.connected)
77
+ return false;
78
+ this.socket.write(line + "\n");
79
+ return true;
80
+ }
81
+ isConnected() {
82
+ return this.connected;
83
+ }
84
+ stop() {
85
+ this.stopped = true;
86
+ if (this.reconnectTimer) {
87
+ clearTimeout(this.reconnectTimer);
88
+ this.reconnectTimer = null;
89
+ }
90
+ if (this.socket) {
91
+ this.socket.destroy();
92
+ this.socket = null;
93
+ }
94
+ this.connected = false;
95
+ }
96
+ }
97
+ //# sourceMappingURL=plugin-socket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-socket.js","sourceRoot":"","sources":["../src/plugin-socket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function requestPort(): number;
2
+ export declare function responsePort(): number;
3
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAgBA,wBAAgB,WAAW,IAAI,MAAM,CAMpC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAMrC"}
package/dist/ports.js ADDED
@@ -0,0 +1,22 @@
1
+ const DEFAULT_REQUEST_PORT = 58763;
2
+ const DEFAULT_RESPONSE_PORT = 58764;
3
+ function parsePort(raw, fallback, name) {
4
+ if (raw === undefined || raw.trim() === "")
5
+ return fallback;
6
+ const trimmed = raw.trim();
7
+ if (!/^[0-9]+$/.test(trimmed)) {
8
+ throw new Error(`${name} must be a positive integer 1-65535, got "${raw}"`);
9
+ }
10
+ const n = Number(trimmed);
11
+ if (!Number.isInteger(n) || n < 1 || n > 65535) {
12
+ throw new Error(`${name} must be a positive integer 1-65535, got "${raw}"`);
13
+ }
14
+ return n;
15
+ }
16
+ export function requestPort() {
17
+ return parsePort(process.env.LIGHTROOM_MCP_REQUEST_PORT, DEFAULT_REQUEST_PORT, "LIGHTROOM_MCP_REQUEST_PORT");
18
+ }
19
+ export function responsePort() {
20
+ return parsePort(process.env.LIGHTROOM_MCP_RESPONSE_PORT, DEFAULT_RESPONSE_PORT, "LIGHTROOM_MCP_RESPONSE_PORT");
21
+ }
22
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA,MAAM,oBAAoB,GAAG,KAAK,CAAC;AACnC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAEpC,SAAS,SAAS,CAAC,GAAuB,EAAE,QAAgB,EAAE,IAAY;IACxE,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC5D,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6CAA6C,GAAG,GAAG,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC1B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,6CAA6C,GAAG,GAAG,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,CACd,OAAO,CAAC,GAAG,CAAC,0BAA0B,EACtC,oBAAoB,EACpB,4BAA4B,CAC7B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,SAAS,CACd,OAAO,CAAC,GAAG,CAAC,2BAA2B,EACvC,qBAAqB,EACrB,6BAA6B,CAC9B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function tokenFilePath(): string;
2
+ export declare function readToken(): string;
3
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../src/token.ts"],"names":[],"mappings":"AAIA,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAclC"}
package/dist/token.js ADDED
@@ -0,0 +1,23 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ export function tokenFilePath() {
5
+ return (process.env.LIGHTROOM_MCP_TOKEN_PATH ??
6
+ path.join(os.homedir(), ".config", "lightroom-mcp", "token"));
7
+ }
8
+ export function readToken() {
9
+ const p = tokenFilePath();
10
+ let raw;
11
+ try {
12
+ raw = fs.readFileSync(p, "utf8");
13
+ }
14
+ catch {
15
+ throw new Error(`Lightroom MCP token file not found at ${p}. ` +
16
+ `Open Lightroom and click 'Start Server' in Plug-in Manager to generate one.`);
17
+ }
18
+ const token = raw.trim();
19
+ if (!token)
20
+ throw new Error(`Token file ${p} is empty`);
21
+ return token;
22
+ }
23
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../src/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,aAAa;IAC3B,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,wBAAwB;QACpC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,CAAC,CAC7D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,CAAC,GAAG,aAAa,EAAE,CAAC;IAC1B,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yCAAyC,CAAC,IAAI;YAC5C,6EAA6E,CAChF,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IACxD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Tool } from "@modelcontextprotocol/sdk/types.js";
2
+ type InputSchema = Tool["inputSchema"];
3
+ export interface ToolContract {
4
+ name: string;
5
+ description: string;
6
+ luaHandler: string;
7
+ inputSchema: InputSchema;
8
+ }
9
+ export declare const POINT_CURVE_SETTING_KEYS: readonly ["ToneCurvePV2012", "ToneCurvePV2012Red", "ToneCurvePV2012Green", "ToneCurvePV2012Blue"];
10
+ export declare const DEVELOP_SETTING_KEYS: readonly ["WhiteBalance", "Temperature", "Tint", "Exposure2012", "Contrast2012", "Highlights2012", "Shadows2012", "Whites2012", "Blacks2012", "Texture", "Clarity2012", "Dehaze", "Vibrance", "Saturation", "SaturationAdjustmentRed", "SaturationAdjustmentOrange", "SaturationAdjustmentYellow", "SaturationAdjustmentGreen", "SaturationAdjustmentAqua", "SaturationAdjustmentBlue", "SaturationAdjustmentPurple", "SaturationAdjustmentMagenta", "HueAdjustmentRed", "HueAdjustmentOrange", "HueAdjustmentYellow", "HueAdjustmentGreen", "HueAdjustmentAqua", "HueAdjustmentBlue", "HueAdjustmentPurple", "HueAdjustmentMagenta", "LuminanceAdjustmentRed", "LuminanceAdjustmentOrange", "LuminanceAdjustmentYellow", "LuminanceAdjustmentGreen", "LuminanceAdjustmentAqua", "LuminanceAdjustmentBlue", "LuminanceAdjustmentPurple", "LuminanceAdjustmentMagenta", "ParametricShadows", "ParametricDarks", "ParametricLights", "ParametricHighlights", "ParametricShadowSplit", "ParametricMidtoneSplit", "ParametricHighlightSplit", "ToneCurvePV2012", "ToneCurvePV2012Red", "ToneCurvePV2012Green", "ToneCurvePV2012Blue", "ToneCurveName2012", "ConvertToGrayscale", "Sharpness", "SharpenRadius", "SharpenDetail", "SharpenEdgeMasking", "LuminanceSmoothing", "LuminanceNoiseReductionDetail", "LuminanceNoiseReductionContrast", "ColorNoiseReduction", "ColorNoiseReductionDetail", "ColorNoiseReductionSmoothness", "LensProfileEnable", "LensManualDistortionAmount", "PerspectiveVertical", "PerspectiveHorizontal", "PerspectiveRotate", "PerspectiveScale", "PerspectiveAspect", "PerspectiveUpright", "PostCropVignetteAmount", "PostCropVignetteMidpoint", "PostCropVignetteRoundness", "PostCropVignetteFeather", "PostCropVignetteStyle", "GrainAmount", "GrainSize", "GrainFrequency", "CropTop", "CropLeft", "CropBottom", "CropRight", "CropAngle"];
11
+ export declare const TOOL_CONTRACTS: ToolContract[];
12
+ export {};
13
+ //# sourceMappingURL=tool-contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-contracts.d.ts","sourceRoot":"","sources":["../src/tool-contracts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAE/D,KAAK,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;AAEvC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAKD,eAAO,MAAM,wBAAwB,YACnC,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,CACb,CAAC;AAEX,eAAO,MAAM,oBAAoB,6wDAgFvB,CAAC;AAqHX,eAAO,MAAM,cAAc,EAAE,YAAY,EA6wCxC,CAAC"}