mcp-relight-harmonize 1.0.0 → 1.0.2

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.
@@ -62,6 +62,11 @@ To craft targeted generative prompts:
62
62
  3. For **GPT Image**: outputs natural descriptive photography directives (85mm f/2.0, physical illumination).
63
63
  4. For **Nano Banana**: outputs dense optical tokens (roughness index, raytraced bounce, ground contact shadow, azimuth).
64
64
 
65
+ ### Workflow E: Output Cache Inspection
66
+ To inspect or discover previously generated relight variations or composite outputs:
67
+ 1. Call tool `list_cached_variations({ cache_dir?: "<path>" })`.
68
+ 2. Inspect `files` list containing filename, full path, byte size, and last modified timestamp for all output artifacts.
69
+
65
70
  ---
66
71
 
67
72
  ## 3. Reference Documentation
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MarwanDevSpace
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # mcp-relight-harmonize
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/mcp-relight-harmonize.svg)](https://www.npmjs.com/package/mcp-relight-harmonize)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![CI](https://github.com/MarwanDevSpace/mcp-relight-harmonize/actions/workflows/ci.yml/badge.svg)](https://github.com/MarwanDevSpace/mcp-relight-harmonize/actions)
6
+ [![Glama](https://img.shields.io/badge/Glama-Listed-24b47e.svg)](https://glama.ai/mcp/servers/MarwanDevSpace/mcp-relight-harmonize)
7
+
3
8
  **mcp-relight-harmonize** is an enterprise-grade TypeScript Model Context Protocol (MCP) server and Antigravity Skill engineered by **MarwanDevSpace**. It delivers local optical profiling, physically-grounded relighting variations, contact-aware composite harmonization, and precision prompt synthesis specifically targeting **GPT Image** (DALL-E 3 / GPT-4o) and **Nano Banana**.
4
9
 
5
10
  ---
@@ -41,6 +46,7 @@
41
46
  | `generate_relight_variations` | `image_path: string`, `target_lighting?: string`, `output_dir?: string` | 4 generated images (Ambient, Dramatic, Rim, Mood) + EV adjustments log. |
42
47
  | `harmonize_composite` | `foreground_path: string`, `background_path: string`, `blend_mode?: string` | Composited image with harmonized CCT, Reinhard color transfer, and contact shadow. |
43
48
  | `synthesize_diffusion_prompt` | `image_path: string`, `user_intent?: string`, `target_model?: "gpt_image" \| "nano_banana"` | Enhancement prompt, Relighting prompt, and calibrated generation parameters. |
49
+ | `list_cached_variations` | `cache_dir?: string` | Inventory of generated relight variations and composite artifacts in the output cache. |
44
50
 
45
51
  ---
46
52
 
@@ -92,6 +98,15 @@ Or via npx when published:
92
98
  }
93
99
  ```
94
100
 
101
+ ### 3. Docker Deployment (Glama Standard)
102
+ ```bash
103
+ # Build image locally
104
+ docker build -t mcp-relight-harmonize .
105
+
106
+ # Run container over stdio
107
+ docker run -i --rm mcp-relight-harmonize
108
+ ```
109
+
95
110
  ---
96
111
 
97
112
  ## Architectural Profile
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ async function main() {
8
8
  const args = process.argv.slice(2);
9
9
  if (args.includes("--verify")) {
10
10
  console.log("[MarwanDevSpace] Verifying TypeScript mcp-relight-harmonize server...");
11
- console.log("Server 'mcp-relight-harmonize' v1.0.0 initialized.");
12
- console.log("Registered tools: ['analyze_optical_profile', 'generate_relight_variations', 'harmonize_composite', 'synthesize_diffusion_prompt']");
11
+ console.log("Server 'mcp-relight-harmonize' v1.0.2 initialized.");
12
+ console.log("Registered tools: ['analyze_optical_profile', 'generate_relight_variations', 'harmonize_composite', 'synthesize_diffusion_prompt', 'list_cached_variations']");
13
13
  console.log("Registered resources: ['optical://presets']");
14
14
  console.log("Output cache directory:", (0, config_1.ensureOutputDirectory)());
15
15
  console.log("[MarwanDevSpace] Server health verification passed (Exit code 0).");
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIGHTING_PRESETS_REFERENCE = void 0;
4
4
  exports.getPresetsJson = getPresetsJson;
5
5
  exports.LIGHTING_PRESETS_REFERENCE = {
6
- version: "1.0.0",
6
+ version: "1.0.2",
7
7
  target_generators: ["GPT Image", "Nano Banana"],
8
8
  presets: [
9
9
  {
package/dist/server.js CHANGED
@@ -7,11 +7,46 @@ const analyze_optical_1 = require("./tools/analyze_optical");
7
7
  const generate_relight_1 = require("./tools/generate_relight");
8
8
  const harmonize_1 = require("./tools/harmonize");
9
9
  const synthesize_prompt_1 = require("./tools/synthesize_prompt");
10
+ const list_cache_1 = require("./tools/list_cache");
10
11
  const presets_1 = require("./resources/presets");
12
+ // Common output envelope schema definition
13
+ const StandardEnvelopeSchema = {
14
+ type: "object",
15
+ properties: {
16
+ status: { type: "string", enum: ["success", "partial", "blocked", "failed"], description: "Execution status." },
17
+ summary: { type: "string", description: "Human-readable executive summary of the operation." },
18
+ data: { type: "object", description: "Typed domain payload." },
19
+ warnings: { type: "array", items: { type: "string" }, description: "Non-fatal warnings if applicable." },
20
+ evidence: {
21
+ type: "object",
22
+ properties: {
23
+ inputsDigest: { type: "string", description: "SHA-256 digest of input parameters." },
24
+ sources: {
25
+ type: "array",
26
+ items: {
27
+ type: "object",
28
+ properties: { label: { type: "string" }, uri: { type: "string" } },
29
+ required: ["label"],
30
+ },
31
+ },
32
+ artifacts: {
33
+ type: "array",
34
+ items: {
35
+ type: "object",
36
+ properties: { label: { type: "string" }, uri: { type: "string" } },
37
+ required: ["label"],
38
+ },
39
+ },
40
+ },
41
+ },
42
+ nextActions: { type: "array", items: { type: "string" }, description: "Actionable follow-up guidance." },
43
+ },
44
+ required: ["status", "summary", "data", "warnings", "evidence", "nextActions"],
45
+ };
11
46
  function createServer() {
12
47
  const server = new index_js_1.Server({
13
48
  name: "mcp-relight-harmonize",
14
- version: "1.0.0",
49
+ version: "1.0.2",
15
50
  }, {
16
51
  capabilities: {
17
52
  tools: {},
@@ -24,94 +59,320 @@ function createServer() {
24
59
  tools: [
25
60
  {
26
61
  name: "analyze_optical_profile",
27
- description: "Extract physical optical metrics from an image: Correlated Color Temperature (CCT in Kelvin), " +
28
- "dominant light vectors, azimuth & elevation angles, luminance dynamic range, and surface roughness.",
62
+ description: "Extract physical optical metrics from an image, including Correlated Color Temperature (CCT in Kelvin), " +
63
+ "dominant 3D lighting vector (azimuth and elevation angles), photometric luminance dynamic range, contrast zones, " +
64
+ "and surface normal roughness index.\n\n" +
65
+ "• Purpose: Diagnostic optical extraction. Unlike 'generate_relight_variations', this tool produces no image files, " +
66
+ "and unlike 'synthesize_diffusion_prompt', it returns pure numerical color-science data rather than text prompts.\n" +
67
+ "• Behavior: Completely read-only, deterministic, zero filesystem modifications, no network egress, and no authentication required.\n" +
68
+ "• When to use: Use as the prerequisite first step before relighting, inpainting, or compositing an image to inspect baseline lighting conditions.\n" +
69
+ "• When NOT to use: Do NOT use if you need modified image files on disk (use 'generate_relight_variations'), if merging a cutout into a scene " +
70
+ "(use 'harmonize_composite'), or if you need generative AI prompts (use 'synthesize_diffusion_prompt').\n" +
71
+ "• Alternatives: Use 'generate_relight_variations' for visual lighting files, or 'synthesize_diffusion_prompt' for model prompts.",
29
72
  inputSchema: {
30
73
  type: "object",
31
74
  properties: {
32
75
  image_path: {
33
76
  type: "string",
34
- description: "Absolute or workspace-relative path to the image file.",
77
+ description: "Absolute or workspace-relative path to a local image file (.png, .jpg, or .jpeg). " +
78
+ "Must be an existing image under 50 MB.",
35
79
  },
36
80
  },
37
81
  required: ["image_path"],
38
82
  },
83
+ outputSchema: {
84
+ ...StandardEnvelopeSchema,
85
+ properties: {
86
+ ...StandardEnvelopeSchema.properties,
87
+ data: {
88
+ type: "object",
89
+ properties: {
90
+ imagePath: { type: "string", description: "Canonical resolved file path." },
91
+ dimensions: { type: "array", items: { type: "number" }, description: "[width, height] in pixels." },
92
+ colorTemperatureKelvin: { type: "number", description: "Correlated Color Temperature (CCT) in Kelvin." },
93
+ dominantLightDirectionVector: { type: "array", items: { type: "number" }, description: "Normalized [X, Y, Z] vector." },
94
+ lightingAngles: {
95
+ type: "object",
96
+ properties: {
97
+ azimuthDeg: { type: "number", description: "Horizontal angle (0-360°)." },
98
+ elevationDeg: { type: "number", description: "Vertical elevation angle (0-90°)." },
99
+ },
100
+ required: ["azimuthDeg", "elevationDeg"],
101
+ },
102
+ meanLuminance: { type: "number", description: "Average photometric luminance (0-255)." },
103
+ luminanceDynamics: {
104
+ type: "object",
105
+ properties: {
106
+ min: { type: "number" },
107
+ max: { type: "number" },
108
+ p5: { type: "number" },
109
+ median: { type: "number" },
110
+ p95: { type: "number" },
111
+ contrastRatio: { type: "number" },
112
+ },
113
+ },
114
+ contrastZones: {
115
+ type: "object",
116
+ properties: {
117
+ specularHighlightsPct: { type: "number" },
118
+ deepShadowsPct: { type: "number" },
119
+ midtonesPct: { type: "number" },
120
+ },
121
+ },
122
+ surfaceNormalVariation: { type: "number", description: "Roughness metric (std dev of normals)." },
123
+ opticalProfileSummary: { type: "string", description: "Executive summary sentence." },
124
+ },
125
+ required: [
126
+ "imagePath",
127
+ "dimensions",
128
+ "colorTemperatureKelvin",
129
+ "dominantLightDirectionVector",
130
+ "lightingAngles",
131
+ "meanLuminance",
132
+ "luminanceDynamics",
133
+ "contrastZones",
134
+ "surfaceNormalVariation",
135
+ "opticalProfileSummary",
136
+ ],
137
+ },
138
+ },
139
+ },
39
140
  },
40
141
  {
41
142
  name: "generate_relight_variations",
42
- description: "Generate 4 physically-grounded relit image variations (Ambient fill, Dramatic chiaroscuro, " +
43
- "Rim light halo, Mood golden-hour) and save them to the output cache directory.",
143
+ description: "Generate 4 physically-grounded relit image variations on disk (Ambient fill, Dramatic chiaroscuro, " +
144
+ "Rim light halo, Mood golden-hour) with mathematical adjustment logs detailing exposure compensation (EV stops) and color balance.\n\n" +
145
+ "• Purpose: Visual image transformation. Unlike 'analyze_optical_profile' which is read-only, this tool renders and writes concrete " +
146
+ "image files to the destination directory. Unlike 'synthesize_diffusion_prompt', it produces immediate local image files.\n" +
147
+ "• Behavior: Mutates filesystem by creating up to 4 image files in the output directory. Deterministic, unmetered local compute, " +
148
+ "no network egress, no authentication required. Re-running overwrites previous variations with the same base name.\n" +
149
+ "• When to use: Use when you need tangible image alternatives of a photo or product render with alternative lighting schemes.\n" +
150
+ "• When NOT to use: Do NOT use if you only need optical metrics (use 'analyze_optical_profile'), if blending a cutout into a background " +
151
+ "(use 'harmonize_composite'), or if you need diffusion AI text prompts (use 'synthesize_diffusion_prompt').\n" +
152
+ "• Alternatives: Use 'synthesize_diffusion_prompt' for text prompts targeting GPT Image or Nano Banana, or 'list_cached_variations' to browse existing outputs.",
44
153
  inputSchema: {
45
154
  type: "object",
46
155
  properties: {
47
156
  image_path: {
48
157
  type: "string",
49
- description: "Path to the source image.",
158
+ description: "Path to the input image (.png, .jpg, .jpeg) to relight.",
50
159
  },
51
160
  target_lighting: {
52
161
  type: "string",
53
162
  enum: ["Ambient", "Dramatic", "Rim", "Mood", "All"],
54
163
  default: "All",
55
- description: "Lighting preset: 'Ambient', 'Dramatic', 'Rim', 'Mood', or 'All'.",
164
+ description: "Lighting preset selection: 'Ambient' (+0.8 EV lifted shadows, 5500K daylight), " +
165
+ "'Dramatic' (-1.5 EV shadow crush, chiaroscuro S-curve), 'Rim' (+1.2 EV normal curvature perimeter halo), " +
166
+ "'Mood' (3200K tungsten amber shift, highlight bloom), or 'All' to generate all four simultaneously. Defaults to 'All'.",
56
167
  },
57
168
  output_dir: {
58
169
  type: "string",
59
- description: "Custom output directory. If omitted, uses default cache directory.",
170
+ default: "",
171
+ description: "Destination folder for generated variation image files. If omitted, defaults to the server's configured cache directory ('./generated_variations').",
60
172
  },
61
173
  },
62
174
  required: ["image_path"],
63
175
  },
176
+ outputSchema: {
177
+ ...StandardEnvelopeSchema,
178
+ properties: {
179
+ ...StandardEnvelopeSchema.properties,
180
+ data: {
181
+ type: "object",
182
+ properties: {
183
+ originalImage: { type: "string" },
184
+ outputDir: { type: "string" },
185
+ totalVariations: { type: "number" },
186
+ variations: {
187
+ type: "array",
188
+ items: {
189
+ type: "object",
190
+ properties: {
191
+ presetName: { type: "string" },
192
+ imagePath: { type: "string" },
193
+ fileSizeBytes: { type: "number" },
194
+ evShiftStops: { type: "number" },
195
+ targetCctKelvin: { type: "number" },
196
+ adjustmentsApplied: { type: "array", items: { type: "string" } },
197
+ },
198
+ required: ["presetName", "imagePath", "fileSizeBytes", "evShiftStops", "targetCctKelvin", "adjustmentsApplied"],
199
+ },
200
+ },
201
+ },
202
+ required: ["originalImage", "outputDir", "totalVariations", "variations"],
203
+ },
204
+ },
205
+ },
64
206
  },
65
207
  {
66
208
  name: "harmonize_composite",
67
- description: "Harmonize and composite a foreground element onto a background scene. Applies Reinhard color " +
68
- "statistics transfer, matches background color temperature, and synthesizes grounded contact shadows.",
209
+ description: "Harmonize and composite a foreground subject cutout onto a background environment scene image using Reinhard " +
210
+ "color statistics transfer in decorrelated lαβ space, background color temperature matching, and synthesized ground contact shadows.\n\n" +
211
+ "• Purpose: Two-image compositing and photometric harmonization. Distinct from 'generate_relight_variations' which operates on a single image, " +
212
+ "this tool resolves color mismatches and grounding between two separate image sources.\n" +
213
+ "• Behavior: Mutates filesystem by writing 1 composited PNG image to the output cache directory. Deterministic, local execution, " +
214
+ "no network egress, no authentication required.\n" +
215
+ "• When to use: Use when placing a cutout product, character, or object onto a new background environment scene, ensuring realistic color adaptation and floor shadows.\n" +
216
+ "• When NOT to use: Do NOT use to relight a single standalone image (use 'generate_relight_variations'), or to analyze metrics alone (use 'analyze_optical_profile').\n" +
217
+ "• Alternatives: Use 'generate_relight_variations' to alter lighting on a single image, or 'synthesize_diffusion_prompt' to craft inpainting prompts for seam blending.",
69
218
  inputSchema: {
70
219
  type: "object",
71
220
  properties: {
72
221
  foreground_path: {
73
222
  type: "string",
74
- description: "Path to the foreground subject cutout (PNG/JPG).",
223
+ description: "Path to foreground subject image. Supports transparent PNG (with alpha channel) or solid background.",
75
224
  },
76
225
  background_path: {
77
226
  type: "string",
78
- description: "Path to the background environment image.",
227
+ description: "Path to target background scene image (.png, .jpg, .jpeg).",
79
228
  },
80
229
  blend_mode: {
81
230
  type: "string",
82
231
  enum: ["seamless", "alpha"],
83
232
  default: "seamless",
84
- description: "Blending algorithm ('seamless' or 'alpha').",
233
+ description: "Blending mode: 'seamless' (applies Reinhard color transfer + contact shadow + alpha blend) " +
234
+ "or 'alpha' (standard alpha composite with contact shadow only). Defaults to 'seamless'.",
85
235
  },
86
236
  },
87
237
  required: ["foreground_path", "background_path"],
88
238
  },
239
+ outputSchema: {
240
+ ...StandardEnvelopeSchema,
241
+ properties: {
242
+ ...StandardEnvelopeSchema.properties,
243
+ data: {
244
+ type: "object",
245
+ properties: {
246
+ compositeImagePath: { type: "string", description: "Absolute path to the rendered composite file on disk." },
247
+ blendMode: { type: "string" },
248
+ foregroundPath: { type: "string" },
249
+ backgroundPath: { type: "string" },
250
+ backgroundCctKelvin: { type: "number", description: "Target background color temperature." },
251
+ luminanceScalingFactor: { type: "number" },
252
+ contactShadowApplied: { type: "boolean", description: "True if contact shadow was synthesized at base." },
253
+ details: { type: "object" },
254
+ },
255
+ required: [
256
+ "compositeImagePath",
257
+ "blendMode",
258
+ "foregroundPath",
259
+ "backgroundPath",
260
+ "backgroundCctKelvin",
261
+ "luminanceScalingFactor",
262
+ "contactShadowApplied",
263
+ ],
264
+ },
265
+ },
266
+ },
89
267
  },
90
268
  {
91
269
  name: "synthesize_diffusion_prompt",
92
- description: "Synthesize precision enhancement and relighting diffusion prompts for GPT Image (DALL-E 3 / GPT-4o) " +
93
- "and Nano Banana based on physical optical analysis of an image. Includes exact Kelvin temperature, " +
94
- "lighting angles, volumetric rays, contact shadows, and calibrated denoising parameters (0.35 - 0.45).",
270
+ description: "Synthesize precision enhancement and relighting diffusion prompts based on physical optical analysis of an image, " +
271
+ "tailored specifically for GPT Image (DALL-E 3 / GPT-4o) and Nano Banana. Outputs photorealistic prompts with physical keywords " +
272
+ "(exact Kelvin CCT, 3D light angles, volumetric dust rays, contact shadows) and calibrated denoising parameters (0.35 - 0.45).\n\n" +
273
+ "• Purpose: Generative AI prompt synthesis. Unlike 'generate_relight_variations' which creates image files locally, this tool translates " +
274
+ "optical geometry into targeted text prompts and hyperparameter sets for external diffusion generators.\n" +
275
+ "• Behavior: Completely read-only, deterministic, zero filesystem modifications, no network calls, and no authentication required.\n" +
276
+ "• When to use: Use when you want to feed photorealistic lighting directives or inpainting prompts into GPT Image or Nano Banana.\n" +
277
+ "• When NOT to use: Do NOT use if you need local image rendering without an external AI model (use 'generate_relight_variations'), " +
278
+ "or if merging cutouts locally (use 'harmonize_composite').\n" +
279
+ "• Alternatives: Use 'generate_relight_variations' for instant offline image files, or 'analyze_optical_profile' for raw numerical statistics.",
95
280
  inputSchema: {
96
281
  type: "object",
97
282
  properties: {
98
283
  image_path: {
99
284
  type: "string",
100
- description: "Path to the reference image.",
285
+ description: "Path to the local reference image (.png, .jpg, .jpeg) to extract optical geometry from.",
101
286
  },
102
287
  user_intent: {
103
288
  type: "string",
104
- description: "Creative intent (e.g. 'golden sunset', 'studio commercial').",
289
+ default: "",
290
+ description: "Optional creative context or scenario description (e.g., 'golden sunset portrait', 'cyberpunk studio product').",
105
291
  },
106
292
  target_model: {
107
293
  type: "string",
108
294
  enum: ["gpt_image", "nano_banana"],
109
295
  default: "gpt_image",
110
- description: "Target engine: 'gpt_image' (GPT Image) or 'nano_banana' (Nano Banana).",
296
+ description: "Target generative engine: 'gpt_image' (outputs natural descriptive studio directives with 85mm prime lens and physical illumination) " +
297
+ "or 'nano_banana' (outputs dense tokenized optical shaders, roughness index, raytraced bounce, and ground contact shadow). Defaults to 'gpt_image'.",
111
298
  },
112
299
  },
113
300
  required: ["image_path"],
114
301
  },
302
+ outputSchema: {
303
+ ...StandardEnvelopeSchema,
304
+ properties: {
305
+ ...StandardEnvelopeSchema.properties,
306
+ data: {
307
+ type: "object",
308
+ properties: {
309
+ targetModel: { type: "string", enum: ["GPT Image", "Nano Banana"] },
310
+ userIntent: { type: "string" },
311
+ enhancementPrompt: { type: "string", description: "Prompt for micro-surface detail and lens clarity upgrade." },
312
+ relightingPrompt: { type: "string", description: "Prompt for physical relighting with angles, CCT, and contact shadows." },
313
+ recommendedParameters: { type: "object", description: "Calibrated diffusion settings (denoising 0.35-0.45, etc.)." },
314
+ opticalKeywordsUsed: { type: "array", items: { type: "string" } },
315
+ },
316
+ required: [
317
+ "targetModel",
318
+ "userIntent",
319
+ "enhancementPrompt",
320
+ "relightingPrompt",
321
+ "recommendedParameters",
322
+ "opticalKeywordsUsed",
323
+ ],
324
+ },
325
+ },
326
+ },
327
+ },
328
+ {
329
+ name: "list_cached_variations",
330
+ description: "List and inspect all previously generated relight variation images and harmonized composite files stored in the output cache directory.\n\n" +
331
+ "• Purpose: Cache inventory and artifact retrieval. Resolves pipeline completeness by allowing callers to discover, verify, " +
332
+ "and retrieve generated files without manual filesystem traversal.\n" +
333
+ "• Behavior: Completely read-only, queries local filesystem cache directory, no network calls, no authentication required.\n" +
334
+ "• When to use: Use after running 'generate_relight_variations' or 'harmonize_composite' to verify written artifacts, inspect file sizes, " +
335
+ "and obtain exact paths for downstream analysis.\n" +
336
+ "• When NOT to use: Do NOT use to generate new images (use 'generate_relight_variations') or to analyze optical metrics (use 'analyze_optical_profile').\n" +
337
+ "• Alternatives: Use 'generate_relight_variations' to create new images, or 'harmonize_composite' to merge cutouts.",
338
+ inputSchema: {
339
+ type: "object",
340
+ properties: {
341
+ cache_dir: {
342
+ type: "string",
343
+ default: "",
344
+ description: "Optional custom directory path to inspect. If omitted, uses server's default cache directory.",
345
+ },
346
+ },
347
+ },
348
+ outputSchema: {
349
+ ...StandardEnvelopeSchema,
350
+ properties: {
351
+ ...StandardEnvelopeSchema.properties,
352
+ data: {
353
+ type: "object",
354
+ properties: {
355
+ cacheDirectory: { type: "string" },
356
+ totalFiles: { type: "number" },
357
+ files: {
358
+ type: "array",
359
+ items: {
360
+ type: "object",
361
+ properties: {
362
+ filename: { type: "string" },
363
+ path: { type: "string" },
364
+ sizeBytes: { type: "number" },
365
+ createdAt: { type: "string" },
366
+ type: { type: "string", enum: ["relight_variation", "harmonized_composite", "other"] },
367
+ },
368
+ required: ["filename", "path", "sizeBytes", "createdAt", "type"],
369
+ },
370
+ },
371
+ },
372
+ required: ["cacheDirectory", "totalFiles", "files"],
373
+ },
374
+ },
375
+ },
115
376
  },
116
377
  ],
117
378
  };
@@ -147,6 +408,11 @@ function createServer() {
147
408
  envelope = (0, synthesize_prompt_1.synthesizeDiffusionPromptTool)(imagePath, userIntent, targetModel);
148
409
  break;
149
410
  }
411
+ case "list_cached_variations": {
412
+ const cacheDir = args.cache_dir ? String(args.cache_dir) : undefined;
413
+ envelope = (0, list_cache_1.listCachedVariationsTool)(cacheDir);
414
+ break;
415
+ }
150
416
  default:
151
417
  throw new Error(`Unknown tool: '${name}'`);
152
418
  }
@@ -0,0 +1,9 @@
1
+ import { ResultEnvelope } from "../core/envelope";
2
+ export interface CachedItem {
3
+ filename: string;
4
+ path: string;
5
+ sizeBytes: number;
6
+ createdAt: string;
7
+ type: "relight_variation" | "harmonized_composite" | "other";
8
+ }
9
+ export declare function listCachedVariationsTool(cacheDir?: string): ResultEnvelope;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listCachedVariationsTool = listCachedVariationsTool;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const crypto_1 = __importDefault(require("crypto"));
10
+ const config_1 = require("../config");
11
+ const envelope_1 = require("../core/envelope");
12
+ const errors_1 = require("../core/errors");
13
+ function listCachedVariationsTool(cacheDir) {
14
+ try {
15
+ const targetDir = cacheDir ? path_1.default.resolve(cacheDir) : (0, config_1.ensureOutputDirectory)();
16
+ if (!fs_1.default.existsSync(targetDir)) {
17
+ return (0, envelope_1.createEnvelope)("success", `Output cache directory is empty or does not yet exist: '${targetDir}'`, { cacheDirectory: targetDir, totalFiles: 0, files: [] }, { nextActions: ["Call 'generate_relight_variations' or 'harmonize_composite' to produce variations."] });
18
+ }
19
+ const entries = fs_1.default.readdirSync(targetDir, { withFileTypes: true });
20
+ const files = [];
21
+ for (const entry of entries) {
22
+ if (entry.isFile()) {
23
+ const ext = path_1.default.extname(entry.name).toLowerCase();
24
+ if (config_1.config.allowedExtensions.has(ext)) {
25
+ const fullPath = path_1.default.join(targetDir, entry.name);
26
+ const stat = fs_1.default.statSync(fullPath);
27
+ let itemType = "other";
28
+ if (entry.name.includes("_relight_")) {
29
+ itemType = "relight_variation";
30
+ }
31
+ else if (entry.name.startsWith("harmonized_")) {
32
+ itemType = "harmonized_composite";
33
+ }
34
+ files.push({
35
+ filename: entry.name,
36
+ path: fullPath,
37
+ sizeBytes: stat.size,
38
+ createdAt: stat.mtime.toISOString(),
39
+ type: itemType,
40
+ });
41
+ }
42
+ }
43
+ }
44
+ // Sort by most recent
45
+ files.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
46
+ const hash = crypto_1.default.createHash("sha256").update(targetDir).digest("hex").slice(0, 12);
47
+ return (0, envelope_1.createEnvelope)("success", `Found ${files.length} cached variation and composite artifacts in '${targetDir}'.`, { cacheDirectory: targetDir, totalFiles: files.length, files }, {
48
+ evidence: {
49
+ inputsDigest: `sha256:${hash}`,
50
+ artifacts: files.map((f) => ({ label: f.filename, uri: `file://${f.path}` })),
51
+ },
52
+ nextActions: [
53
+ "Call 'analyze_optical_profile' on any cached variation to evaluate its modified optical properties.",
54
+ "Call 'synthesize_diffusion_prompt' to craft AI inpainting prompts from a cached variation.",
55
+ ],
56
+ });
57
+ }
58
+ catch (err) {
59
+ const isAppErr = err instanceof errors_1.AppError;
60
+ const msg = err.message || "Failed to list cached variations.";
61
+ const code = isAppErr ? err.code : "INTERNAL_ERROR";
62
+ const hint = isAppErr ? err.actionableHint : "Verify directory read permissions.";
63
+ return (0, envelope_1.createEnvelope)("failed", `Listing cache failed: ${msg}`, { error_code: code, actionable_hint: hint }, {
64
+ warnings: [msg],
65
+ nextActions: [hint],
66
+ });
67
+ }
68
+ }
package/glama.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://glama.ai/mcp/schemas/server.json",
3
+ "maintainers": [
4
+ "MarwanDevSpace"
5
+ ]
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-relight-harmonize",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Professional TypeScript Model Context Protocol (MCP) server & agent skill for optical profiling, physical relighting, composite harmonization, and diffusion prompt synthesis (GPT Image & Nano Banana).",
5
5
  "author": "MarwanDevSpace <dev@marwandevspace.io>",
6
6
  "license": "MIT",
@@ -53,6 +53,8 @@
53
53
  "files": [
54
54
  "dist",
55
55
  "package.json",
56
+ "LICENSE",
57
+ "glama.json",
56
58
  "MASTER.md",
57
59
  "mcp_config.json",
58
60
  ".agents",