@codingmantra/mcp-server 0.1.4 → 0.2.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/build/index.js CHANGED
@@ -3,35 +3,74 @@ 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.1.0",
9
+ version: "0.2.2",
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", minLength: 10, maxLength: 5000, description: "Required video description or creative prompt (10-5000 characters)." },
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
+ maxItems: 4,
32
+ items: { type: "object", properties: { image: { type: "string", description: "Public image URL, data URI, or local file path." }, label: { type: "string", description: "Optional reference label." } }, required: ["image"] },
33
+ description: "Reference images for style matching. Choose only one image mode: main image, frames, or references. Veo supports up to 3 references and requires 8 seconds for references or an end frame."
34
+ },
35
+ negativePrompt: { type: "string", description: "Elements to exclude from the video." },
36
+ dialogue: { type: "string", maxLength: 200, description: "Optional voiceover or dialogue script (up to 200 characters)." },
37
+ soundSuggestion: { type: "string", description: "Background music or sound effects direction." },
38
+ cameraMotion: { type: "string", description: "Camera motion instructions (e.g., 'orbit right', 'slow push-in')." },
39
+ ambientNoise: { type: "string", description: "Ambient sound description." },
40
+ language: { type: "string", description: "Voiceover language." },
41
+ timeDuration: { type: "string", description: "Required duration in seconds supported by the selected model. Veo supports 4, 6, or 8; use 8 for 1080p, references, or an end frame." },
42
+ resolution: { type: "string", description: "Required resolution supported by the selected model (480p, 720p, 768p, or 1080p)." },
43
+ model: { type: "string", description: "Model slug (e.g., 'google/veo-3.1-fast', 'alibaba/wan-3.0')." },
44
+ aspectRatio: { type: "string", description: "Required aspect ratio supported by the selected model (e.g., 9:16 or 16:9)." },
45
+ generateAudio: { type: "boolean", description: "Whether to generate synchronized audio. Default is false." },
46
+ seed: { type: "integer", minimum: 0, maximum: Number.MAX_SAFE_INTEGER, description: "Optional nonnegative random seed for models that support it." },
47
+ guidanceScale: { type: "number", description: "Optional guidance strength for models that support it." },
48
+ soundEffects: { type: "string", description: "Sound effects direction for Story Teller." },
49
+ styleAmbiance: { type: "string", description: "Visual style and atmosphere for Story Teller." },
50
+ characters: { type: "array", description: "Optional named characters for consistent storytelling.", items: {
51
+ type: "object", properties: {
52
+ id: { type: "string", description: "Unique character ID." },
53
+ name: { type: "string", description: "Character name." },
54
+ description: { type: "string", description: "Character appearance and role." }
55
+ }, required: ["id", "name", "description"]
56
+ } }
57
+ };
14
58
  // ─── Tool Definitions ──────────────────────────────────────────────────────────
