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.
- package/.agents/skills/mcp-relight-harmonize/SKILL.md +5 -0
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.js +2 -2
- package/dist/resources/presets.js +1 -1
- package/dist/server.js +286 -20
- package/dist/tools/list_cache.d.ts +9 -0
- package/dist/tools/list_cache.js +68 -0
- package/glama.json +6 -0
- package/package.json +3 -1
|
@@ -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
|
+
[](https://www.npmjs.com/package/mcp-relight-harmonize)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://github.com/MarwanDevSpace/mcp-relight-harmonize/actions)
|
|
6
|
+
[](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.
|
|
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.
|
|
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.
|
|
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
|
|
28
|
-
"dominant
|
|
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
|
|
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)
|
|
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
|
|
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'
|
|
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
|
-
|
|
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
|
|
68
|
-
"statistics transfer,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
93
|
-
"
|
|
94
|
-
"
|
|
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
|
-
|
|
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' (
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-relight-harmonize",
|
|
3
|
-
"version": "1.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",
|