@codingmantra/mcp-server 0.1.3 → 0.2.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.
- package/README.md +126 -30
- package/build/api-client.js +45 -15
- package/build/image-utils.js +145 -0
- package/build/index.js +588 -124
- package/package.json +14 -5
- package/src/api-client.ts +0 -47
- package/src/index.ts +0 -440
- package/tsconfig.json +0 -14
package/build/index.js
CHANGED
|
@@ -3,35 +3,62 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { apiClient } from './api-client.js';
|
|
6
|
+
import { formatImageToolResult, resolveImageInput, resolveImagesArray } from './image-utils.js';
|
|
6
7
|
const server = new Server({
|
|
7
8
|
name: "codingmantra-mcp",
|
|
8
|
-
version: "0.
|
|
9
|
+
version: "0.2.0",
|
|
9
10
|
}, {
|
|
10
11
|
capabilities: {
|
|
11
12
|
tools: {},
|
|
12
13
|
},
|
|
13
14
|
});
|
|
15
|
+
// ─── Reusable Parameter Definitions ──────────────────────────────────────────
|
|
16
|
+
const IMAGE_SOURCE_SCHEMA = {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
url: { type: "string", description: "Public URL of the image." },
|
|
20
|
+
dataUrl: { type: "string", description: "Base64 data URI of the image." },
|
|
21
|
+
path: { type: "string", description: "Local file path on disk (will be automatically resolved to base64)." }
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const COMMON_VIDEO_PROPERTIES = {
|
|
25
|
+
prompt: { type: "string", description: "Detailed video description or creative prompt." },
|
|
26
|
+
mainImage: { type: "string", description: "Base64 data URI, local file path, or public URL for the main subject image (Image-to-Video mode)." },
|
|
27
|
+
startImage: { type: "string", description: "Base64 data URI, local file path, or public URL for the first frame." },
|
|
28
|
+
endImage: { type: "string", description: "Base64 data URI, local file path, or public URL for the last frame." },
|
|
29
|
+
referenceImages: {
|
|
30
|
+
type: "array",
|
|
31
|
+
items: { type: "object", properties: { image: { type: "string" }, label: { type: "string" } } },
|
|
32
|
+
description: "Array of reference images for style matching."
|
|
33
|
+
},
|
|
34
|
+
negativePrompt: { type: "string", description: "Elements to exclude from the video." },
|
|
35
|
+
dialogue: { type: "string", description: "Optional voiceover or dialogue script." },
|
|
36
|
+
soundSuggestion: { type: "string", description: "Background music or sound effects direction." },
|
|
37
|
+
cameraMotion: { type: "string", description: "Camera motion instructions (e.g., 'orbit right', 'slow push-in')." },
|
|
38
|
+
ambientNoise: { type: "string", description: "Ambient sound description." },
|
|
39
|
+
language: { type: "string", description: "Voiceover language." },
|
|
40
|
+
timeDuration: { type: "string", description: "Video duration in seconds. Default is '4'." },
|
|
41
|
+
resolution: { type: "string", description: "Resolution (e.g., '720p', '1080p'). Default is '720p'." },
|
|
42
|
+
model: { type: "string", description: "Model slug (e.g., 'google/veo-3.1-fast', 'openrouter/wan-2.7')." },
|
|
43
|
+
aspectRatio: { type: "string", description: "Aspect ratio (e.g., '9:16', '16:9'). Default is '9:16'." },
|
|
44
|
+
generateAudio: { type: "boolean", description: "Whether to generate synchronized audio. Default is false." }
|
|
45
|
+
};
|
|
14
46
|
// ─── Tool Definitions ──────────────────────────────────────────────────────────
|
|
47
|
+
// 1. Product Photography
|
|
15
48
|
const GENERATE_PRODUCT_PHOTO_TOOL = {
|
|
16
49
|
name: "generate_product_photo",
|
|
17
|
-
description: "Generates
|
|
50
|
+
description: "Generates studio-grade commercial product photography with customizable background scenes, ambient occlusion contact shadows, camera optics, resolutions (1K, 2K, 4K), aspect ratios, and dynamic logo/text overlays. Returns the image directly for inline display (synchronous).",
|
|
18
51
|
inputSchema: {
|
|
19
52
|
type: "object",
|
|
20
53
|
properties: {
|
|
21
54
|
productImages: {
|
|
22
55
|
type: "array",
|
|
23
|
-
items:
|
|
24
|
-
|
|
25
|
-
properties: {
|
|
26
|
-
url: { type: "string", description: "Public URL of the product image." },
|
|
27
|
-
dataUrl: { type: "string", description: "Base64 data URI of the product image." }
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
description: "Array of product images. Each must have either a 'url' or 'dataUrl'. At least one required."
|
|
56
|
+
items: IMAGE_SOURCE_SCHEMA,
|
|
57
|
+
description: "Array of product images. Each item can have 'url', 'dataUrl', or local 'path'. At least one required."
|
|
31
58
|
},
|
|
32
59
|
scenePrompt: {
|
|
33
60
|
type: "string",
|
|
34
|
-
description: "A detailed description of the background scene and
|
|
61
|
+
description: "A detailed description of the background scene, surface, and environment for the product photo."
|
|
35
62
|
},
|
|
36
63
|
model: {
|
|
37
64
|
type: "string",
|
|
@@ -39,23 +66,23 @@ const GENERATE_PRODUCT_PHOTO_TOOL = {
|
|
|
39
66
|
},
|
|
40
67
|
resolution: {
|
|
41
68
|
type: "string",
|
|
42
|
-
description: "Output resolution (
|
|
69
|
+
description: "Output resolution ('1K', '2K', '4K'). Default is '1K'.",
|
|
43
70
|
},
|
|
44
71
|
aspectRatio: {
|
|
45
72
|
type: "string",
|
|
46
|
-
description: "Output aspect ratio (
|
|
73
|
+
description: "Output aspect ratio ('1:1', '16:9', '9:16', '4:5'). Default is '1:1'.",
|
|
47
74
|
},
|
|
48
75
|
referenceImage: {
|
|
49
76
|
type: "string",
|
|
50
|
-
description: "
|
|
77
|
+
description: "Optional URL, data URI, or local file path of a style reference image."
|
|
51
78
|
},
|
|
52
79
|
promptId: {
|
|
53
80
|
type: "string",
|
|
54
|
-
description: "Optional ID of a pre-defined
|
|
81
|
+
description: "Optional ID of a pre-defined background template."
|
|
55
82
|
},
|
|
56
83
|
logoImage: {
|
|
57
84
|
type: "string",
|
|
58
|
-
description: "Optional URL or
|
|
85
|
+
description: "Optional URL, data URI, or local file path of a brand logo to place on the image."
|
|
59
86
|
},
|
|
60
87
|
logoPlacement: {
|
|
61
88
|
type: "string",
|
|
@@ -63,7 +90,7 @@ const GENERATE_PRODUCT_PHOTO_TOOL = {
|
|
|
63
90
|
},
|
|
64
91
|
logoSize: {
|
|
65
92
|
type: "number",
|
|
66
|
-
description: "Scale of the logo overlay (0.
|
|
93
|
+
description: "Scale of the logo overlay (0.05 to 0.5). Default is 0.15."
|
|
67
94
|
},
|
|
68
95
|
overlayText: {
|
|
69
96
|
type: "string",
|
|
@@ -71,15 +98,16 @@ const GENERATE_PRODUCT_PHOTO_TOOL = {
|
|
|
71
98
|
},
|
|
72
99
|
marketingText: {
|
|
73
100
|
type: "string",
|
|
74
|
-
description: "Optional marketing text to overlay."
|
|
101
|
+
description: "Optional marketing text or slogan to overlay."
|
|
75
102
|
}
|
|
76
103
|
},
|
|
77
104
|
required: ["productImages", "scenePrompt"]
|
|
78
105
|
}
|
|
79
106
|
};
|
|
107
|
+
// 2. Logo Creator
|
|
80
108
|
const GENERATE_LOGO_TOOL = {
|
|
81
109
|
name: "generate_logo",
|
|
82
|
-
description: "Generates
|
|
110
|
+
description: "Generates bespoke vector-style or 3D brand logos with customizable typography, colors, industries, and reference images. Returns the generated logo directly for inline display (synchronous).",
|
|
83
111
|
inputSchema: {
|
|
84
112
|
type: "object",
|
|
85
113
|
properties: {
|
|
@@ -89,7 +117,7 @@ const GENERATE_LOGO_TOOL = {
|
|
|
89
117
|
},
|
|
90
118
|
stylePrompt: {
|
|
91
119
|
type: "string",
|
|
92
|
-
description: "A detailed style description for the logo (e.g., 'minimalist flat vector logo,
|
|
120
|
+
description: "A detailed style description for the logo (e.g., 'minimalist flat vector logo, navy blue and gold, clean tech aesthetic')."
|
|
93
121
|
},
|
|
94
122
|
subtitle: {
|
|
95
123
|
type: "string",
|
|
@@ -97,15 +125,15 @@ const GENERATE_LOGO_TOOL = {
|
|
|
97
125
|
},
|
|
98
126
|
industry: {
|
|
99
127
|
type: "string",
|
|
100
|
-
description: "The industry the company operates in (e.g., 'Technology', '
|
|
128
|
+
description: "The industry the company operates in (e.g., 'Technology', 'Luxury Jewelry', 'Fashion')."
|
|
101
129
|
},
|
|
102
130
|
ideas: {
|
|
103
131
|
type: "string",
|
|
104
|
-
description: "
|
|
132
|
+
description: "Core keywords, symbols, or concepts for the logo."
|
|
105
133
|
},
|
|
106
134
|
colors: {
|
|
107
135
|
type: "string",
|
|
108
|
-
description: "Preferred color palette description (e.g., '
|
|
136
|
+
description: "Preferred color palette description (e.g., 'emerald green and brushed gold')."
|
|
109
137
|
},
|
|
110
138
|
aspectRatio: {
|
|
111
139
|
type: "string",
|
|
@@ -113,27 +141,331 @@ const GENERATE_LOGO_TOOL = {
|
|
|
113
141
|
},
|
|
114
142
|
modelId: {
|
|
115
143
|
type: "string",
|
|
116
|
-
description: "The image model ID. Default uses
|
|
144
|
+
description: "The image model ID. Default uses platform default.",
|
|
117
145
|
},
|
|
118
146
|
resolution: {
|
|
119
147
|
type: "string",
|
|
120
|
-
description: "Output resolution (
|
|
148
|
+
description: "Output resolution ('1K', '2K'). Default is '1K'.",
|
|
121
149
|
},
|
|
122
|
-
|
|
150
|
+
referenceImage: {
|
|
151
|
+
type: "string",
|
|
152
|
+
description: "Optional style reference image URL, data URI, or local file path."
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
required: ["companyName", "stylePrompt"]
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
// 3. Jewelry Try-On
|
|
159
|
+
const TRY_ON_JEWELRY_TOOL = {
|
|
160
|
+
name: "try_on_jewelry",
|
|
161
|
+
description: "Luxury jewelry virtual try-on. Places necklaces, rings, earrings, and bracelets naturally on human models with prongs/gemstone preservation, authentic optical dispersion, and anatomical contact shadows. Returns generated image directly (synchronous).",
|
|
162
|
+
inputSchema: {
|
|
163
|
+
type: "object",
|
|
164
|
+
properties: {
|
|
165
|
+
jewelryImages: {
|
|
166
|
+
type: "array",
|
|
167
|
+
items: IMAGE_SOURCE_SCHEMA,
|
|
168
|
+
description: "Array of jewelry product images. Each can have 'url', 'dataUrl', or local 'path'. At least one required."
|
|
169
|
+
},
|
|
170
|
+
personImage: {
|
|
171
|
+
type: "string",
|
|
172
|
+
description: "Optional photo of a person to model the jewelry (URL, data URI, or local file path). If omitted, an elegant editorial model is generated."
|
|
173
|
+
},
|
|
174
|
+
prompt: {
|
|
175
|
+
type: "string",
|
|
176
|
+
description: "Creative styling directions (e.g., 'wearing black velvet evening gown, soft studio rim lighting, Parisian luxury ambiance')."
|
|
177
|
+
},
|
|
178
|
+
sizeMeasurement: {
|
|
179
|
+
type: "string",
|
|
180
|
+
description: "Physical dimensions (e.g., '2 cm pendant', 'ring size 7') to maintain accurate scale."
|
|
181
|
+
},
|
|
182
|
+
aspectRatio: {
|
|
123
183
|
type: "string",
|
|
124
|
-
description: "
|
|
184
|
+
description: "Output aspect ratio ('1:1', '9:16', '16:9'). Default is '1:1'."
|
|
185
|
+
},
|
|
186
|
+
resolution: {
|
|
187
|
+
type: "string",
|
|
188
|
+
description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
|
|
189
|
+
},
|
|
190
|
+
model: {
|
|
191
|
+
type: "string",
|
|
192
|
+
description: "Model tier slug ('standard', 'pro', 'flash'). Default is 'pro'."
|
|
125
193
|
},
|
|
126
194
|
referenceImage: {
|
|
127
195
|
type: "string",
|
|
128
|
-
description: "
|
|
196
|
+
description: "Optional campaign reference image (URL, data URI, or local path) to copy lighting and mood from."
|
|
197
|
+
},
|
|
198
|
+
logoImage: {
|
|
199
|
+
type: "string",
|
|
200
|
+
description: "Optional brand logo overlay."
|
|
201
|
+
},
|
|
202
|
+
logoPlacement: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "Logo placement ('top-left', 'bottom-right', etc.)."
|
|
205
|
+
},
|
|
206
|
+
overlayText: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: "Optional text to overlay on the image."
|
|
209
|
+
},
|
|
210
|
+
marketingText: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "Optional marketing text or slogan."
|
|
129
213
|
}
|
|
130
214
|
},
|
|
131
|
-
required: ["
|
|
215
|
+
required: ["jewelryImages"]
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
// 4. Apparel Try-On
|
|
219
|
+
const TRY_ON_APPAREL_TOOL = {
|
|
220
|
+
name: "try_on_apparel",
|
|
221
|
+
description: "Virtual fashion apparel try-on. Drapes garments realistically along model anatomy with natural gravitational folds, realistic tension, Hasselblad medium-format camera optics, and ground contact shadows. Returns generated image directly (synchronous).",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
apparelImages: {
|
|
226
|
+
type: "array",
|
|
227
|
+
items: IMAGE_SOURCE_SCHEMA,
|
|
228
|
+
description: "Array of clothing/apparel images. Each can have 'url', 'dataUrl', or local 'path'. At least one required."
|
|
229
|
+
},
|
|
230
|
+
modelImage: {
|
|
231
|
+
type: "string",
|
|
232
|
+
description: "Optional photo of a person/model (URL, data URI, or local path). If omitted, a professional fashion model is generated."
|
|
233
|
+
},
|
|
234
|
+
prompt: {
|
|
235
|
+
type: "string",
|
|
236
|
+
description: "Styling and scene creative direction (e.g., 'standing on Milan street, golden hour sunlight, editorial fashion magazine style')."
|
|
237
|
+
},
|
|
238
|
+
aspectRatio: {
|
|
239
|
+
type: "string",
|
|
240
|
+
description: "Output aspect ratio ('1:1', '9:16', '16:9', '4:5'). Default is '1:1'."
|
|
241
|
+
},
|
|
242
|
+
resolution: {
|
|
243
|
+
type: "string",
|
|
244
|
+
description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
|
|
245
|
+
},
|
|
246
|
+
referenceImage: {
|
|
247
|
+
type: "string",
|
|
248
|
+
description: "Optional style reference image URL, data URI, or local path."
|
|
249
|
+
},
|
|
250
|
+
logoImage: {
|
|
251
|
+
type: "string",
|
|
252
|
+
description: "Optional brand logo overlay."
|
|
253
|
+
},
|
|
254
|
+
overlayText: {
|
|
255
|
+
type: "string",
|
|
256
|
+
description: "Optional text overlay."
|
|
257
|
+
},
|
|
258
|
+
marketingText: {
|
|
259
|
+
type: "string",
|
|
260
|
+
description: "Optional marketing copy."
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
required: ["apparelImages"]
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
// 5. Product Try-On
|
|
267
|
+
const TRY_ON_PRODUCT_TOOL = {
|
|
268
|
+
name: "try_on_product",
|
|
269
|
+
description: "Universal commercial product staging and try-on across footwear, accessories, watches, eyewear, cosmetics, and furniture with 3-point studio lighting and ambient occlusion contact shadows. Returns generated image directly (synchronous).",
|
|
270
|
+
inputSchema: {
|
|
271
|
+
type: "object",
|
|
272
|
+
properties: {
|
|
273
|
+
productImages: {
|
|
274
|
+
type: "array",
|
|
275
|
+
items: IMAGE_SOURCE_SCHEMA,
|
|
276
|
+
description: "Array of product images. At least one required."
|
|
277
|
+
},
|
|
278
|
+
mainImage: {
|
|
279
|
+
type: "string",
|
|
280
|
+
description: "Optional base image (model portrait, empty room, or tabletop) to place the product into."
|
|
281
|
+
},
|
|
282
|
+
prompt: {
|
|
283
|
+
type: "string",
|
|
284
|
+
description: "Creative styling and environment directions."
|
|
285
|
+
},
|
|
286
|
+
sizeMeasurement: {
|
|
287
|
+
type: "string",
|
|
288
|
+
description: "Physical dimensions (e.g., '10 inches', '30 cm') to maintain exact scale."
|
|
289
|
+
},
|
|
290
|
+
aspectRatio: {
|
|
291
|
+
type: "string",
|
|
292
|
+
description: "Output aspect ratio ('1:1', '16:9', '9:16'). Default is '1:1'."
|
|
293
|
+
},
|
|
294
|
+
resolution: {
|
|
295
|
+
type: "string",
|
|
296
|
+
description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
|
|
297
|
+
},
|
|
298
|
+
referenceImage: {
|
|
299
|
+
type: "string",
|
|
300
|
+
description: "Optional style reference image."
|
|
301
|
+
},
|
|
302
|
+
logoImage: {
|
|
303
|
+
type: "string",
|
|
304
|
+
description: "Optional brand logo overlay."
|
|
305
|
+
},
|
|
306
|
+
overlayText: {
|
|
307
|
+
type: "string",
|
|
308
|
+
description: "Optional text overlay."
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
required: ["productImages"]
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
// 6. Festival Post Generator
|
|
315
|
+
const GENERATE_FESTIVAL_POST_TOOL = {
|
|
316
|
+
name: "generate_festival_post",
|
|
317
|
+
description: "Generates high-conversion festival marketing graphics, holiday greetings (Diwali, Eid, Christmas, New Year, Valentine's), and promotional event banners with product and brand logo integration. Returns generated image directly (synchronous).",
|
|
318
|
+
inputSchema: {
|
|
319
|
+
type: "object",
|
|
320
|
+
properties: {
|
|
321
|
+
festivalPrompt: {
|
|
322
|
+
type: "string",
|
|
323
|
+
description: "Festival theme or campaign message (e.g., 'Diwali luxury jewelry promotion with warm golden diyas and floral rangoli')."
|
|
324
|
+
},
|
|
325
|
+
productImage: {
|
|
326
|
+
type: "string",
|
|
327
|
+
description: "Optional hero product image (URL, data URI, or local file path) to place centrally in the festive composition."
|
|
328
|
+
},
|
|
329
|
+
logoImage: {
|
|
330
|
+
type: "string",
|
|
331
|
+
description: "Optional brand logo image."
|
|
332
|
+
},
|
|
333
|
+
overlayText: {
|
|
334
|
+
type: "string",
|
|
335
|
+
description: "Main headline or festive greeting text (e.g., 'Happy Diwali - 20% Off')."
|
|
336
|
+
},
|
|
337
|
+
marketingText: {
|
|
338
|
+
type: "string",
|
|
339
|
+
description: "Supporting promotional message or contact details."
|
|
340
|
+
},
|
|
341
|
+
aspectRatio: {
|
|
342
|
+
type: "string",
|
|
343
|
+
description: "Output aspect ratio ('1:1', '9:16', '16:9'). Default is '1:1'."
|
|
344
|
+
},
|
|
345
|
+
resolution: {
|
|
346
|
+
type: "string",
|
|
347
|
+
description: "Output resolution ('1K', '2K'). Default is '1K'."
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
required: ["festivalPrompt"]
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
// 7. YouTube Thumbnail Generator
|
|
354
|
+
const GENERATE_YOUTUBE_THUMBNAIL_TOOL = {
|
|
355
|
+
name: "generate_youtube_thumbnail",
|
|
356
|
+
description: "Generates high-CTR YouTube thumbnails with rule-of-thirds composition, emotional human expressions, vibrant contrast separation, and legible bold text overlays. Returns generated image directly (synchronous).",
|
|
357
|
+
inputSchema: {
|
|
358
|
+
type: "object",
|
|
359
|
+
properties: {
|
|
360
|
+
prompt: {
|
|
361
|
+
type: "string",
|
|
362
|
+
description: "Core video concept or thumbnail description (e.g., 'Shocked tech reviewer holding futuristic glowing AI smartphone, neon studio background')."
|
|
363
|
+
},
|
|
364
|
+
overlayText: {
|
|
365
|
+
type: "string",
|
|
366
|
+
description: "Short, punchy thumbnail headline text (e.g., 'DON\\'T BUY THIS!')."
|
|
367
|
+
},
|
|
368
|
+
personImage: {
|
|
369
|
+
type: "string",
|
|
370
|
+
description: "Optional photo of the YouTube creator/subject (URL, data URI, or local path) to feature prominently."
|
|
371
|
+
},
|
|
372
|
+
backgroundImage: {
|
|
373
|
+
type: "string",
|
|
374
|
+
description: "Optional custom background image."
|
|
375
|
+
},
|
|
376
|
+
logoImage: {
|
|
377
|
+
type: "string",
|
|
378
|
+
description: "Optional channel/brand logo to place in corner."
|
|
379
|
+
},
|
|
380
|
+
aspectRatio: {
|
|
381
|
+
type: "string",
|
|
382
|
+
description: "Thumbnail aspect ratio. Default is '16:9'."
|
|
383
|
+
},
|
|
384
|
+
resolution: {
|
|
385
|
+
type: "string",
|
|
386
|
+
description: "Output resolution ('1K', '2K'). Default is '1K'."
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
required: ["prompt"]
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
// 8. Passport Photo Creator
|
|
393
|
+
const GENERATE_PASSPORT_PHOTO_TOOL = {
|
|
394
|
+
name: "generate_passport_photo",
|
|
395
|
+
description: "Converts any portrait into an official, compliant passport or visa identity photo with pixel-level facial identity preservation, neutral lighting, and plain white/off-white background. Returns generated image directly (synchronous).",
|
|
396
|
+
inputSchema: {
|
|
397
|
+
type: "object",
|
|
398
|
+
properties: {
|
|
399
|
+
photoDataUri: {
|
|
400
|
+
type: "string",
|
|
401
|
+
description: "Source portrait photo (URL, data URI, or local file path)."
|
|
402
|
+
},
|
|
403
|
+
prompt: {
|
|
404
|
+
type: "string",
|
|
405
|
+
description: "Adjustments (e.g., 'white background, formal dark navy business suit, centered frontal view')."
|
|
406
|
+
},
|
|
407
|
+
aspectRatio: {
|
|
408
|
+
type: "string",
|
|
409
|
+
description: "Passport aspect ratio ('1:1', '4:5'). Default is '1:1'."
|
|
410
|
+
},
|
|
411
|
+
resolution: {
|
|
412
|
+
type: "string",
|
|
413
|
+
description: "Output resolution ('1K', '2K'). Default is '1K'."
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
required: ["photoDataUri"]
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
// 9. Sketch to Real
|
|
420
|
+
const SKETCH_TO_REAL_TOOL = {
|
|
421
|
+
name: "sketch_to_real",
|
|
422
|
+
description: "Transforms hand-drawn sketches, line drawings, or product drafts into photorealistic commercial images while strictly preserving the sketched silhouette, proportions, and outlines. Returns generated image directly (synchronous).",
|
|
423
|
+
inputSchema: {
|
|
424
|
+
type: "object",
|
|
425
|
+
properties: {
|
|
426
|
+
sketchImage: {
|
|
427
|
+
type: "string",
|
|
428
|
+
description: "The source sketch/drawing image (URL, data URI, or local file path)."
|
|
429
|
+
},
|
|
430
|
+
prompt: {
|
|
431
|
+
type: "string",
|
|
432
|
+
description: "Description of materials, textures, colors, and lighting (e.g., 'matte black carbon fiber body, polished titanium accents, studio lighting')."
|
|
433
|
+
},
|
|
434
|
+
style: {
|
|
435
|
+
type: "string",
|
|
436
|
+
description: "Target render style ('Photorealistic', 'Cinematic', '3D Render'). Default is 'Photorealistic'."
|
|
437
|
+
},
|
|
438
|
+
aspectRatio: {
|
|
439
|
+
type: "string",
|
|
440
|
+
description: "Output aspect ratio ('1:1', '16:9', '9:16'). Default is '1:1'."
|
|
441
|
+
},
|
|
442
|
+
resolution: {
|
|
443
|
+
type: "string",
|
|
444
|
+
description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
required: ["sketchImage"]
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
// 10. Upscale Image
|
|
451
|
+
const UPSCALE_IMAGE_TOOL = {
|
|
452
|
+
name: "upscale_image",
|
|
453
|
+
description: "Upscales and enhances any image with AI detail recovery, removing compression artifacts and increasing resolution. Returns upscaled image directly (synchronous).",
|
|
454
|
+
inputSchema: {
|
|
455
|
+
type: "object",
|
|
456
|
+
properties: {
|
|
457
|
+
imageDataUri: {
|
|
458
|
+
type: "string",
|
|
459
|
+
description: "The image to upscale (URL, data URI, or local file path)."
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
required: ["imageDataUri"]
|
|
132
463
|
}
|
|
133
464
|
};
|
|
465
|
+
// 11. QR Code Generator (Full-featured)
|
|
134
466
|
const GENERATE_QR_CODE_TOOL = {
|
|
135
467
|
name: "generate_qr_code",
|
|
136
|
-
description: "Generates
|
|
468
|
+
description: "Generates custom QR codes for URLs, plain text, Wi-Fi credentials, vCards, emails, and UPI payment strings with customizable colors, sizing, and error correction. Returns QR image directly (synchronous).",
|
|
137
469
|
inputSchema: {
|
|
138
470
|
type: "object",
|
|
139
471
|
properties: {
|
|
@@ -144,7 +476,7 @@ const GENERATE_QR_CODE_TOOL = {
|
|
|
144
476
|
},
|
|
145
477
|
value: {
|
|
146
478
|
type: "string",
|
|
147
|
-
description: "The text or
|
|
479
|
+
description: "The text, URL, or data payload to encode."
|
|
148
480
|
},
|
|
149
481
|
size: {
|
|
150
482
|
type: "number",
|
|
@@ -167,9 +499,10 @@ const GENERATE_QR_CODE_TOOL = {
|
|
|
167
499
|
required: ["type", "value"]
|
|
168
500
|
}
|
|
169
501
|
};
|
|
502
|
+
// 12. QR Code Simple
|
|
170
503
|
const GENERATE_QR_CODE_SIMPLE_TOOL = {
|
|
171
504
|
name: "generate_qr_code_simple",
|
|
172
|
-
description: "Quick QR code generation from plain text or URL.
|
|
505
|
+
description: "Quick single-parameter QR code generation from plain text or URL. Returns QR image directly (synchronous).",
|
|
173
506
|
inputSchema: {
|
|
174
507
|
type: "object",
|
|
175
508
|
properties: {
|
|
@@ -181,60 +514,62 @@ const GENERATE_QR_CODE_SIMPLE_TOOL = {
|
|
|
181
514
|
required: ["text"]
|
|
182
515
|
}
|
|
183
516
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
resolution: { type: "string", description: "Resolution (e.g., '720p', '1080p'). Default is '720p'." },
|
|
202
|
-
model: { type: "string", description: "Model slug (e.g., 'google/veo-3.1-fast', 'openrouter/wan-2.7')." },
|
|
203
|
-
aspectRatio: { type: "string", description: "Aspect ratio (e.g., '9:16', '16:9'). Default is '9:16'." },
|
|
204
|
-
generateAudio: { type: "boolean", description: "Whether to generate audio. Default is false." }
|
|
517
|
+
// 13. Get User Credits
|
|
518
|
+
const GET_USER_CREDITS_TOOL = {
|
|
519
|
+
name: "get_user_credits",
|
|
520
|
+
description: "Checks your current CodingMantra AI wallet credit balance. Useful before launching batches or large video/image tasks.",
|
|
521
|
+
inputSchema: {
|
|
522
|
+
type: "object",
|
|
523
|
+
properties: {}
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
// 14. List Image Models
|
|
527
|
+
const LIST_IMAGE_MODELS_TOOL = {
|
|
528
|
+
name: "list_image_models",
|
|
529
|
+
description: "Lists all available CodingMantra AI image generation models (e.g., Gemini Flash, Gemini Pro, Flux Pro), including supported resolutions, aspect ratios, and credit costs.",
|
|
530
|
+
inputSchema: {
|
|
531
|
+
type: "object",
|
|
532
|
+
properties: {}
|
|
533
|
+
}
|
|
205
534
|
};
|
|
535
|
+
// 15. Product Video Generation (Async)
|
|
206
536
|
const GENERATE_PRODUCT_VIDEO_TOOL = {
|
|
207
537
|
name: "generate_product_video",
|
|
208
|
-
description: "Generates
|
|
538
|
+
description: "Generates commercial product videography. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion.",
|
|
209
539
|
inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
|
|
210
540
|
};
|
|
541
|
+
// 16. Apparel Video Generation (Async)
|
|
211
542
|
const GENERATE_APPAREL_VIDEO_TOOL = {
|
|
212
543
|
name: "generate_apparel_video",
|
|
213
|
-
description: "Generates
|
|
544
|
+
description: "Generates runway fashion and apparel motion showcase videos. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion.",
|
|
214
545
|
inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
|
|
215
546
|
};
|
|
547
|
+
// 17. Festival Video Generation (Async)
|
|
216
548
|
const GENERATE_FESTIVAL_VIDEO_TOOL = {
|
|
217
549
|
name: "generate_festival_video",
|
|
218
|
-
description: "Generates
|
|
550
|
+
description: "Generates celebratory festival greetings and motion graphics. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion.",
|
|
219
551
|
inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
|
|
220
552
|
};
|
|
553
|
+
// 18. Jewelry Video Generation (Async)
|
|
221
554
|
const GENERATE_JEWELRY_VIDEO_TOOL = {
|
|
222
555
|
name: "generate_jewelry_video",
|
|
223
|
-
description: "Generates
|
|
556
|
+
description: "Generates luxury jewelry videography with optical brilliance and rotating display. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion.",
|
|
224
557
|
inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
|
|
225
558
|
};
|
|
559
|
+
// 19. Logo Video Generation (Async)
|
|
226
560
|
const GENERATE_LOGO_VIDEO_TOOL = {
|
|
227
561
|
name: "generate_logo_video",
|
|
228
|
-
description: "Generates
|
|
562
|
+
description: "Generates 3D logo reveal and motion graphic video intros. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion.",
|
|
229
563
|
inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
|
|
230
564
|
};
|
|
565
|
+
// 20. Check Video Job Status (Polling)
|
|
231
566
|
const CHECK_JOB_STATUS_TOOL = {
|
|
232
567
|
name: "check_video_job_status",
|
|
233
|
-
description: "Checks the status of an asynchronous video generation job. Call this repeatedly (
|
|
568
|
+
description: "Checks the real-time status of an asynchronous video generation job. Call this repeatedly (every 10-30 seconds) until the job status is 'completed' or 'failed'.",
|
|
234
569
|
inputSchema: {
|
|
235
570
|
type: "object",
|
|
236
571
|
properties: {
|
|
237
|
-
jobId: { type: "string", description: "The ID of the job to
|
|
572
|
+
jobId: { type: "string", description: "The ID of the video job to track." }
|
|
238
573
|
},
|
|
239
574
|
required: ["jobId"]
|
|
240
575
|
}
|
|
@@ -245,8 +580,18 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
245
580
|
tools: [
|
|
246
581
|
GENERATE_PRODUCT_PHOTO_TOOL,
|
|
247
582
|
GENERATE_LOGO_TOOL,
|
|
583
|
+
TRY_ON_JEWELRY_TOOL,
|
|
584
|
+
TRY_ON_APPAREL_TOOL,
|
|
585
|
+
TRY_ON_PRODUCT_TOOL,
|
|
586
|
+
GENERATE_FESTIVAL_POST_TOOL,
|
|
587
|
+
GENERATE_YOUTUBE_THUMBNAIL_TOOL,
|
|
588
|
+
GENERATE_PASSPORT_PHOTO_TOOL,
|
|
589
|
+
SKETCH_TO_REAL_TOOL,
|
|
590
|
+
UPSCALE_IMAGE_TOOL,
|
|
248
591
|
GENERATE_QR_CODE_TOOL,
|
|
249
592
|
GENERATE_QR_CODE_SIMPLE_TOOL,
|
|
593
|
+
GET_USER_CREDITS_TOOL,
|
|
594
|
+
LIST_IMAGE_MODELS_TOOL,
|
|
250
595
|
GENERATE_PRODUCT_VIDEO_TOOL,
|
|
251
596
|
GENERATE_APPAREL_VIDEO_TOOL,
|
|
252
597
|
GENERATE_FESTIVAL_VIDEO_TOOL,
|
|
@@ -259,86 +604,195 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
259
604
|
// ─── Tool Execution ─────────────────────────────────────────────────────────────
|
|
260
605
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
261
606
|
try {
|
|
262
|
-
const { name, arguments:
|
|
263
|
-
|
|
607
|
+
const { name, arguments: rawArgs } = request.params;
|
|
608
|
+
const args = rawArgs || {};
|
|
609
|
+
// ── 1. Product Photography (synchronous) ──────────────────────────────────
|
|
264
610
|
if (name === 'generate_product_photo') {
|
|
611
|
+
const payload = {
|
|
612
|
+
...args,
|
|
613
|
+
productImages: resolveImagesArray(args.productImages),
|
|
614
|
+
referenceImage: resolveImageInput(args.referenceImage),
|
|
615
|
+
logoImage: resolveImageInput(args.logoImage),
|
|
616
|
+
};
|
|
265
617
|
const response = await apiClient('/image/product-photography', {
|
|
266
618
|
method: 'POST',
|
|
267
|
-
body: JSON.stringify(
|
|
619
|
+
body: JSON.stringify(payload),
|
|
268
620
|
});
|
|
269
|
-
|
|
270
|
-
let text = '✅ Product photo generated successfully!\n\n';
|
|
271
|
-
if (result?.imageUrl) {
|
|
272
|
-
text += `Image URL: ${result.imageUrl}`;
|
|
273
|
-
}
|
|
274
|
-
else if (result?.photoDataUri) {
|
|
275
|
-
text += `Image data returned (base64). Length: ${String(result.photoDataUri).length} characters.\nData URI: ${String(result.photoDataUri).substring(0, 100)}...`;
|
|
276
|
-
}
|
|
277
|
-
else if (result?.output) {
|
|
278
|
-
text += `Image data returned (base64). Length: ${String(result.output).length} characters.`;
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
text += `Result: ${JSON.stringify(result).substring(0, 500)}`;
|
|
282
|
-
}
|
|
283
|
-
return { content: [{ type: "text", text }] };
|
|
621
|
+
return formatImageToolResult(response, 'Product Photo Generated');
|
|
284
622
|
}
|
|
285
|
-
// ── Logo Creator (synchronous)
|
|
623
|
+
// ── 2. Logo Creator (synchronous) ─────────────────────────────────────────
|
|
286
624
|
if (name === 'generate_logo') {
|
|
625
|
+
const payload = {
|
|
626
|
+
...args,
|
|
627
|
+
referenceImage: resolveImageInput(args.referenceImage),
|
|
628
|
+
};
|
|
287
629
|
const response = await apiClient('/image/logo-creator', {
|
|
288
630
|
method: 'POST',
|
|
289
|
-
body: JSON.stringify(
|
|
631
|
+
body: JSON.stringify(payload),
|
|
290
632
|
});
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
633
|
+
return formatImageToolResult(response, 'Brand Logo Generated');
|
|
634
|
+
}
|
|
635
|
+
// ── 3. Jewelry Try-On (synchronous) ───────────────────────────────────────
|
|
636
|
+
if (name === 'try_on_jewelry') {
|
|
637
|
+
const payload = {
|
|
638
|
+
...args,
|
|
639
|
+
jewelryImages: resolveImagesArray(args.jewelryImages),
|
|
640
|
+
personImage: resolveImageInput(args.personImage),
|
|
641
|
+
referenceImage: resolveImageInput(args.referenceImage),
|
|
642
|
+
logoImage: resolveImageInput(args.logoImage),
|
|
643
|
+
};
|
|
644
|
+
const response = await apiClient('/image/jewelry-try-on', {
|
|
645
|
+
method: 'POST',
|
|
646
|
+
body: JSON.stringify(payload),
|
|
647
|
+
});
|
|
648
|
+
return formatImageToolResult(response, 'Jewelry Try-On Image Generated');
|
|
649
|
+
}
|
|
650
|
+
// ── 4. Apparel Try-On (synchronous) ───────────────────────────────────────
|
|
651
|
+
if (name === 'try_on_apparel') {
|
|
652
|
+
const payload = {
|
|
653
|
+
...args,
|
|
654
|
+
apparelImages: resolveImagesArray(args.apparelImages),
|
|
655
|
+
modelImage: resolveImageInput(args.modelImage),
|
|
656
|
+
referenceImage: resolveImageInput(args.referenceImage),
|
|
657
|
+
logoImage: resolveImageInput(args.logoImage),
|
|
658
|
+
};
|
|
659
|
+
const response = await apiClient('/image/apparel-try-on', {
|
|
660
|
+
method: 'POST',
|
|
661
|
+
body: JSON.stringify(payload),
|
|
662
|
+
});
|
|
663
|
+
return formatImageToolResult(response, 'Apparel Try-On Image Generated');
|
|
664
|
+
}
|
|
665
|
+
// ── 5. Product Try-On (synchronous) ───────────────────────────────────────
|
|
666
|
+
if (name === 'try_on_product') {
|
|
667
|
+
const payload = {
|
|
668
|
+
...args,
|
|
669
|
+
productImages: resolveImagesArray(args.productImages),
|
|
670
|
+
mainImage: resolveImageInput(args.mainImage),
|
|
671
|
+
referenceImage: resolveImageInput(args.referenceImage),
|
|
672
|
+
logoImage: resolveImageInput(args.logoImage),
|
|
673
|
+
};
|
|
674
|
+
const response = await apiClient('/image/product-try-on', {
|
|
675
|
+
method: 'POST',
|
|
676
|
+
body: JSON.stringify(payload),
|
|
677
|
+
});
|
|
678
|
+
return formatImageToolResult(response, 'Product Try-On Image Generated');
|
|
679
|
+
}
|
|
680
|
+
// ── 6. Festival Post (synchronous) ────────────────────────────────────────
|
|
681
|
+
if (name === 'generate_festival_post') {
|
|
682
|
+
const payload = {
|
|
683
|
+
...args,
|
|
684
|
+
productImage: resolveImageInput(args.productImage),
|
|
685
|
+
logoImage: resolveImageInput(args.logoImage),
|
|
686
|
+
};
|
|
687
|
+
const response = await apiClient('/image/festival-post-generator', {
|
|
688
|
+
method: 'POST',
|
|
689
|
+
body: JSON.stringify(payload),
|
|
690
|
+
});
|
|
691
|
+
return formatImageToolResult(response, 'Festival Post Generated');
|
|
692
|
+
}
|
|
693
|
+
// ── 7. YouTube Thumbnail (synchronous) ────────────────────────────────────
|
|
694
|
+
if (name === 'generate_youtube_thumbnail') {
|
|
695
|
+
const payload = {
|
|
696
|
+
...args,
|
|
697
|
+
personImage: resolveImageInput(args.personImage),
|
|
698
|
+
backgroundImage: resolveImageInput(args.backgroundImage),
|
|
699
|
+
logoImage: resolveImageInput(args.logoImage),
|
|
700
|
+
};
|
|
701
|
+
const response = await apiClient('/image/youtube-thumbnail-generator', {
|
|
702
|
+
method: 'POST',
|
|
703
|
+
body: JSON.stringify(payload),
|
|
704
|
+
});
|
|
705
|
+
return formatImageToolResult(response, 'YouTube Thumbnail Generated');
|
|
706
|
+
}
|
|
707
|
+
// ── 8. Passport Photo (synchronous) ───────────────────────────────────────
|
|
708
|
+
if (name === 'generate_passport_photo') {
|
|
709
|
+
const payload = {
|
|
710
|
+
...args,
|
|
711
|
+
photoDataUri: resolveImageInput(args.photoDataUri),
|
|
712
|
+
};
|
|
713
|
+
const response = await apiClient('/image/passport-photo', {
|
|
714
|
+
method: 'POST',
|
|
715
|
+
body: JSON.stringify(payload),
|
|
716
|
+
});
|
|
717
|
+
return formatImageToolResult(response, 'Passport Photo Generated');
|
|
718
|
+
}
|
|
719
|
+
// ── 9. Sketch to Real (synchronous) ───────────────────────────────────────
|
|
720
|
+
if (name === 'sketch_to_real') {
|
|
721
|
+
const payload = {
|
|
722
|
+
...args,
|
|
723
|
+
sketchImage: resolveImageInput(args.sketchImage),
|
|
724
|
+
referenceImage: resolveImageInput(args.referenceImage),
|
|
725
|
+
logoImage: resolveImageInput(args.logoImage),
|
|
726
|
+
};
|
|
727
|
+
const response = await apiClient('/image/sketch-to-real', {
|
|
728
|
+
method: 'POST',
|
|
729
|
+
body: JSON.stringify(payload),
|
|
730
|
+
});
|
|
731
|
+
return formatImageToolResult(response, 'Sketch-to-Real Image Generated');
|
|
732
|
+
}
|
|
733
|
+
// ── 10. Upscale Image (synchronous) ───────────────────────────────────────
|
|
734
|
+
if (name === 'upscale_image') {
|
|
735
|
+
const payload = {
|
|
736
|
+
...args,
|
|
737
|
+
imageDataUri: resolveImageInput(args.imageDataUri),
|
|
738
|
+
};
|
|
739
|
+
const response = await apiClient('/image/upscale-image', {
|
|
740
|
+
method: 'POST',
|
|
741
|
+
body: JSON.stringify(payload),
|
|
742
|
+
});
|
|
743
|
+
return formatImageToolResult(response, 'Image Upscaled Successfully');
|
|
306
744
|
}
|
|
307
|
-
// ── QR Code (POST, full-featured)
|
|
745
|
+
// ── 11. QR Code (POST, full-featured) ─────────────────────────────────────
|
|
308
746
|
if (name === 'generate_qr_code') {
|
|
309
747
|
const response = await apiClient('/qr-code/generate', {
|
|
310
748
|
method: 'POST',
|
|
311
749
|
body: JSON.stringify(args),
|
|
312
750
|
});
|
|
313
|
-
|
|
314
|
-
if (qrDataUrl) {
|
|
315
|
-
return {
|
|
316
|
-
content: [{ type: "text", text: `✅ QR Code generated successfully!\n\nData URI: ${qrDataUrl}` }],
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
return {
|
|
320
|
-
content: [{ type: "text", text: `✅ QR Code generated. Result: ${JSON.stringify(response.data).substring(0, 500)}` }],
|
|
321
|
-
};
|
|
751
|
+
return formatImageToolResult(response, 'QR Code Generated');
|
|
322
752
|
}
|
|
323
|
-
// ── QR Code Simple (GET,
|
|
753
|
+
// ── 12. QR Code Simple (GET, legacy) ──────────────────────────────────────
|
|
324
754
|
if (name === 'generate_qr_code_simple') {
|
|
325
755
|
const { text } = args;
|
|
326
756
|
const encodedText = encodeURIComponent(text);
|
|
327
|
-
const apiKey = process.env.CODINGMANTRA_API_KEY;
|
|
757
|
+
const apiKey = process.env.CODINGMANTRA_API_KEY || '';
|
|
328
758
|
const response = await apiClient(`/qr-code/generate-base64/${apiKey}/${encodedText}`, {
|
|
329
759
|
method: 'GET',
|
|
330
760
|
});
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
761
|
+
return formatImageToolResult(response, 'QR Code Generated');
|
|
762
|
+
}
|
|
763
|
+
// ── 13. Get User Credits ──────────────────────────────────────────────────
|
|
764
|
+
if (name === 'get_user_credits') {
|
|
765
|
+
const response = await apiClient('/user/credits', {
|
|
766
|
+
method: 'GET',
|
|
767
|
+
});
|
|
768
|
+
const credits = response.data?.credits ?? 0;
|
|
769
|
+
return {
|
|
770
|
+
content: [{
|
|
771
|
+
type: "text",
|
|
772
|
+
text: `💳 **CodingMantra Wallet Balance**\n\n- Available Credits: **${credits}**\n- Need more credits? Top up at [CodingMantra Pricing](https://codingmantra.com/pricing)\n- View full usage history in your [Account Dashboard](https://codingmantra.com/dashboard)`
|
|
773
|
+
}]
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
// ── 14. List Image Models ─────────────────────────────────────────────────
|
|
777
|
+
if (name === 'list_image_models') {
|
|
778
|
+
const response = await apiClient('/image/image-model-options', {
|
|
779
|
+
method: 'GET',
|
|
780
|
+
});
|
|
781
|
+
const models = response.data || [];
|
|
782
|
+
let text = `🤖 **Available CodingMantra Image Models**\n\n`;
|
|
783
|
+
for (const m of models) {
|
|
784
|
+
text += `#### ${m.name} (\`${m.model}\`)\n`;
|
|
785
|
+
text += `- **Provider:** ${m.provider}\n`;
|
|
786
|
+
text += `- **Description:** ${m.description || 'High-performance generative model'}\n`;
|
|
787
|
+
text += `- **Supported Resolutions:** ${m.supportedResolutions?.join(', ') || '1K'}\n`;
|
|
788
|
+
text += `- **Supported Aspect Ratios:** ${m.supportedAspectRatios?.join(', ') || '1:1'}\n`;
|
|
789
|
+
text += `- **Credit Cost:** ${m.estimatedCost?.credits ?? 10} credits\n\n`;
|
|
336
790
|
}
|
|
337
791
|
return {
|
|
338
|
-
content: [{ type: "text", text:
|
|
792
|
+
content: [{ type: "text", text: text.trim() }]
|
|
339
793
|
};
|
|
340
794
|
}
|
|
341
|
-
// ── Video Generation (Asynchronous)
|
|
795
|
+
// ── 15-19. Video Generation (Asynchronous) ────────────────────────────────
|
|
342
796
|
const videoEndpoints = {
|
|
343
797
|
generate_product_video: '/product-video-generation',
|
|
344
798
|
generate_apparel_video: '/apparel-video-generation',
|
|
@@ -347,22 +801,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
347
801
|
generate_logo_video: '/logo-video-generation'
|
|
348
802
|
};
|
|
349
803
|
if (videoEndpoints[name]) {
|
|
804
|
+
const payload = {
|
|
805
|
+
...args,
|
|
806
|
+
mainImage: resolveImageInput(args.mainImage),
|
|
807
|
+
startImage: resolveImageInput(args.startImage),
|
|
808
|
+
endImage: resolveImageInput(args.endImage),
|
|
809
|
+
};
|
|
350
810
|
const response = await apiClient(videoEndpoints[name], {
|
|
351
811
|
method: 'POST',
|
|
352
|
-
body: JSON.stringify(
|
|
812
|
+
body: JSON.stringify(payload),
|
|
353
813
|
});
|
|
354
814
|
const result = response.data || response;
|
|
355
815
|
if (result?.jobId) {
|
|
356
816
|
return {
|
|
357
817
|
content: [{
|
|
358
818
|
type: "text",
|
|
359
|
-
text:
|
|
819
|
+
text: `🎬 **Video Generation Job Queued Successfully!**\n\n- **Job ID:** \`${result.jobId}\`\n- **Status:** Queued\n- **Message:** ${result.message || 'Video job dispatched to rendering pipeline.'}\n\n👉 **Next Step:** Call the \`check_video_job_status\` tool with \`jobId: "${result.jobId}"\` every 15-30 seconds until completed. Rendering typically takes 1-3 minutes.`
|
|
360
820
|
}]
|
|
361
821
|
};
|
|
362
822
|
}
|
|
363
823
|
return { content: [{ type: "text", text: `Job started. Result: ${JSON.stringify(result).substring(0, 500)}` }] };
|
|
364
824
|
}
|
|
365
|
-
// ── Check Job Status
|
|
825
|
+
// ── 20. Check Video Job Status ────────────────────────────────────────────
|
|
366
826
|
if (name === 'check_video_job_status') {
|
|
367
827
|
const { jobId } = args;
|
|
368
828
|
const response = await apiClient(`/jobs/status?jobId=${encodeURIComponent(jobId)}`, {
|
|
@@ -371,15 +831,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
371
831
|
const result = response.data || response;
|
|
372
832
|
if (result?.job) {
|
|
373
833
|
const job = result.job;
|
|
374
|
-
let text =
|
|
834
|
+
let text = `### 🎬 Video Job Status: **${job.status.toUpperCase()}**\n\n`;
|
|
835
|
+
text += `- **Progress:** ${job.progress}%\n`;
|
|
836
|
+
text += `- **Stage:** ${job.stage || 'Processing'}\n`;
|
|
375
837
|
if (job.status === 'completed' && job.result?.videoUrl) {
|
|
376
|
-
text += `\n
|
|
838
|
+
text += `\n✅ **Video Ready:** [Watch / Download Video](${job.result.videoUrl})\n`;
|
|
839
|
+
text += `\n📁 **Gallery:** View in your [CodingMantra Gallery](https://codingmantra.com/my-gallery)`;
|
|
377
840
|
}
|
|
378
841
|
else if (job.status === 'failed') {
|
|
379
|
-
text += `\n
|
|
842
|
+
text += `\n❌ **Job Failed:** ${job.error || 'Unknown error occurred during generation.'}\n`;
|
|
843
|
+
text += `Your credits have been refunded automatically.`;
|
|
380
844
|
}
|
|
381
845
|
else {
|
|
382
|
-
text += `\n
|
|
846
|
+
text += `\n⏳ **Job is actively rendering.** Please poll again in 15-20 seconds.`;
|
|
383
847
|
}
|
|
384
848
|
return { content: [{ type: "text", text }] };
|
|
385
849
|
}
|
|
@@ -389,7 +853,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
389
853
|
}
|
|
390
854
|
catch (error) {
|
|
391
855
|
return {
|
|
392
|
-
content: [{ type: "text", text: `❌ Error
|
|
856
|
+
content: [{ type: "text", text: `❌ **Error:** ${error.message || error}` }],
|
|
393
857
|
isError: true,
|
|
394
858
|
};
|
|
395
859
|
}
|
|
@@ -398,9 +862,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
398
862
|
async function run() {
|
|
399
863
|
const transport = new StdioServerTransport();
|
|
400
864
|
await server.connect(transport);
|
|
401
|
-
console.error("CodingMantra MCP Server running on stdio");
|
|
865
|
+
console.error("[CodingMantra MCP] Server v0.2.0 initialized and running on stdio (20 tools active)");
|
|
402
866
|
}
|
|
403
867
|
run().catch((error) => {
|
|
404
|
-
console.error("Fatal error running server:", error);
|
|
868
|
+
console.error("Fatal error running CodingMantra MCP server:", error);
|
|
405
869
|
process.exit(1);
|
|
406
870
|
});
|