59
+ // 1. Product Photography
15
60
  const GENERATE_PRODUCT_PHOTO_TOOL = {
16
61
  name: "generate_product_photo",
17
- description: "Generates an AI product photography image with a specific scene prompt. Automatically deducts credits from your CodingMantra account. Returns the generated image directly (synchronous).",
62
+ 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
63
  inputSchema: {
19
64
  type: "object",
20
65
  properties: {
21
66
  productImages: {
22
67
  type: "array",
23
- items: {
24
- type: "object",
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."
68
+ items: IMAGE_SOURCE_SCHEMA,
69
+ description: "Array of product images. Each item can have 'url', 'dataUrl', or local 'path'. At least one required."
31
70
  },
32
71
  scenePrompt: {
33
72
  type: "string",
34
- description: "A detailed description of the background scene and setting for the product photo."
73
+ description: "A detailed description of the background scene, surface, and environment for the product photo."
35
74
  },
36
75
  model: {
37
76
  type: "string",
@@ -39,23 +78,23 @@ const GENERATE_PRODUCT_PHOTO_TOOL = {
39
78
  },
40
79
  resolution: {
41
80
  type: "string",
42
- description: "Output resolution (e.g., '1K', '2K', '4K'). Default is '1K'.",
81
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'.",
43
82
  },
44
83
  aspectRatio: {
45
84
  type: "string",
46
- description: "Output aspect ratio (e.g., '1:1', '16:9', '9:16'). Default is '1:1'.",
85
+ description: "Output aspect ratio ('1:1', '16:9', '9:16', '4:5'). Default is '1:1'.",
47
86
  },
48
87
  referenceImage: {
49
88
  type: "string",
50
- description: "An optional URL or data URI of a reference image to guide style."
89
+ description: "Optional URL, data URI, or local file path of a style reference image."
51
90
  },
52
91
  promptId: {
53
92
  type: "string",
54
- description: "Optional ID of a pre-defined prompt template."
93
+ description: "Optional ID of a pre-defined background template."
55
94
  },
56
95
  logoImage: {
57
96
  type: "string",
58
- description: "Optional URL or Base64 data URI of a logo to place on the image."
97
+ description: "Optional URL, data URI, or local file path of a brand logo to place on the image."
59
98
  },
60
99
  logoPlacement: {
61
100
  type: "string",
@@ -63,7 +102,7 @@ const GENERATE_PRODUCT_PHOTO_TOOL = {
63
102
  },
64
103
  logoSize: {
65
104
  type: "number",
66
- description: "Scale of the logo overlay (0.1 to 1.0)."
105
+ description: "Scale of the logo overlay (0.05 to 0.5). Default is 0.15."
67
106
  },
68
107
  overlayText: {
69
108
  type: "string",
@@ -71,15 +110,16 @@ const GENERATE_PRODUCT_PHOTO_TOOL = {
71
110
  },
72
111
  marketingText: {
73
112
  type: "string",
74
- description: "Optional marketing text to overlay."
113
+ description: "Optional marketing text or slogan to overlay."
75
114
  }
76
115
  },
77
116
  required: ["productImages", "scenePrompt"]
78
117
  }
79
118
  };
119
+ // 2. Logo Creator
80
120
  const GENERATE_LOGO_TOOL = {
81
121
  name: "generate_logo",
82
- description: "Generates an AI brand logo. Requires a company name and style description. Automatically deducts credits from your CodingMantra account. Returns the generated logo directly (synchronous).",
122
+ description: "Generates bespoke vector-style brand logos, monograms, emblems, and modern wordmarks with character-perfect typography, color palettes, and reference image guidance. Default model is Flash. Returns the generated logo directly for inline display (synchronous).",
83
123
  inputSchema: {
84
124
  type: "object",
85
125
  properties: {
@@ -89,7 +129,7 @@ const GENERATE_LOGO_TOOL = {
89
129
  },
90
130
  stylePrompt: {
91
131
  type: "string",
92
- description: "A detailed style description for the logo (e.g., 'minimalist flat vector logo, dark blue and gold color scheme, modern tech feel')."
132
+ description: "A detailed style description for the logo (e.g., 'minimalist flat vector logo, navy blue and gold, clean tech aesthetic')."
93
133
  },
94
134
  subtitle: {
95
135
  type: "string",
@@ -97,43 +137,646 @@ const GENERATE_LOGO_TOOL = {
97
137
  },
98
138
  industry: {
99
139
  type: "string",
100
- description: "The industry the company operates in (e.g., 'Technology', 'Food & Beverage')."
140
+ description: "The industry the company operates in (e.g., 'Technology', 'Luxury Jewelry', 'Fashion')."
101
141
  },
102
142
  ideas: {
103
143
  type: "string",
104
- description: "User-provided ideas, keywords, or concepts for the logo."
144
+ description: "Core keywords, symbols, or concepts for the logo."
105
145
  },
106
146
  colors: {
107
147
  type: "string",
108
- description: "Preferred color palette description (e.g., 'blue and silver', '#FF5733 and white')."
148
+ description: "Preferred color palette description (e.g., 'emerald green and brushed gold')."
149
+ },
150
+ aspectRatio: {
151
+ type: "string",
152
+ description: "Output aspect ratio ('1:1', '16:9', '4:3'). Default is '1:1'.",
153
+ },
154
+ model: {
155
+ type: "string",
156
+ description: "Model tier to use ('flash' or 'pro'). Default is 'flash' (google/gemini-3.1-flash-image)."
157
+ },
158
+ modelId: {
159
+ type: "string",
160
+ description: "Full image model ID if specific model desired (e.g. google/gemini-3.1-flash-image).",
161
+ },
162
+ resolution: {
163
+ type: "string",
164
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'.",
165
+ },
166
+ referenceImage: {
167
+ type: "string",
168
+ description: "Optional style reference image URL, data URI, or local file path."
169
+ }
170
+ },
171
+ required: ["companyName", "stylePrompt"]
172
+ }
173
+ };
174
+ // 3. Jewelry Try-On
175
+ const TRY_ON_JEWELRY_TOOL = {
176
+ name: "try_on_jewelry",
177
+ 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).",
178
+ inputSchema: {
179
+ type: "object",
180
+ properties: {
181
+ jewelryImages: {
182
+ type: "array",
183
+ items: IMAGE_SOURCE_SCHEMA,
184
+ description: "Array of jewelry product images. Each can have 'url', 'dataUrl', or local 'path'. At least one required."
185
+ },
186
+ personImage: {
187
+ type: "string",
188
+ 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."
189
+ },
190
+ prompt: {
191
+ type: "string",
192
+ description: "Creative styling directions (e.g., 'wearing black velvet evening gown, soft studio rim lighting, Parisian luxury ambiance')."
193
+ },
194
+ sizeMeasurement: {
195
+ type: "string",
196
+ description: "Physical dimensions (e.g., '2 cm pendant', 'ring size 7') to maintain accurate scale."
197
+ },
198
+ aspectRatio: {
199
+ type: "string",
200
+ description: "Output aspect ratio ('1:1', '9:16', '16:9'). Default is '1:1'."
201
+ },
202
+ resolution: {
203
+ type: "string",
204
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
205
+ },
206
+ model: {
207
+ type: "string",
208
+ description: "Model tier slug ('standard', 'pro', 'flash'). Default is 'flash'."
209
+ },
210
+ referenceImage: {
211
+ type: "string",
212
+ description: "Optional campaign reference image (URL, data URI, or local path) to copy lighting and mood from."
213
+ },
214
+ logoImage: {
215
+ type: "string",
216
+ description: "Optional brand logo overlay."
217
+ },
218
+ logoPlacement: {
219
+ type: "string",
220
+ description: "Logo placement ('top-left', 'bottom-right', etc.)."
221
+ },
222
+ overlayText: {
223
+ type: "string",
224
+ description: "Optional text to overlay on the image."
225
+ },
226
+ marketingText: {
227
+ type: "string",
228
+ description: "Optional marketing text or slogan."
229
+ }
230
+ },
231
+ required: ["jewelryImages"]
232
+ }
233
+ };
234
+ // 4. Apparel Try-On
235
+ const TRY_ON_APPAREL_TOOL = {
236
+ name: "try_on_apparel",
237
+ description: "Virtual fashion apparel try-on. Drapes garments realistically along model anatomy with natural gravitational folds, realistic tension, ambient occlusion contact shadows, and editorial lighting. Default model tier is Flash. Returns generated image directly (synchronous).",
238
+ inputSchema: {
239
+ type: "object",
240
+ properties: {
241
+ apparelImages: {
242
+ type: "array",
243
+ items: IMAGE_SOURCE_SCHEMA,
244
+ description: "Array of clothing/apparel images. Each can have 'url', 'dataUrl', or local 'path'. At least one required."
245
+ },
246
+ modelImage: {
247
+ type: "string",
248
+ description: "Optional photo of a person/model (URL, data URI, or local path). If omitted, a professional fashion model is generated."
249
+ },
250
+ prompt: {
251
+ type: "string",
252
+ description: "Styling and scene creative direction (e.g., 'standing on Milan street, golden hour sunlight, editorial fashion magazine style')."
253
+ },
254
+ aspectRatio: {
255
+ type: "string",
256
+ description: "Output aspect ratio ('9:16', '1:1', '16:9', '4:5'). Default is '9:16'."
257
+ },
258
+ resolution: {
259
+ type: "string",
260
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
261
+ },
262
+ model: {
263
+ type: "string",
264
+ description: "The model tier ('flash', 'pro', 'standard'). Default is 'flash'."
265
+ },
266
+ referenceImage: {
267
+ type: "string",
268
+ description: "Optional style reference image URL, data URI, or local path."
269
+ },
270
+ logoImage: {
271
+ type: "string",
272
+ description: "Optional brand logo overlay."
273
+ },
274
+ overlayText: {
275
+ type: "string",
276
+ description: "Optional text overlay."
277
+ },
278
+ marketingText: {
279
+ type: "string",
280
+ description: "Optional marketing copy."
281
+ }
282
+ },
283
+ required: ["apparelImages"]
284
+ }
285
+ };
286
+ // 5. Product Try-On
287
+ const TRY_ON_PRODUCT_TOOL = {
288
+ name: "try_on_product",
289
+ 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. Default model tier is Flash. Returns generated image directly (synchronous).",
290
+ inputSchema: {
291
+ type: "object",
292
+ properties: {
293
+ productImages: {
294
+ type: "array",
295
+ items: IMAGE_SOURCE_SCHEMA,
296
+ description: "Array of product images. At least one required."
297
+ },
298
+ mainImage: {
299
+ type: "string",
300
+ description: "Optional base image (model portrait, empty room, or tabletop) to place the product into."
301
+ },
302
+ prompt: {
303
+ type: "string",
304
+ description: "Creative styling and environment directions."
305
+ },
306
+ sizeMeasurement: {
307
+ type: "string",
308
+ description: "Physical dimensions (e.g., '10 inches', '30 cm') to maintain exact scale."
309
+ },
310
+ aspectRatio: {
311
+ type: "string",
312
+ description: "Output aspect ratio ('1:1', '16:9', '9:16'). Default is '1:1'."
313
+ },
314
+ resolution: {
315
+ type: "string",
316
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
317
+ },
318
+ model: {
319
+ type: "string",
320
+ description: "The model tier ('flash', 'pro', 'standard'). Default is 'flash'."
321
+ },
322
+ referenceImage: {
323
+ type: "string",
324
+ description: "Optional style reference image."
325
+ },
326
+ logoImage: {
327
+ type: "string",
328
+ description: "Optional brand logo overlay."
329
+ },
330
+ overlayText: {
331
+ type: "string",
332
+ description: "Optional text overlay."
333
+ }
334
+ },
335
+ required: ["productImages"]
336
+ }
337
+ };
338
+ // 6. Festival Post Generator
339
+ const GENERATE_FESTIVAL_POST_TOOL = {
340
+ name: "generate_festival_post",
341
+ 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. Default model is Flash. Returns generated image directly (synchronous).",
342
+ inputSchema: {
343
+ type: "object",
344
+ properties: {
345
+ festivalPrompt: {
346
+ type: "string",
347
+ description: "Festival theme or campaign message (e.g., 'Diwali luxury jewelry promotion with warm golden diyas and floral rangoli')."
348
+ },
349
+ productImage: {
350
+ type: "string",
351
+ description: "Optional hero product image (URL, data URI, or local file path) to place centrally in the festive composition."
352
+ },
353
+ logoImage: {
354
+ type: "string",
355
+ description: "Optional brand logo image."
356
+ },
357
+ overlayText: {
358
+ type: "string",
359
+ description: "Main headline or festive greeting text (e.g., 'Happy Diwali - 20% Off')."
360
+ },
361
+ marketingText: {
362
+ type: "string",
363
+ description: "Supporting promotional message or contact details."
364
+ },
365
+ model: {
366
+ type: "string",
367
+ description: "The model tier ('flash', 'pro', 'standard'). Default is 'flash' (Gemini 3.1 Flash)."
109
368
  },
110
369
  aspectRatio: {
111
370
  type: "string",
112
- description: "Output aspect ratio. Default is '1:1'.",
371
+ description: "Output aspect ratio ('1:1', '9:16', '16:9'). Default is '1:1'."
372
+ },
373
+ resolution: {
374
+ type: "string",
375
+ description: "Output resolution ('1K', '2K'). Default is '1K'."
376
+ }
377
+ },
378
+ required: ["festivalPrompt"]
379
+ }
380
+ };
381
+ // 7. Human Builder / Virtual Model Generator
382
+ const BUILD_HUMAN_TOOL = {
383
+ name: "build_human",
384
+ description: "Creates photorealistic AI human models tailored for brand photography, fashion, ecommerce, and marketing campaigns with complete demographic, facial, hair, and body customization, plus multi-view character sheets (2x2 grid). Default model tier is Flash. Returns generated image directly (synchronous).",
385
+ inputSchema: {
386
+ type: "object",
387
+ properties: {
388
+ name: { type: "string", description: "Optional name or character identifier for the model." },
389
+ age: { type: "string", description: "Age category or specific age (e.g., 'Young Adult', '25 years old', 'Middle-aged')." },
390
+ gender: { type: "string", description: "Gender identity (e.g., 'Female', 'Male', 'Non-binary')." },
391
+ culturalTraits: { type: "string", description: "Ethnicity or cultural heritage (e.g., 'South Asian', 'East Asian', 'Caucasian', 'African')." },
392
+ country: { type: "string", description: "Country of origin (e.g., 'India', 'Japan', 'France', 'Brazil')." },
393
+ skinTone: { type: "string", description: "Complexion and skin tone (e.g., 'Medium Warm', 'Fair Porcelain', 'Rich Deep Bronze')." },
394
+ eyeColor: { type: "string", description: "Eye color (e.g., 'Dark Brown', 'Hazel Green', 'Deep Blue')." },
395
+ eyeShape: { type: "string", description: "Eye shape (e.g., 'Almond', 'Round', 'Monolid', 'Hooded')." },
396
+ noseShape: { type: "string", description: "Nose shape (e.g., 'Natural Straight', 'Aquiline', 'Button', 'Broad')." },
397
+ faceShape: { type: "string", description: "Face shape (e.g., 'Oval', 'Square', 'Heart', 'Round')." },
398
+ hairStyle: { type: "string", description: "Hairstyle (e.g., 'Long Waves', 'Buzz Cut', 'Sleek Bob', 'Afro', 'Layered Cut')." },
399
+ hairColor: { type: "string", description: "Hair color (e.g., 'Natural Black', 'Chestnut Brown', 'Honey Blonde')." },
400
+ hairThickness: { type: "string", description: "Hair density (e.g., 'Thick', 'Medium', 'Fine')." },
401
+ hairType: { type: "string", description: "Hair texture (e.g., 'Straight', 'Wavy', 'Curly', 'Coily')." },
402
+ bodySize: { type: "string", description: "Body build (e.g., 'Petite', 'Slim Athletic', 'Medium', 'Curvy', 'Plus Size')." },
403
+ bodyHeight: { type: "string", description: "Height description (e.g., 'Petite', 'Average', 'Tall Fashion Model')." },
404
+ bodyShape: { type: "string", description: "Body shape (e.g., 'Hourglass', 'Athletic Rectangular', 'Pear', 'Inverted Triangle')." },
405
+ muscleDefinition: { type: "string", description: "Muscle definition (e.g., 'Soft Natural', 'Toned Athletic', 'Defined Muscular')." },
406
+ cupSize: { type: "string", description: "Bust proportion if female (e.g., 'Small', 'Medium', 'Full')." },
407
+ extraDescription: { type: "string", description: "Extra unique physical traits (e.g., 'light freckles across nose bridge, subtle smile')." },
408
+ customTweakPrompt: { type: "string", description: "Custom refinement or scene styling prompt (e.g., 'wearing an ivory cashmere turtleneck against a warm minimalist studio backdrop')." },
409
+ generateMultiView: { type: "boolean", description: "Generate a 4-panel consistent character sheet in a 2x2 grid (Front, Side, 3/4, Close-up detail). Default is false." },
410
+ referenceImages: {
411
+ type: "array",
412
+ items: { type: "string" },
413
+ description: "Optional array of reference face or body images (URLs, data URIs, or local file paths) to guide identity."
414
+ },
415
+ aspectRatio: { type: "string", description: "Output aspect ratio ('1:1', '9:16', '4:5', '16:9'). Default is '1:1'." },
416
+ resolution: { type: "string", description: "Output resolution ('1K', '2K', '4K'). Default is '1K'." },
417
+ model: { type: "string", description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash'." }
418
+ }
419
+ }
420
+ };
421
+ // 8. YouTube Thumbnail Generator
422
+ const GENERATE_YOUTUBE_THUMBNAIL_TOOL = {
423
+ name: "generate_youtube_thumbnail",
424
+ 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).",
425
+ inputSchema: {
426
+ type: "object",
427
+ properties: {
428
+ prompt: {
429
+ type: "string",
430
+ description: "Core video concept or thumbnail description (e.g., 'Shocked tech reviewer holding futuristic glowing AI smartphone, neon studio background')."
431
+ },
432
+ overlayText: {
433
+ type: "string",
434
+ description: "Short, punchy thumbnail headline text (e.g., 'DON\\'T BUY THIS!')."
435
+ },
436
+ personImage: {
437
+ type: "string",
438
+ description: "Optional photo of the YouTube creator/subject (URL, data URI, or local path) to feature prominently."
439
+ },
440
+ backgroundImage: {
441
+ type: "string",
442
+ description: "Optional custom background image."
443
+ },
444
+ logoImage: {
445
+ type: "string",
446
+ description: "Optional channel/brand logo to place in corner."
447
+ },
448
+ referenceImage: {
449
+ type: "string",
450
+ description: "Optional style or concept reference image (URL, data URI, or local file path)."
451
+ },
452
+ promptId: {
453
+ type: "string",
454
+ description: "Optional ID or title of a pre-defined layout style template from youtubeThumbnailPrompts."
455
+ },
456
+ model: {
457
+ type: "string",
458
+ description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash' (google/gemini-3.1-flash-image)."
113
459
  },
114
460
  modelId: {
115
461
  type: "string",
116
- description: "The image model ID. Default uses the platform default.",
462
+ description: "The image model ID."
463
+ },
464
+ aspectRatio: {
465
+ type: "string",
466
+ description: "Thumbnail aspect ratio. Default is '16:9'."
117
467
  },
118
468
  resolution: {
119
469
  type: "string",
120
- description: "Output resolution (e.g., '1K', '2K'). Default is '1K'.",
470
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
471
+ }
472
+ },
473
+ required: ["prompt"]
474
+ }
475
+ };
476
+ // 8. Passport Photo Creator
477
+ const GENERATE_PASSPORT_PHOTO_TOOL = {
478
+ name: "generate_passport_photo",
479
+ 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. Default model tier is Flash. Default aspect ratio is '1:1'. Returns generated image directly (synchronous).",
480
+ inputSchema: {
481
+ type: "object",
482
+ properties: {
483
+ photoDataUri: {
484
+ type: "string",
485
+ description: "Source portrait photo (URL, data URI, or local file path) to convert."
486
+ },
487
+ prompt: {
488
+ type: "string",
489
+ description: "Adjustments (e.g., 'white background, formal dark navy business suit, centered frontal view')."
121
490
  },
122
491
  promptId: {
123
492
  type: "string",
124
- description: "Optional ID or value of a pre-defined style template."
493
+ description: "Optional ID or title of a pre-defined style template from passportPhotoPrompts."
125
494
  },
126
495
  referenceImage: {
127
496
  type: "string",
128
- description: "An optional general style reference image URL or data URI."
497
+ description: "Optional style reference image (URL, data URI, or local file path) to guide attire or lighting."
498
+ },
499
+ model: {
500
+ type: "string",
501
+ description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash' (google/gemini-3.1-flash-image)."
502
+ },
503
+ modelId: {
504
+ type: "string",
505
+ description: "The image model ID."
506
+ },
507
+ aspectRatio: {
508
+ type: "string",
509
+ description: "Passport aspect ratio ('1:1', '4:5', '3:4'). Default is '1:1'."
510
+ },
511
+ resolution: {
512
+ type: "string",
513
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
129
514
  }
130
515
  },
131
- required: ["companyName", "stylePrompt"]
516
+ required: ["photoDataUri"]
517
+ }
518
+ };
519
+ // 10. Sketch to Real
520
+ const SKETCH_TO_REAL_TOOL = {
521
+ name: "sketch_to_real",
522
+ description: "Transforms hand-drawn sketches, line drawings, or product drafts into photorealistic commercial images while strictly preserving the sketched silhouette, proportions, and outlines. Default model tier is Flash. Returns generated image directly (synchronous).",
523
+ inputSchema: {
524
+ type: "object",
525
+ properties: {
526
+ sketchImage: {
527
+ type: "string",
528
+ description: "The source sketch/drawing image (URL, data URI, or local file path)."
529
+ },
530
+ prompt: {
531
+ type: "string",
532
+ description: "Description of materials, textures, colors, and lighting (e.g., 'matte black carbon fiber body, polished titanium accents, studio lighting')."
533
+ },
534
+ style: {
535
+ type: "string",
536
+ description: "Target render style ('Photorealistic', 'Cinematic', '3D Render'). Default is 'Photorealistic'."
537
+ },
538
+ model: {
539
+ type: "string",
540
+ description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash'."
541
+ },
542
+ referenceImage: {
543
+ type: "string",
544
+ description: "Optional style/concept reference image (URL, data URI, or local file path)."
545
+ },
546
+ logoImage: {
547
+ type: "string",
548
+ description: "Optional brand logo overlay (URL, data URI, or local file path)."
549
+ },
550
+ overlayText: {
551
+ type: "string",
552
+ description: "Optional headline text overlay."
553
+ },
554
+ marketingText: {
555
+ type: "string",
556
+ description: "Optional marketing copy overlay."
557
+ },
558
+ aspectRatio: {
559
+ type: "string",
560
+ description: "Output aspect ratio ('1:1', '16:9', '9:16', '4:5'). Default is '1:1'."
561
+ },
562
+ resolution: {
563
+ type: "string",
564
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
565
+ }
566
+ },
567
+ required: ["sketchImage"]
568
+ }
569
+ };
570
+ // 11. Professional Photography Editor
571
+ const EDIT_PROFESSIONAL_PHOTO_TOOL = {
572
+ name: "edit_professional_photo",
573
+ description: "Transforms, grades, and enhances portraits and lifestyle photos into magazine-grade studio or commercial editorial photography. Enforces natural dermal micro-pores, authentic iris catchlights, directional studio lighting, and realistic depth of field. Default model tier is Flash. Returns generated image directly (synchronous).",
574
+ inputSchema: {
575
+ type: "object",
576
+ properties: {
577
+ images: {
578
+ type: "array",
579
+ items: IMAGE_SOURCE_SCHEMA,
580
+ description: "Array of subject reference photos (URL, data URI, or local file path). Each item can have 'url', 'dataUrl', or local 'path'. At least one required."
581
+ },
582
+ prompt: {
583
+ type: "string",
584
+ description: "Enhancement instruction or desired photo transformation (e.g., 'warm dusk cinematic lighting, shallow depth of field, natural skin texture, magazine editorial portrait')."
585
+ },
586
+ image: {
587
+ type: "string",
588
+ description: "Convenience single subject photo (URL, data URI, or local file path) if only providing one image."
589
+ },
590
+ referenceImage: {
591
+ type: "string",
592
+ description: "Optional style or concept reference image (URL, data URI, or local file path)."
593
+ },
594
+ promptId: {
595
+ type: "string",
596
+ description: "Optional ID or title of a pre-defined photography scene template."
597
+ },
598
+ model: {
599
+ type: "string",
600
+ description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash' (google/gemini-3.1-flash-image)."
601
+ },
602
+ modelId: {
603
+ type: "string",
604
+ description: "The image model ID."
605
+ },
606
+ resolution: {
607
+ type: "string",
608
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
609
+ },
610
+ aspectRatio: {
611
+ type: "string",
612
+ description: "Output aspect ratio ('1:1', '4:5', '9:16', '16:9'). Default is '1:1'."
613
+ }
614
+ },
615
+ required: ["prompt"]
616
+ }
617
+ };
618
+ // 12. Nano Banana Image Editor (AI)
619
+ const EDIT_NANO_BANANA_TOOL = {
620
+ name: "edit_nano_banana",
621
+ description: "Creative multi-reference AI image editor and compositor. Generates, modifies, or restyles images from multiple reference photos and text instructions with multi-subject composition, perspective alignment, ground contact shadows, and versatile artistic styles (photorealistic, action figurine, chibi/Funko Pop, anime/Ghibli, 3D render). Default model tier is Flash. Returns generated image directly (synchronous).",
622
+ inputSchema: {
623
+ type: "object",
624
+ properties: {
625
+ images: {
626
+ type: "array",
627
+ items: IMAGE_SOURCE_SCHEMA,
628
+ description: "Array of reference images (URL, data URI, or local file path). Each item can have 'url', 'dataUrl', or local 'path'. At least one required."
629
+ },
630
+ prompt: {
631
+ type: "string",
632
+ description: "The creative editing or generation prompt describing desired composition, style, or changes."
633
+ },
634
+ image: {
635
+ type: "string",
636
+ description: "Convenience single reference photo (URL, data URI, or local file path) if only providing one image."
637
+ },
638
+ referenceImage: {
639
+ type: "string",
640
+ description: "Optional style or concept reference image (URL, data URI, or local file path)."
641
+ },
642
+ promptId: {
643
+ type: "string",
644
+ description: "Optional ID or title of a pre-defined style template (e.g., 'action-figure', 'funko-pop', 'studio-ghibli', '16bit-game-character')."
645
+ },
646
+ model: {
647
+ type: "string",
648
+ description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash' (google/gemini-3.1-flash-image)."
649
+ },
650
+ modelId: {
651
+ type: "string",
652
+ description: "The image model ID."
653
+ },
654
+ resolution: {
655
+ type: "string",
656
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
657
+ },
658
+ aspectRatio: {
659
+ type: "string",
660
+ description: "Output aspect ratio ('1:1', '4:5', '9:16', '16:9'). Default is '1:1'."
661
+ }
662
+ },
663
+ required: ["prompt"]
664
+ }
665
+ };
666
+ // 13. Blog Image Generator (AI)
667
+ const GENERATE_BLOG_IMAGE_TOOL = {
668
+ name: "generate_blog_image",
669
+ description: "Generates high-impact, editorial blog hero images, article thumbnails, and banners with clean composition, visual hierarchy, intentional negative space, and exact text overlays. Default aspect ratio is 16:9. Default model tier is Flash. Returns generated image directly (synchronous).",
670
+ inputSchema: {
671
+ type: "object",
672
+ properties: {
673
+ prompt: {
674
+ type: "string",
675
+ description: "Description of the desired blog hero image, article concept, or editorial illustration."
676
+ },
677
+ overlayText: {
678
+ type: "string",
679
+ description: "Optional short text, title, or headline to overlay legibly on the image."
680
+ },
681
+ referenceImage: {
682
+ type: "string",
683
+ description: "Optional style or aesthetic reference image (URL, data URI, or local file path)."
684
+ },
685
+ promptId: {
686
+ type: "string",
687
+ description: "Optional ID or title of a pre-defined style template from blogImagePrompts (e.g., 'neon-cyber-circuit', 'balancing-stones')."
688
+ },
689
+ model: {
690
+ type: "string",
691
+ description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash' (google/gemini-3.1-flash-image)."
692
+ },
693
+ modelId: {
694
+ type: "string",
695
+ description: "The image model ID."
696
+ },
697
+ resolution: {
698
+ type: "string",
699
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
700
+ },
701
+ aspectRatio: {
702
+ type: "string",
703
+ description: "Output aspect ratio ('16:9', '1:1', '4:3', '3:4', '9:16', '4:5'). Default is '16:9'."
704
+ }
705
+ },
706
+ required: ["prompt"]
707
+ }
708
+ };
709
+ // 14. Baby Milestone Generator
710
+ const GENERATE_BABY_MILESTONE_TOOL = {
711
+ name: "generate_baby_milestone",
712
+ description: "Creates magical baby milestone keepsake portraits by isolating the baby and generating enchanting themed backgrounds with organic milestone text integration, while preserving the baby's exact face, expression, skin tone, clothing, and posture. Default model tier is Flash. Default aspect ratio is '1:1'. Returns generated image directly (synchronous).",
713
+ inputSchema: {
714
+ type: "object",
715
+ properties: {
716
+ photoDataUri: {
717
+ type: "string",
718
+ description: "The baby photo (URL, data URI, or local file path) to isolate and preserve as the main subject."
719
+ },
720
+ milestoneValue: {
721
+ type: "number",
722
+ description: "Milestone number (e.g., 1, 3, 6, 12)."
723
+ },
724
+ milestoneUnit: {
725
+ type: "string",
726
+ enum: ["month", "year"],
727
+ description: "Unit for milestone ('month' or 'year')."
728
+ },
729
+ customPrompt: {
730
+ type: "string",
731
+ description: "Creative theme or environment instructions (e.g., 'Enchanted woodland forest with soft morning sunbeams and wildflowers')."
732
+ },
733
+ promptId: {
734
+ type: "string",
735
+ description: "Optional ID or title of a pre-defined style preset from babyMilestonePrompts."
736
+ },
737
+ referenceImage: {
738
+ type: "string",
739
+ description: "Optional environmental style reference image (URL, data URI, or local file path)."
740
+ },
741
+ model: {
742
+ type: "string",
743
+ description: "Model tier ('flash', 'pro', 'standard'). Default is 'flash' (google/gemini-3.1-flash-image)."
744
+ },
745
+ modelId: {
746
+ type: "string",
747
+ description: "The image model ID."
748
+ },
749
+ resolution: {
750
+ type: "string",
751
+ description: "Output resolution ('1K', '2K', '4K'). Default is '1K'."
752
+ },
753
+ aspectRatio: {
754
+ type: "string",
755
+ description: "Output aspect ratio ('1:1', '4:5', '3:4', '16:9', '9:16'). Default is '1:1'."
756
+ }
757
+ },
758
+ required: ["photoDataUri"]
759
+ }
760
+ };
761
+ // 13. Upscale Image
762
+ const UPSCALE_IMAGE_TOOL = {
763
+ name: "upscale_image",
764
+ description: "Enhances a reference image with the Pro AI model at 4K. Preserves composition through instructions and validates larger dimensions and matching proportions. AI may alter fine details. Uses 60 credits; returns the upscaled image synchronously.",
765
+ inputSchema: {
766
+ type: "object",
767
+ properties: {
768
+ imageDataUri: {
769
+ type: "string",
770
+ description: "The image to upscale (URL, data URI, or local file path)."
771
+ }
772
+ },
773
+ required: ["imageDataUri"]
132
774
  }
133
775
  };
776
+ // 11. QR Code Generator (Full-featured)
134
777
  const GENERATE_QR_CODE_TOOL = {
135
778
  name: "generate_qr_code",
136
- description: "Generates a QR code for a given text, URL, WiFi, email, vCard, or UPI payment. Returns a base64 encoded image data URI. Supports customization of colors, size, error correction level, and optional logo overlay.",
779
+ 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
780
  inputSchema: {
138
781
  type: "object",
139
782
  properties: {
@@ -144,7 +787,7 @@ const GENERATE_QR_CODE_TOOL = {
144
787
  },
145
788
  value: {
146
789
  type: "string",
147
- description: "The text or URL to encode (required for 'url' and 'text' types)."
790
+ description: "The text, URL, or data payload to encode."
148
791
  },
149
792
  size: {
150
793
  type: "number",
@@ -167,9 +810,10 @@ const GENERATE_QR_CODE_TOOL = {
167
810
  required: ["type", "value"]
168
811
  }
169
812
  };
813
+ // 12. QR Code Simple
170
814
  const GENERATE_QR_CODE_SIMPLE_TOOL = {
171
815
  name: "generate_qr_code_simple",
172
- description: "Quick QR code generation from plain text or URL. Uses a simple GET-based endpoint. Returns a base64 encoded QR code image.",
816
+ description: "Quick single-parameter QR code generation from plain text or URL. Returns QR image directly (synchronous).",
173
817
  inputSchema: {
174
818
  type: "object",
175
819
  properties: {
@@ -181,60 +825,119 @@ const GENERATE_QR_CODE_SIMPLE_TOOL = {
181
825
  required: ["text"]
182
826
  }
183
827
  };
184
- const COMMON_VIDEO_PROPERTIES = {
185
- prompt: { type: "string", description: "Detailed video description or prompt." },
186
- mainImage: { type: "string", description: "Base64 data URI or URL for the main image (Image-to-Video mode)." },
187
- startImage: { type: "string", description: "Base64 data URI or URL for the first frame." },
188
- endImage: { type: "string", description: "Base64 data URI or URL for the last frame." },
189
- referenceImages: {
190
- type: "array",
191
- items: { type: "object", properties: { image: { type: "string" }, label: { type: "string" } } },
192
- description: "Array of reference images for style matching."
193
- },
194
- negativePrompt: { type: "string", description: "Elements to exclude from the video." },
195
- dialogue: { type: "string", description: "Optional voiceover or dialogue." },
196
- soundSuggestion: { type: "string", description: "Background music or sound effects." },
197
- cameraMotion: { type: "string", description: "Camera motion instructions." },
198
- ambientNoise: { type: "string", description: "Ambient sound description." },
199
- language: { type: "string", description: "Voiceover language." },
200
- timeDuration: { type: "string", description: "Video duration in seconds. Default is '4'." },
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." }
828
+ // 13. Get User Credits
829
+ const GET_USER_CREDITS_TOOL = {
830
+ name: "get_user_credits",
831
+ description: "Checks your current CodingMantra AI wallet credit balance. Useful before launching batches or large video/image tasks.",
832
+ inputSchema: {
833
+ type: "object",
834
+ properties: {}
835
+ }
836
+ };
837
+ // 14. List Image Models
838
+ const LIST_IMAGE_MODELS_TOOL = {
839
+ name: "list_image_models",
840
+ description: "Lists all available CodingMantra AI image generation models (e.g., Gemini Flash, Gemini Pro, Grok Imagine 2.0, GPT Image 2.5 Flare), including supported resolutions, aspect ratios, and credit costs.",
841
+ inputSchema: {
842
+ type: "object",
843
+ properties: {}
844
+ }
205
845
  };
846
+ // 15. Product Video Generation (Async)
206
847
  const GENERATE_PRODUCT_VIDEO_TOOL = {
207
848
  name: "generate_product_video",
208
- description: "Generates a product video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
209
- inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
849
+ description: "Generates commercial product videography. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion.",
850
+ inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES, required: ["prompt", "model", "resolution", "aspectRatio", "timeDuration"] }
210
851
  };
852
+ // 16. Apparel Video Generation (Async)
211
853
  const GENERATE_APPAREL_VIDEO_TOOL = {
212
854
  name: "generate_apparel_video",
213
- description: "Generates an apparel try-on video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
214
- inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
855
+ 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.",
856
+ inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES, required: ["prompt", "model", "resolution", "aspectRatio", "timeDuration"] }
215
857
  };
858
+ // 17. Festival Video Generation (Async)
216
859
  const GENERATE_FESTIVAL_VIDEO_TOOL = {
217
860
  name: "generate_festival_video",
218
- description: "Generates a festival or event video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
219
- inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
861
+ 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.",
862
+ inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES, required: ["prompt", "model", "resolution", "aspectRatio", "timeDuration"] }
220
863
  };
864
+ // 18. Jewelry Video Generation (Async)
221
865
  const GENERATE_JEWELRY_VIDEO_TOOL = {
222
866
  name: "generate_jewelry_video",
223
- description: "Generates a jewelry showcase video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
224
- inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
867
+ 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.",
868
+ inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES, required: ["prompt", "model", "resolution", "aspectRatio", "timeDuration"] }
225
869
  };
870
+ // 19. Logo Video Generation (Async)
226
871
  const GENERATE_LOGO_VIDEO_TOOL = {
227
872
  name: "generate_logo_video",
228
- description: "Generates a logo animation video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
229
- inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES }
873
+ 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.",
874
+ inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES, required: ["prompt", "model", "resolution", "aspectRatio", "timeDuration"] }
875
+ };
876
+ const GENERATE_STORY_VIDEO_TOOL = {
877
+ name: "generate_story_video",
878
+ description: "Generates cinematic story videos with character descriptions, reference images, sound effects, and atmosphere. Asynchronous: returns a jobId; poll check_video_job_status until completed, failed, or cancelled.",
879
+ inputSchema: { type: "object", properties: COMMON_VIDEO_PROPERTIES, required: ["prompt", "model", "resolution", "aspectRatio", "timeDuration"] }
880
+ };
881
+ const GENERATE_TALKING_AVATAR_TOOL = {
882
+ name: "generate_talking_avatar",
883
+ description: "Animates a portrait with HeyGen Avatar IV lip sync from a script and HeyGen voice ID. Asynchronous: returns a jobId; poll check_video_job_status until completed, failed, or cancelled. Credits are calculated by the server from the script budget and resolution.",
884
+ inputSchema: {
885
+ type: "object",
886
+ properties: {
887
+ image: { type: "string", description: "PNG, JPEG, or WebP portrait as a base64 data URI or local file path. Public URLs are not accepted." },
888
+ speechMode: { type: "string", enum: ["text"], default: "text", description: "Speech source; currently only text is supported." },
889
+ script: { type: "string", minLength: 3, maxLength: 2000, description: "Spoken script. Must fit the selected duration budget and speech speed." },
890
+ voiceId: { type: "string", minLength: 1, maxLength: 150, pattern: "^[A-Za-z0-9_-]+$", description: "HeyGen voice ID for the narration." },
891
+ speed: { type: "number", minimum: 0.5, maximum: 1.5, default: 1, description: "Speech speed multiplier." },
892
+ targetDuration: { type: "integer", minimum: 5, maximum: 120, default: 15, description: "Maximum script duration budget in seconds." },
893
+ performancePreset: { type: "string", enum: ["natural", "professional", "energetic", "friendly", "presenter", "storyteller"], default: "professional", description: "Avatar performance style." },
894
+ expressiveness: { type: "string", enum: ["low", "medium", "high"], description: "Optional expression intensity override." },
895
+ motionPrompt: { type: "string", maxLength: 500, description: "Optional motion instructions overriding the preset." },
896
+ backgroundMode: { type: "string", enum: ["original", "color"], default: "original", description: "Preserve the portrait background or use a solid color." },
897
+ backgroundColor: { type: "string", pattern: "^#[0-9a-fA-F]{6}$", default: "#111827", description: "Solid background color when backgroundMode is color." },
898
+ captions: { type: "boolean", default: false, description: "Whether to add captions." },
899
+ title: { type: "string", maxLength: 100, description: "Optional video title." },
900
+ resolution: { type: "string", enum: ["720p", "1080p"], default: "1080p", description: "Output resolution." },
901
+ aspectRatio: { type: "string", enum: ["9:16", "1:1", "16:9"], default: "9:16", description: "Output aspect ratio." }
902
+ },
903
+ required: ["image", "script", "voiceId"]
904
+ }
230
905
  };
906
+ const GENERATE_JEWELLERY_RATE_POSTER_TOOL = {
907
+ name: "generate_jewellery_rate_poster",
908
+ description: "Generates a branded gold and silver rate poster. Synchronous: returns the generated image for inline display. Rates are supplied by the caller, not fetched from a market feed.",
909
+ inputSchema: {
910
+ type: "object",
911
+ properties: {
912
+ scenePrompt: { type: "string", description: "Poster scene, background, and visual style." },
913
+ productImage: { type: "string", description: "Optional jewelry image URL, data URI, or local file path." },
914
+ logoImage: { type: "string", description: "Optional brand logo URL, data URI, or local file path." },
915
+ goldRate24k: { type: "string", description: "24K gold rate text, including currency and units." },
916
+ goldRate22k: { type: "string", description: "22K gold rate text, including currency and units." },
917
+ goldRate18k: { type: "string", description: "18K gold rate text, including currency and units." },
918
+ goldRate14k: { type: "string", description: "14K gold rate text, including currency and units." },
919
+ silverRate: { type: "string", description: "Silver rate text, including currency and units." },
920
+ date: { type: "string", description: "Date to display on the poster." },
921
+ brandName: { type: "string", description: "Jewelry business name." },
922
+ tagLine: { type: "string", description: "Optional brand tagline." },
923
+ footerText: { type: "string", description: "Optional footer or disclaimer." },
924
+ address: { type: "string", description: "Business address." },
925
+ phoneNumber: { type: "string", description: "Business contact number." },
926
+ model: { type: "string", description: "Image model slug; use list_image_models to discover models." },
927
+ resolution: { type: "string", description: "Resolution supported by the selected model (1K, 2K, or 4K)." },
928
+ aspectRatio: { type: "string", default: "1:1", description: "Aspect ratio supported by the selected model." }
929
+ },
930
+ required: ["scenePrompt"]
931
+ }
932
+ };
933
+ // 20. Check Video Job Status (Polling)
231
934
  const CHECK_JOB_STATUS_TOOL = {
232
935
  name: "check_video_job_status",
233
- description: "Checks the status of an asynchronous video generation job. Call this repeatedly (e.g. every 10-30 seconds) until the job status is 'completed' or 'failed'.",
936
+ 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', 'failed', or 'cancelled'.",
234
937
  inputSchema: {
235
938
  type: "object",
236
939
  properties: {
237
- jobId: { type: "string", description: "The ID of the job to check." }
940
+ jobId: { type: "string", description: "The ID of the video job to track." }
238
941
  },
239
942
  required: ["jobId"]
240
943
  }
@@ -243,15 +946,33 @@ const CHECK_JOB_STATUS_TOOL = {
243
946
  server.setRequestHandler(ListToolsRequestSchema, async () => {
244
947
  return {
245
948
  tools: [
949
+ GENERATE_JEWELLERY_RATE_POSTER_TOOL,
246
950
  GENERATE_PRODUCT_PHOTO_TOOL,
247
951
  GENERATE_LOGO_TOOL,
952
+ TRY_ON_JEWELRY_TOOL,
953
+ TRY_ON_APPAREL_TOOL,
954
+ TRY_ON_PRODUCT_TOOL,
955
+ GENERATE_FESTIVAL_POST_TOOL,
956
+ BUILD_HUMAN_TOOL,
957
+ GENERATE_YOUTUBE_THUMBNAIL_TOOL,
958
+ GENERATE_PASSPORT_PHOTO_TOOL,
959
+ SKETCH_TO_REAL_TOOL,
960
+ EDIT_PROFESSIONAL_PHOTO_TOOL,
961
+ EDIT_NANO_BANANA_TOOL,
962
+ GENERATE_BLOG_IMAGE_TOOL,
963
+ GENERATE_BABY_MILESTONE_TOOL,
964
+ UPSCALE_IMAGE_TOOL,
248
965
  GENERATE_QR_CODE_TOOL,
249
966
  GENERATE_QR_CODE_SIMPLE_TOOL,
967
+ GET_USER_CREDITS_TOOL,
968
+ LIST_IMAGE_MODELS_TOOL,
250
969
  GENERATE_PRODUCT_VIDEO_TOOL,
251
970
  GENERATE_APPAREL_VIDEO_TOOL,
252
971
  GENERATE_FESTIVAL_VIDEO_TOOL,
253
972
  GENERATE_JEWELRY_VIDEO_TOOL,
254
973
  GENERATE_LOGO_VIDEO_TOOL,
974
+ GENERATE_STORY_VIDEO_TOOL,
975
+ GENERATE_TALKING_AVATAR_TOOL,
255
976
  CHECK_JOB_STATUS_TOOL,
256
977
  ],
257
978
  };
@@ -259,110 +980,330 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
259
980
  // ─── Tool Execution ─────────────────────────────────────────────────────────────
260
981
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
261
982
  try {
262
- const { name, arguments: args } = request.params;
263
- // ── Product Photography (synchronous) ────────────────────────────────────
983
+ const { name, arguments: rawArgs } = request.params;
984
+ const args = rawArgs || {};
985
+ if (name === 'generate_jewellery_rate_poster') {
986
+ const response = await apiClient('/image/jewellery-rate-poster', {
987
+ method: 'POST',
988
+ body: JSON.stringify({ ...args, productImage: resolveImageInput(args.productImage),
989
+ logoImage: resolveImageInput(args.logoImage), outputResult: 'base64' }),
990
+ });
991
+ return formatImageToolResult(response, 'Jewellery Rate Poster Generated');
992
+ }
993
+ // ── 1. Product Photography (synchronous) ──────────────────────────────────
264
994
  if (name === 'generate_product_photo') {
995
+ const payload = {
996
+ ...args,
997
+ productImages: resolveImagesArray(args.productImages),
998
+ referenceImage: resolveImageInput(args.referenceImage),
999
+ logoImage: resolveImageInput(args.logoImage),
1000
+ };
265
1001
  const response = await apiClient('/image/product-photography', {
266
1002
  method: 'POST',
267
- body: JSON.stringify(args),
1003
+ body: JSON.stringify(payload),
268
1004
  });
269
- const result = response.data;
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 }] };
1005
+ return formatImageToolResult(response, 'Product Photo Generated');
284
1006
  }
285
- // ── Logo Creator (synchronous) ───────────────────────────────────────────
1007
+ // ── 2. Logo Creator (synchronous) ─────────────────────────────────────────
286
1008
  if (name === 'generate_logo') {
1009
+ const payload = {
1010
+ ...args,
1011
+ referenceImage: resolveImageInput(args.referenceImage),
1012
+ };
287
1013
  const response = await apiClient('/image/logo-creator', {
288
1014
  method: 'POST',
289
- body: JSON.stringify(args),
1015
+ body: JSON.stringify(payload),
290
1016
  });
291
- const result = response.data;
292
- let text = '✅ Logo generated successfully!\n\n';
293
- if (result?.imageUrl) {
294
- text += `Image URL: ${result.imageUrl}`;
295
- }
296
- else if (result?.photoDataUri) {
297
- text += `Image data returned (base64). Length: ${String(result.photoDataUri).length} characters.\nData URI: ${String(result.photoDataUri).substring(0, 100)}...`;
298
- }
299
- else if (result?.output) {
300
- text += `Image data returned (base64). Length: ${String(result.output).length} characters.`;
1017
+ return formatImageToolResult(response, 'Brand Logo Generated');
1018
+ }
1019
+ // ── 3. Jewelry Try-On (synchronous) ───────────────────────────────────────
1020
+ if (name === 'try_on_jewelry') {
1021
+ const payload = {
1022
+ ...args,
1023
+ jewelryImages: resolveImagesArray(args.jewelryImages),
1024
+ personImage: resolveImageInput(args.personImage),
1025
+ referenceImage: resolveImageInput(args.referenceImage),
1026
+ logoImage: resolveImageInput(args.logoImage),
1027
+ };
1028
+ const response = await apiClient('/image/jewelry-try-on', {
1029
+ method: 'POST',
1030
+ body: JSON.stringify(payload),
1031
+ });
1032
+ return formatImageToolResult(response, 'Jewelry Try-On Image Generated');
1033
+ }
1034
+ // ── 4. Apparel Try-On (synchronous) ───────────────────────────────────────
1035
+ if (name === 'try_on_apparel') {
1036
+ const payload = {
1037
+ ...args,
1038
+ apparelImages: resolveImagesArray(args.apparelImages),
1039
+ modelImage: resolveImageInput(args.modelImage),
1040
+ referenceImage: resolveImageInput(args.referenceImage),
1041
+ logoImage: resolveImageInput(args.logoImage),
1042
+ };
1043
+ const response = await apiClient('/image/apparel-try-on', {
1044
+ method: 'POST',
1045
+ body: JSON.stringify(payload),
1046
+ });
1047
+ return formatImageToolResult(response, 'Apparel Try-On Image Generated');
1048
+ }
1049
+ // ── 5. Product Try-On (synchronous) ───────────────────────────────────────
1050
+ if (name === 'try_on_product') {
1051
+ const payload = {
1052
+ ...args,
1053
+ productImages: resolveImagesArray(args.productImages),
1054
+ mainImage: resolveImageInput(args.mainImage),
1055
+ referenceImage: resolveImageInput(args.referenceImage),
1056
+ logoImage: resolveImageInput(args.logoImage),
1057
+ };
1058
+ const response = await apiClient('/image/product-try-on', {
1059
+ method: 'POST',
1060
+ body: JSON.stringify(payload),
1061
+ });
1062
+ return formatImageToolResult(response, 'Product Try-On Image Generated');
1063
+ }
1064
+ // ── 6. Festival Post (synchronous) ────────────────────────────────────────
1065
+ if (name === 'generate_festival_post') {
1066
+ const payload = {
1067
+ ...args,
1068
+ productImage: resolveImageInput(args.productImage),
1069
+ logoImage: resolveImageInput(args.logoImage),
1070
+ };
1071
+ const response = await apiClient('/image/festival-post-generator', {
1072
+ method: 'POST',
1073
+ body: JSON.stringify(payload),
1074
+ });
1075
+ return formatImageToolResult(response, 'Festival Post Generated');
1076
+ }
1077
+ // ── 7. Human Builder (synchronous) ────────────────────────────────────────
1078
+ if (name === 'build_human') {
1079
+ const payload = {
1080
+ ...args,
1081
+ referenceImages: Array.isArray(args.referenceImages)
1082
+ ? args.referenceImages.map((img) => typeof img === 'string'
1083
+ ? resolveImageInput(img)
1084
+ : resolveImageInput(img?.dataUrl || img?.path || img?.url))
1085
+ : undefined,
1086
+ };
1087
+ const response = await apiClient('/image/human-builder', {
1088
+ method: 'POST',
1089
+ body: JSON.stringify(payload),
1090
+ });
1091
+ return formatImageToolResult(response, 'AI Human Model Generated');
1092
+ }
1093
+ // ── 8. YouTube Thumbnail (synchronous) ────────────────────────────────────
1094
+ if (name === 'generate_youtube_thumbnail') {
1095
+ const payload = {
1096
+ ...args,
1097
+ personImage: resolveImageInput(args.personImage),
1098
+ backgroundImage: resolveImageInput(args.backgroundImage),
1099
+ logoImage: resolveImageInput(args.logoImage),
1100
+ referenceImage: resolveImageInput(args.referenceImage),
1101
+ };
1102
+ const response = await apiClient('/image/youtube-thumbnail-generator', {
1103
+ method: 'POST',
1104
+ body: JSON.stringify(payload),
1105
+ });
1106
+ return formatImageToolResult(response, 'YouTube Thumbnail Generated');
1107
+ }
1108
+ // ── 8. Passport Photo (synchronous) ───────────────────────────────────────
1109
+ if (name === 'generate_passport_photo') {
1110
+ const payload = {
1111
+ ...args,
1112
+ photoDataUri: resolveImageInput(args.photoDataUri),
1113
+ referenceImage: resolveImageInput(args.referenceImage),
1114
+ };
1115
+ const response = await apiClient('/image/passport-photo', {
1116
+ method: 'POST',
1117
+ body: JSON.stringify(payload),
1118
+ });
1119
+ return formatImageToolResult(response, 'Passport Photo Generated');
1120
+ }
1121
+ // ── 9. Sketch to Real (synchronous) ───────────────────────────────────────
1122
+ if (name === 'sketch_to_real') {
1123
+ const payload = {
1124
+ ...args,
1125
+ sketchImage: resolveImageInput(args.sketchImage),
1126
+ referenceImage: resolveImageInput(args.referenceImage),
1127
+ logoImage: resolveImageInput(args.logoImage),
1128
+ };
1129
+ const response = await apiClient('/image/sketch-to-real', {
1130
+ method: 'POST',
1131
+ body: JSON.stringify(payload),
1132
+ });
1133
+ return formatImageToolResult(response, 'Sketch-to-Real Image Generated');
1134
+ }
1135
+ // ── 10. Professional Photography Editor (synchronous) ─────────────────────
1136
+ if (name === 'edit_professional_photo') {
1137
+ let resolvedImages = args.images ? resolveImagesArray(args.images) : [];
1138
+ if (resolvedImages.length === 0 && args.image) {
1139
+ const single = resolveImageInput(args.image);
1140
+ if (single) {
1141
+ resolvedImages = [{ dataUrl: single }];
1142
+ }
301
1143
  }
302
- else {
303
- text += `Result: ${JSON.stringify(result).substring(0, 500)}`;
1144
+ const payload = {
1145
+ ...args,
1146
+ images: resolvedImages,
1147
+ referenceImage: resolveImageInput(args.referenceImage),
1148
+ };
1149
+ const response = await apiClient('/image/professional-photography-editor', {
1150
+ method: 'POST',
1151
+ body: JSON.stringify(payload),
1152
+ });
1153
+ return formatImageToolResult(response, 'Professional Photo Generated');
1154
+ }
1155
+ // ── 11. Nano Banana Image Editor (synchronous) ────────────────────────────
1156
+ if (name === 'edit_nano_banana') {
1157
+ let resolvedImages = args.images ? resolveImagesArray(args.images) : [];
1158
+ if (resolvedImages.length === 0 && args.image) {
1159
+ const single = resolveImageInput(args.image);
1160
+ if (single) {
1161
+ resolvedImages = [{ dataUrl: single }];
1162
+ }
304
1163
  }
305
- return { content: [{ type: "text", text }] };
1164
+ const payload = {
1165
+ ...args,
1166
+ images: resolvedImages,
1167
+ referenceImage: resolveImageInput(args.referenceImage),
1168
+ };
1169
+ const response = await apiClient('/image/nano-banana-editor', {
1170
+ method: 'POST',
1171
+ body: JSON.stringify(payload),
1172
+ });
1173
+ return formatImageToolResult(response, 'Nano Banana Image Generated');
306
1174
  }
307
- // ── QR Code (POST, full-featured) ────────────────────────────────────────
1175
+ // ── 12. Blog Image Generator (synchronous) ────────────────────────────────
1176
+ if (name === 'generate_blog_image') {
1177
+ const payload = {
1178
+ ...args,
1179
+ referenceImage: resolveImageInput(args.referenceImage),
1180
+ };
1181
+ const response = await apiClient('/image/blog-image-generator', {
1182
+ method: 'POST',
1183
+ body: JSON.stringify(payload),
1184
+ });
1185
+ return formatImageToolResult(response, 'Blog Image Generated');
1186
+ }
1187
+ // ── 13. Baby Milestone Generator (synchronous) ───────────────────────────
1188
+ if (name === 'generate_baby_milestone') {
1189
+ const payload = {
1190
+ ...args,
1191
+ photoDataUri: resolveImageInput(args.photoDataUri),
1192
+ referenceImage: resolveImageInput(args.referenceImage),
1193
+ };
1194
+ const response = await apiClient('/image/baby-milestone-generator', {
1195
+ method: 'POST',
1196
+ body: JSON.stringify(payload),
1197
+ });
1198
+ return formatImageToolResult(response, 'Baby Milestone Photo Generated');
1199
+ }
1200
+ // ── 12. Upscale Image (synchronous) ───────────────────────────────────────
1201
+ if (name === 'upscale_image') {
1202
+ const payload = {
1203
+ ...args,
1204
+ imageDataUri: resolveImageInput(args.imageDataUri),
1205
+ };
1206
+ const response = await apiClient('/image/upscale-image', {
1207
+ method: 'POST',
1208
+ body: JSON.stringify(payload),
1209
+ signal: AbortSignal.timeout(12 * 60 * 1000),
1210
+ });
1211
+ return formatImageToolResult(response, 'Image Upscaled Successfully');
1212
+ }
1213
+ // ── 11. QR Code (POST, full-featured) ─────────────────────────────────────
308
1214
  if (name === 'generate_qr_code') {
309
1215
  const response = await apiClient('/qr-code/generate', {
310
1216
  method: 'POST',
311
1217
  body: JSON.stringify(args),
312
1218
  });
313
- const qrDataUrl = response.data?.qrCodeDataUrl;
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
- };
1219
+ return formatImageToolResult(response, 'QR Code Generated');
322
1220
  }
323
- // ── QR Code Simple (GET, path-based — legacy/backward compatible) ────────
1221
+ // ── 12. QR Code Simple (GET, legacy) ──────────────────────────────────────
324
1222
  if (name === 'generate_qr_code_simple') {
325
1223
  const { text } = args;
326
1224
  const encodedText = encodeURIComponent(text);
327
- const apiKey = process.env.CODINGMANTRA_API_KEY;
1225
+ const apiKey = process.env.CODINGMANTRA_API_KEY || '';
328
1226
  const response = await apiClient(`/qr-code/generate-base64/${apiKey}/${encodedText}`, {
329
1227
  method: 'GET',
330
1228
  });
331
- const qrDataUrl = response.data?.qrCodeDataUrl;
332
- if (qrDataUrl) {
333
- return {
334
- content: [{ type: "text", text: `✅ QR Code generated successfully!\n\nData URI: ${qrDataUrl}` }],
335
- };
1229
+ return formatImageToolResult(response, 'QR Code Generated');
1230
+ }
1231
+ // ── 13. Get User Credits ──────────────────────────────────────────────────
1232
+ if (name === 'get_user_credits') {
1233
+ const response = await apiClient('/user/credits', {
1234
+ method: 'GET',
1235
+ });
1236
+ const credits = response.data?.credits ?? 0;
1237
+ return {
1238
+ content: [{
1239
+ type: "text",
1240
+ 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)`
1241
+ }]
1242
+ };
1243
+ }
1244
+ // ── 14. List Image Models ─────────────────────────────────────────────────
1245
+ if (name === 'list_image_models') {
1246
+ const response = await apiClient('/image/image-model-options', {
1247
+ method: 'GET',
1248
+ });
1249
+ const models = response.data || [];
1250
+ let text = `🤖 **Available CodingMantra Image Models**\n\n`;
1251
+ for (const m of models) {
1252
+ text += `#### ${m.name} (\`${m.model}\`)\n`;
1253
+ text += `- **Provider:** ${m.provider}\n`;
1254
+ text += `- **Description:** ${m.description || 'High-performance generative model'}\n`;
1255
+ text += `- **Supported Resolutions:** ${m.supportedResolutions?.join(', ') || '1K'}\n`;
1256
+ text += `- **Supported Aspect Ratios:** ${m.supportedAspectRatios?.join(', ') || '1:1'}\n`;
1257
+ text += `- **Credit Cost:** ${m.estimatedCost?.credits ?? 10} credits\n\n`;
336
1258
  }
337
1259
  return {
338
- content: [{ type: "text", text: `✅ QR Code generated. Result: ${JSON.stringify(response.data).substring(0, 500)}` }],
1260
+ content: [{ type: "text", text: text.trim() }]
339
1261
  };
340
1262
  }
341
- // ── Video Generation (Asynchronous) ──────────────────────────────────────
1263
+ // ── 15-19. Video Generation (Asynchronous) ────────────────────────────────
342
1264
  const videoEndpoints = {
343
1265
  generate_product_video: '/product-video-generation',
344
1266
  generate_apparel_video: '/apparel-video-generation',
345
1267
  generate_festival_video: '/festival-video-generation',
346
1268
  generate_jewelry_video: '/jewelry-video-generation',
347
- generate_logo_video: '/logo-video-generation'
1269
+ generate_logo_video: '/logo-video-generation',
1270
+ generate_story_video: '/story-teller-generation',
1271
+ generate_talking_avatar: '/ai/talking-avatar/generate'
348
1272
  };
349
1273
  if (videoEndpoints[name]) {
1274
+ const optionalImage = (input) => typeof input === 'string' && input.trim()
1275
+ ? resolveImageInput(input) : undefined;
1276
+ const references = Array.isArray(args.referenceImages)
1277
+ ? args.referenceImages.map((reference) => ({
1278
+ ...reference, image: resolveImageInput(reference.image)
1279
+ })) : undefined;
1280
+ const avatarImage = name === 'generate_talking_avatar' ? optionalImage(args.image) : undefined;
1281
+ if (name === 'generate_talking_avatar' && !/^data:image\/(png|jpeg|webp);base64,/.test(avatarImage || '')) {
1282
+ throw new Error('Talking Avatar requires a PNG, JPEG, or WebP portrait as a local file path or base64 data URI.');
1283
+ }
1284
+ const payload = name === 'generate_talking_avatar' ? { ...args, image: avatarImage } : {
1285
+ ...args,
1286
+ mainImage: optionalImage(args.mainImage),
1287
+ startImage: optionalImage(args.startImage),
1288
+ endImage: optionalImage(args.endImage),
1289
+ referenceImages: references,
1290
+ };
350
1291
  const response = await apiClient(videoEndpoints[name], {
351
1292
  method: 'POST',
352
- body: JSON.stringify(args),
1293
+ body: JSON.stringify(payload),
353
1294
  });
354
1295
  const result = response.data || response;
355
1296
  if (result?.jobId) {
356
1297
  return {
357
1298
  content: [{
358
1299
  type: "text",
359
- text: `✅ Job created successfully!\nJob ID: ${result.jobId}\nMessage: ${result.message}\n\nPlease use the check_video_job_status tool with this Job ID to poll for the result. Wait about 10-30 seconds between polls. Video generation can take several minutes.`
1300
+ 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
1301
  }]
361
1302
  };
362
1303
  }
363
1304
  return { content: [{ type: "text", text: `Job started. Result: ${JSON.stringify(result).substring(0, 500)}` }] };
364
1305
  }
365
- // ── Check Job Status ─────────────────────────────────────────────────────
1306
+ // ── 20. Check Video Job Status ────────────────────────────────────────────
366
1307
  if (name === 'check_video_job_status') {
367
1308
  const { jobId } = args;
368
1309
  const response = await apiClient(`/jobs/status?jobId=${encodeURIComponent(jobId)}`, {
@@ -371,15 +1312,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
371
1312
  const result = response.data || response;
372
1313
  if (result?.job) {
373
1314
  const job = result.job;
374
- let text = `Job Status: ${job.status}\nProgress: ${job.progress}%\nStage: ${job.stage || 'N/A'}`;
1315
+ let text = `### 🎬 Video Job Status: **${job.status.toUpperCase()}**\n\n`;
1316
+ text += `- **Progress:** ${job.progress}%\n`;
1317
+ text += `- **Stage:** ${job.stage || 'Processing'}\n`;
375
1318
  if (job.status === 'completed' && job.result?.videoUrl) {
376
- text += `\n\n✅ Video generated successfully!\nVideo URL: ${job.result.videoUrl}`;
1319
+ text += `\n✅ **Video Ready:** [Watch / Download Video](${job.result.videoUrl})\n`;
1320
+ text += `\n📁 **Gallery:** View in your [CodingMantra Gallery](https://codingmantra.com/my-gallery)`;
1321
+ }
1322
+ else if (job.status === 'cancelled') {
1323
+ text += `\nJob cancelled. No further polling is needed.`;
377
1324
  }
378
1325
  else if (job.status === 'failed') {
379
- text += `\n\n❌ Job failed: ${job.error || 'Unknown error'}`;
1326
+ text += `\n❌ **Job Failed:** ${job.error || 'Unknown error occurred during generation.'}\n`;
1327
+ text += `Your credits have been refunded automatically.`;
380
1328
  }
381
1329
  else {
382
- text += `\n\nJob is still running. Please check back again shortly.`;
1330
+ text += `\n⏳ **Job is actively rendering.** Please poll again in 15-20 seconds.`;
383
1331
  }
384
1332
  return { content: [{ type: "text", text }] };
385
1333
  }
@@ -389,7 +1337,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
389
1337
  }
390
1338
  catch (error) {
391
1339
  return {
392
- content: [{ type: "text", text: `❌ Error: ${error.message}` }],
1340
+ content: [{ type: "text", text: `❌ **Error:** ${error.message || error}` }],
393
1341
  isError: true,
394
1342
  };
395
1343
  }
@@ -398,9 +1346,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
398
1346
  async function run() {
399
1347
  const transport = new StdioServerTransport();
400
1348
  await server.connect(transport);
401
- console.error("CodingMantra MCP Server running on stdio");
1349
+ console.error("[CodingMantra MCP] Server v0.2.2 initialized and running on stdio (28 tools active)");
402
1350
  }
403
1351
  run().catch((error) => {
404
- console.error("Fatal error running server:", error);
1352
+ console.error("Fatal error running CodingMantra MCP server:", error);
405
1353
  process.exit(1);
406
1354
  });