@felores/kie-ai-mcp-server 1.1.0 → 1.1.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/README.md CHANGED
@@ -52,6 +52,7 @@ export KIE_AI_API_KEY="your-api-key-here"
52
52
  export KIE_AI_BASE_URL="https://api.kie.ai/api/v1" # Default
53
53
  export KIE_AI_TIMEOUT="60000" # Default: 60 seconds
54
54
  export KIE_AI_DB_PATH="./tasks.db" # Default: ./tasks.db
55
+ export KIE_AI_CALLBACK_URL="https://your-domain.com/api/callback" # Default callback URL for video generation
55
56
  ```
56
57
 
57
58
  ### MCP Configuration
@@ -146,10 +147,12 @@ Generate videos using Veo3.
146
147
  - `prompt` (string, required): Video description
147
148
  - `imageUrls` (array, optional): Image for image-to-video (max 1)
148
149
  - `model` (enum, optional): "veo3" or "veo3_fast" (default: "veo3")
149
- - `aspectRatio` (enum, optional): "16:9" or "9:16" (default: "16:9")
150
+ - `aspectRatio` (enum, optional): "16:9", "9:16", or "Auto" (default: "16:9", only 16:9 supports 1080P)
150
151
  - `seeds` (integer, optional): Random seed 10000-99999
151
152
  - `watermark` (string, optional): Watermark text
152
- - `enableFallback` (boolean, optional): Enable fallback mechanism
153
+ - `callBackUrl` (string, optional): Callback URL for completion notifications
154
+ - `enableFallback` (boolean, optional): Enable fallback mechanism (default: false, fallback videos cannot use 1080P endpoint)
155
+ - `enableTranslation` (boolean, optional): Auto-translate prompts to English (default: true)
153
156
 
154
157
  **Example:**
155
158
  ```json
@@ -157,7 +160,8 @@ Generate videos using Veo3.
157
160
  "prompt": "A dog playing in a park",
158
161
  "model": "veo3",
159
162
  "aspectRatio": "16:9",
160
- "seeds": 12345
163
+ "seeds": 12345,
164
+ "enableTranslation": true
161
165
  }
162
166
  ```
163
167
 
@@ -331,6 +335,15 @@ MIT License - see LICENSE file for details.
331
335
 
332
336
  ## Changelog
333
337
 
338
+ ### v1.1.1 (2025-01-14)
339
+
340
+ **Improvements:**
341
+ - Added `KIE_AI_CALLBACK_URL` environment variable for default callback URL
342
+ - Added `enableTranslation` parameter to Veo3 (auto-translate prompts to English)
343
+ - Added `Auto` option to Veo3 `aspectRatio`
344
+ - Exposed `callBackUrl` parameter in Veo3 tool schema
345
+ - Veo3 tool now fully aligned with official Kie.ai API documentation
346
+
334
347
  ### v1.1.0 (2025-01-14)
335
348
 
336
349
  **Breaking Changes:**
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ class KieAiMcpServer {
12
12
  constructor() {
13
13
  this.server = new Server({
14
14
  name: 'kie-ai-mcp-server',
15
- version: '1.1.0',
15
+ version: '1.1.2',
16
16
  });
17
17
  // Initialize client with config from environment
18
18
  const config = {
@@ -27,6 +27,40 @@ class KieAiMcpServer {
27
27
  this.db = new TaskDatabase(process.env.KIE_AI_DB_PATH);
28
28
  this.setupHandlers();
29
29
  }
30
+ formatError(toolName, error, paramDescriptions) {
31
+ let errorMessage = 'Unknown error';
32
+ let errorDetails = '';
33
+ if (error instanceof Error) {
34
+ errorMessage = error.message;
35
+ // Check for Zod validation errors
36
+ if (errorMessage.includes('ZodError')) {
37
+ const lines = errorMessage.split('\n');
38
+ const validationErrors = lines.filter(line => line.includes('Expected') || line.includes('Required') || line.includes('Invalid'));
39
+ if (validationErrors.length > 0) {
40
+ errorDetails = `Validation errors:\n${validationErrors.map(err => `- ${err.trim()}`).join('\n')}`;
41
+ }
42
+ }
43
+ }
44
+ // Build parameter guidance
45
+ const paramGuidance = Object.entries(paramDescriptions)
46
+ .map(([param, desc]) => `- ${param}: ${desc}`)
47
+ .join('\n');
48
+ return {
49
+ content: [
50
+ {
51
+ type: 'text',
52
+ text: JSON.stringify({
53
+ success: false,
54
+ tool: toolName,
55
+ error: errorMessage,
56
+ details: errorDetails,
57
+ parameter_guidance: paramGuidance,
58
+ message: `Failed to execute ${toolName}. Check parameters and try again.`
59
+ }, null, 2)
60
+ }
61
+ ]
62
+ };
63
+ }
30
64
  setupHandlers() {
31
65
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
32
66
  return {
@@ -152,8 +186,8 @@ class KieAiMcpServer {
152
186
  },
153
187
  aspectRatio: {
154
188
  type: 'string',
155
- enum: ['16:9', '9:16'],
156
- description: 'Video aspect ratio',
189
+ enum: ['16:9', '9:16', 'Auto'],
190
+ description: 'Video aspect ratio (16:9 supports 1080P)',
157
191
  default: '16:9'
158
192
  },
159
193
  seeds: {
@@ -162,10 +196,20 @@ class KieAiMcpServer {
162
196
  minimum: 10000,
163
197
  maximum: 99999
164
198
  },
199
+ callBackUrl: {
200
+ type: 'string',
201
+ format: 'uri',
202
+ description: 'Callback URL for task completion notifications'
203
+ },
165
204
  enableFallback: {
166
205
  type: 'boolean',
167
- description: 'Enable fallback mechanism for content policy failures',
206
+ description: 'Enable fallback mechanism for content policy failures (Note: fallback videos cannot use 1080P endpoint)',
168
207
  default: false
208
+ },
209
+ enableTranslation: {
210
+ type: 'boolean',
211
+ description: 'Auto-translate prompts to English for better results',
212
+ default: true
169
213
  }
170
214
  },
171
215
  required: ['prompt']
@@ -259,8 +303,8 @@ class KieAiMcpServer {
259
303
  });
260
304
  }
261
305
  async handleGenerateNanoBanana(args) {
262
- const request = NanoBananaGenerateSchema.parse(args);
263
306
  try {
307
+ const request = NanoBananaGenerateSchema.parse(args);
264
308
  const response = await this.client.generateNanoBanana(request);
265
309
  if (response.data?.taskId) {
266
310
  await this.db.createTask({
@@ -284,23 +328,16 @@ class KieAiMcpServer {
284
328
  };
285
329
  }
286
330
  catch (error) {
287
- const message = error instanceof Error ? error.message : 'Generation failed';
288
- return {
289
- content: [
290
- {
291
- type: 'text',
292
- text: JSON.stringify({
293
- success: false,
294
- error: message
295
- }, null, 2)
296
- }
297
- ]
298
- };
331
+ return this.formatError('generate_nano_banana', error, {
332
+ prompt: 'Required: text description of image to generate (max 5000 chars)',
333
+ output_format: 'Optional: "png" or "jpeg"',
334
+ image_size: 'Optional: aspect ratio like "16:9", "1:1", etc.'
335
+ });
299
336
  }
300
337
  }
301
338
  async handleEditNanoBanana(args) {
302
- const request = NanoBananaEditSchema.parse(args);
303
339
  try {
340
+ const request = NanoBananaEditSchema.parse(args);
304
341
  const response = await this.client.editNanoBanana(request);
305
342
  if (response.data?.taskId) {
306
343
  await this.db.createTask({
@@ -324,23 +361,17 @@ class KieAiMcpServer {
324
361
  };
325
362
  }
326
363
  catch (error) {
327
- const message = error instanceof Error ? error.message : 'Editing failed';
328
- return {
329
- content: [
330
- {
331
- type: 'text',
332
- text: JSON.stringify({
333
- success: false,
334
- error: message
335
- }, null, 2)
336
- }
337
- ]
338
- };
364
+ return this.formatError('edit_nano_banana', error, {
365
+ prompt: 'Required: editing instructions (max 5000 chars)',
366
+ image_urls: 'Required: array of 1-10 image URLs to edit',
367
+ output_format: 'Optional: "png" or "jpeg"',
368
+ image_size: 'Optional: aspect ratio like "16:9", "1:1", etc.'
369
+ });
339
370
  }
340
371
  }
341
372
  async handleUpscaleNanoBanana(args) {
342
- const request = NanoBananaUpscaleSchema.parse(args);
343
373
  try {
374
+ const request = NanoBananaUpscaleSchema.parse(args);
344
375
  const response = await this.client.upscaleNanaBanana(request);
345
376
  if (response.data?.taskId) {
346
377
  await this.db.createTask({
@@ -364,23 +395,20 @@ class KieAiMcpServer {
364
395
  };
365
396
  }
366
397
  catch (error) {
367
- const message = error instanceof Error ? error.message : 'Upscale failed';
368
- return {
369
- content: [
370
- {
371
- type: 'text',
372
- text: JSON.stringify({
373
- success: false,
374
- error: message
375
- }, null, 2)
376
- }
377
- ]
378
- };
398
+ return this.formatError('upscale_nano_banana', error, {
399
+ image: 'Required: URL of image to upscale (jpeg/png/webp, max 10MB)',
400
+ scale: 'Optional: upscale factor 1-4 (default: 2)',
401
+ face_enhance: 'Optional: enable face enhancement (default: false)'
402
+ });
379
403
  }
380
404
  }
381
405
  async handleGenerateVeo3Video(args) {
382
- const request = Veo3GenerateSchema.parse(args);
383
406
  try {
407
+ const request = Veo3GenerateSchema.parse(args);
408
+ // Use environment variable as fallback if callBackUrl not provided
409
+ if (!request.callBackUrl && process.env.KIE_AI_CALLBACK_URL) {
410
+ request.callBackUrl = process.env.KIE_AI_CALLBACK_URL;
411
+ }
384
412
  const response = await this.client.generateVeo3Video(request);
385
413
  if (response.data?.taskId) {
386
414
  await this.db.createTask({
@@ -404,26 +432,25 @@ class KieAiMcpServer {
404
432
  };
405
433
  }
406
434
  catch (error) {
407
- const message = error instanceof Error ? error.message : 'Video generation failed';
408
- return {
409
- content: [
410
- {
411
- type: 'text',
412
- text: JSON.stringify({
413
- success: false,
414
- error: message
415
- }, null, 2)
416
- }
417
- ]
418
- };
435
+ return this.formatError('generate_veo3_video', error, {
436
+ prompt: 'Required: video description (max 2000 chars)',
437
+ imageUrls: 'Optional: array with 1 image URL for image-to-video',
438
+ model: 'Optional: "veo3" (quality) or "veo3_fast" (cost-efficient)',
439
+ watermark: 'Optional: watermark text (max 100 chars)',
440
+ aspectRatio: 'Optional: "16:9", "9:16", or "Auto"',
441
+ seeds: 'Optional: random seed (10000-99999)',
442
+ callBackUrl: 'Optional: callback URL for notifications',
443
+ enableFallback: 'Optional: enable fallback for content policy failures',
444
+ enableTranslation: 'Optional: auto-translate prompts to English'
445
+ });
419
446
  }
420
447
  }
421
448
  async handleGetTaskStatus(args) {
422
- const { task_id } = args;
423
- if (!task_id || typeof task_id !== 'string') {
424
- throw new McpError(ErrorCode.InvalidParams, 'task_id is required and must be a string');
425
- }
426
449
  try {
450
+ const { task_id } = args;
451
+ if (!task_id || typeof task_id !== 'string') {
452
+ throw new McpError(ErrorCode.InvalidParams, 'task_id is required and must be a string');
453
+ }
427
454
  const localTask = await this.db.getTask(task_id);
428
455
  // Always try to get updated status from API, passing api_type if available
429
456
  let apiResponse = null;
@@ -481,23 +508,14 @@ class KieAiMcpServer {
481
508
  };
482
509
  }
483
510
  catch (error) {
484
- const message = error instanceof Error ? error.message : 'Failed to get task status';
485
- return {
486
- content: [
487
- {
488
- type: 'text',
489
- text: JSON.stringify({
490
- success: false,
491
- error: message
492
- }, null, 2)
493
- }
494
- ]
495
- };
511
+ return this.formatError('get_task_status', error, {
512
+ task_id: 'Required: task ID to check status for'
513
+ });
496
514
  }
497
515
  }
498
516
  async handleListTasks(args) {
499
- const { limit = 20, status } = args;
500
517
  try {
518
+ const { limit = 20, status } = args;
501
519
  let tasks;
502
520
  if (status) {
503
521
  tasks = await this.db.getTasksByStatus(status, limit);
@@ -520,26 +538,18 @@ class KieAiMcpServer {
520
538
  };
521
539
  }
522
540
  catch (error) {
523
- const message = error instanceof Error ? error.message : 'Failed to list tasks';
524
- return {
525
- content: [
526
- {
527
- type: 'text',
528
- text: JSON.stringify({
529
- success: false,
530
- error: message
531
- }, null, 2)
532
- }
533
- ]
534
- };
541
+ return this.formatError('list_tasks', error, {
542
+ limit: 'Optional: max tasks to return (1-100, default: 20)',
543
+ status: 'Optional: filter by status (pending, processing, completed, failed)'
544
+ });
535
545
  }
536
546
  }
537
547
  async handleGetVeo1080pVideo(args) {
538
- const { task_id, index } = args;
539
- if (!task_id || typeof task_id !== 'string') {
540
- throw new McpError(ErrorCode.InvalidParams, 'task_id is required and must be a string');
541
- }
542
548
  try {
549
+ const { task_id, index } = args;
550
+ if (!task_id || typeof task_id !== 'string') {
551
+ throw new McpError(ErrorCode.InvalidParams, 'task_id is required and must be a string');
552
+ }
543
553
  const response = await this.client.getVeo1080pVideo(task_id, index);
544
554
  return {
545
555
  content: [
@@ -557,18 +567,10 @@ class KieAiMcpServer {
557
567
  };
558
568
  }
559
569
  catch (error) {
560
- const message = error instanceof Error ? error.message : 'Failed to get 1080p video';
561
- return {
562
- content: [
563
- {
564
- type: 'text',
565
- text: JSON.stringify({
566
- success: false,
567
- error: message
568
- }, null, 2)
569
- }
570
- ]
571
- };
570
+ return this.formatError('get_veo3_1080p_video', error, {
571
+ task_id: 'Required: Veo3 task ID to get 1080p video for',
572
+ index: 'Optional: video index (for multiple video results)'
573
+ });
572
574
  }
573
575
  }
574
576
  async run() {
package/dist/types.d.ts CHANGED
@@ -46,28 +46,31 @@ export declare const Veo3GenerateSchema: z.ZodObject<{
46
46
  imageUrls: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
47
47
  model: z.ZodDefault<z.ZodEnum<["veo3", "veo3_fast"]>>;
48
48
  watermark: z.ZodOptional<z.ZodString>;
49
- aspectRatio: z.ZodDefault<z.ZodEnum<["16:9", "9:16"]>>;
49
+ aspectRatio: z.ZodDefault<z.ZodEnum<["16:9", "9:16", "Auto"]>>;
50
50
  seeds: z.ZodOptional<z.ZodNumber>;
51
51
  callBackUrl: z.ZodOptional<z.ZodString>;
52
52
  enableFallback: z.ZodDefault<z.ZodBoolean>;
53
+ enableTranslation: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
53
54
  }, "strip", z.ZodTypeAny, {
54
55
  prompt: string;
55
56
  model: "veo3" | "veo3_fast";
56
- aspectRatio: "9:16" | "16:9";
57
+ aspectRatio: "9:16" | "16:9" | "Auto";
57
58
  enableFallback: boolean;
58
59
  imageUrls?: string[] | undefined;
59
60
  watermark?: string | undefined;
60
61
  seeds?: number | undefined;
61
62
  callBackUrl?: string | undefined;
63
+ enableTranslation?: boolean | undefined;
62
64
  }, {
63
65
  prompt: string;
64
66
  imageUrls?: string[] | undefined;
65
67
  model?: "veo3" | "veo3_fast" | undefined;
66
68
  watermark?: string | undefined;
67
- aspectRatio?: "9:16" | "16:9" | undefined;
69
+ aspectRatio?: "9:16" | "16:9" | "Auto" | undefined;
68
70
  seeds?: number | undefined;
69
71
  callBackUrl?: string | undefined;
70
72
  enableFallback?: boolean | undefined;
73
+ enableTranslation?: boolean | undefined;
71
74
  }>;
72
75
  export type NanoBananaGenerateRequest = z.infer<typeof NanoBananaGenerateSchema>;
73
76
  export type NanaBananaEditRequest = z.infer<typeof NanoBananaEditSchema>;
package/dist/types.js CHANGED
@@ -21,8 +21,9 @@ export const Veo3GenerateSchema = z.object({
21
21
  imageUrls: z.array(z.string().url()).max(1).optional(),
22
22
  model: z.enum(['veo3', 'veo3_fast']).default('veo3'),
23
23
  watermark: z.string().max(100).optional(),
24
- aspectRatio: z.enum(['16:9', '9:16']).default('16:9'),
24
+ aspectRatio: z.enum(['16:9', '9:16', 'Auto']).default('16:9'),
25
25
  seeds: z.number().int().min(10000).max(99999).optional(),
26
26
  callBackUrl: z.string().url().optional(),
27
- enableFallback: z.boolean().default(false)
27
+ enableFallback: z.boolean().default(false),
28
+ enableTranslation: z.boolean().default(true).optional()
28
29
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felores/kie-ai-mcp-server",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "MCP server for Kie.ai APIs (Nano Banana image generation/editing and Veo3 video generation)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {