@felores/kie-ai-mcp-server 1.0.3 → 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
@@ -5,7 +5,8 @@ An MCP (Model Context Protocol) server that provides access to Kie.ai's AI APIs
5
5
  ## Features
6
6
 
7
7
  - **Nano Banana Image Generation**: Text-to-image generation using Google's Gemini 2.5 Flash Image Preview
8
- - **Nano Banana Image Editing**: Natural language image editing with up to 5 input images
8
+ - **Nano Banana Image Editing**: Natural language image editing with up to 10 input images
9
+ - **Nano Banana Image Upscaling**: Upscale images 1-4x with optional face enhancement
9
10
  - **Veo3 Video Generation**: Professional-quality video generation with text-to-video and image-to-video capabilities
10
11
  - **1080p Video Upgrade**: Get high-definition versions of Veo3 videos
11
12
  - **Task Management**: SQLite-based task tracking with status polling
@@ -51,6 +52,7 @@ export KIE_AI_API_KEY="your-api-key-here"
51
52
  export KIE_AI_BASE_URL="https://api.kie.ai/api/v1" # Default
52
53
  export KIE_AI_TIMEOUT="60000" # Default: 60 seconds
53
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
54
56
  ```
55
57
 
56
58
  ### MCP Configuration
@@ -89,12 +91,16 @@ Or if installed globally:
89
91
  Generate images using Nano Banana.
90
92
 
91
93
  **Parameters:**
92
- - `prompt` (string, required): Text description of the image to generate
94
+ - `prompt` (string, required): Text description of the image to generate (max 5000 chars)
95
+ - `output_format` (string, optional): "png" or "jpeg" (default: "png")
96
+ - `image_size` (string, optional): Aspect ratio - "1:1", "9:16", "16:9", "3:4", "4:3", "3:2", "2:3", "5:4", "4:5", "21:9", "auto" (default: "1:1")
93
97
 
94
98
  **Example:**
95
99
  ```json
96
100
  {
97
- "prompt": "A surreal painting of a giant banana floating in space"
101
+ "prompt": "A surreal painting of a giant banana floating in space",
102
+ "output_format": "png",
103
+ "image_size": "16:9"
98
104
  }
99
105
  ```
100
106
 
@@ -102,28 +108,51 @@ Generate images using Nano Banana.
102
108
  Edit images using natural language prompts.
103
109
 
104
110
  **Parameters:**
105
- - `prompt` (string, required): Description of edits to make
106
- - `image_urls` (array, required): URLs of images to edit (max 5)
111
+ - `prompt` (string, required): Description of edits to make (max 5000 chars)
112
+ - `image_urls` (array, required): URLs of images to edit (max 10)
113
+ - `output_format` (string, optional): "png" or "jpeg" (default: "png")
114
+ - `image_size` (string, optional): Aspect ratio (default: "1:1")
107
115
 
108
116
  **Example:**
109
117
  ```json
110
118
  {
111
119
  "prompt": "Add a rainbow arching over the mountains",
112
- "image_urls": ["https://example.com/image.jpg"]
120
+ "image_urls": ["https://example.com/image.jpg"],
121
+ "output_format": "png",
122
+ "image_size": "16:9"
113
123
  }
114
124
  ```
115
125
 
116
- ### 3. `generate_veo3_video`
126
+ ### 3. `upscale_nano_banana`
127
+ Upscale images with optional face enhancement.
128
+
129
+ **Parameters:**
130
+ - `image` (string, required): URL of image to upscale (max 10MB, jpeg/png/webp)
131
+ - `scale` (integer, optional): Upscale factor 1-4 (default: 2)
132
+ - `face_enhance` (boolean, optional): Enable GFPGAN face enhancement (default: false)
133
+
134
+ **Example:**
135
+ ```json
136
+ {
137
+ "image": "https://example.com/image.jpg",
138
+ "scale": 4,
139
+ "face_enhance": true
140
+ }
141
+ ```
142
+
143
+ ### 4. `generate_veo3_video`
117
144
  Generate videos using Veo3.
118
145
 
119
146
  **Parameters:**
120
147
  - `prompt` (string, required): Video description
121
148
  - `imageUrls` (array, optional): Image for image-to-video (max 1)
122
149
  - `model` (enum, optional): "veo3" or "veo3_fast" (default: "veo3")
123
- - `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)
124
151
  - `seeds` (integer, optional): Random seed 10000-99999
125
152
  - `watermark` (string, optional): Watermark text
126
- - `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)
127
156
 
128
157
  **Example:**
129
158
  ```json
@@ -131,24 +160,25 @@ Generate videos using Veo3.
131
160
  "prompt": "A dog playing in a park",
132
161
  "model": "veo3",
133
162
  "aspectRatio": "16:9",
134
- "seeds": 12345
163
+ "seeds": 12345,
164
+ "enableTranslation": true
135
165
  }
136
166
  ```
137
167
 
138
- ### 4. `get_task_status`
168
+ ### 5. `get_task_status`
139
169
  Check the status of a generation task.
140
170
 
141
171
  **Parameters:**
142
172
  - `task_id` (string, required): Task ID to check
143
173
 
144
- ### 5. `list_tasks`
174
+ ### 6. `list_tasks`
145
175
  List recent tasks with their status.
146
176
 
147
177
  **Parameters:**
148
178
  - `limit` (integer, optional): Max tasks to return (default: 20, max: 100)
149
179
  - `status` (string, optional): Filter by status ("pending", "processing", "completed", "failed")
150
180
 
151
- ### 6. `get_veo3_1080p_video`
181
+ ### 7. `get_veo3_1080p_video`
152
182
  Get 1080P high-definition version of a Veo3 video.
153
183
 
154
184
  **Parameters:**
@@ -164,10 +194,12 @@ The server interfaces with these Kie.ai API endpoints:
164
194
  - **Veo3 Video Generation**: `POST /api/v1/veo/generate` ✅ **VALIDATED**
165
195
  - **Veo3 Video Status**: `GET /api/v1/veo/record-info` ✅ **VALIDATED**
166
196
  - **Veo3 1080p Upgrade**: `GET /api/v1/veo/get-1080p-video` ✅ **VALIDATED**
167
- - **Nano Banana Generation**: `POST /api/v1/playground/createTask` ✅ **VALIDATED**
168
- - **Nano Banana Status**: `GET /api/v1/playground/recordInfo` ✅ **VALIDATED**
197
+ - **Nano Banana Generation**: `POST /api/v1/jobs/createTask`
198
+ - **Nano Banana Edit**: `POST /api/v1/jobs/createTask`
199
+ - **Nano Banana Upscale**: `POST /api/v1/jobs/createTask`
200
+ - **Nano Banana Status**: `GET /api/v1/jobs/recordInfo`
169
201
 
170
- All endpoints have been tested and validated with live API responses.
202
+ All endpoints follow official Kie.ai API documentation.
171
203
 
172
204
  ## Database Schema
173
205
 
@@ -303,7 +335,40 @@ MIT License - see LICENSE file for details.
303
335
 
304
336
  ## Changelog
305
337
 
306
- ### v1.0.0
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
+
347
+ ### v1.1.0 (2025-01-14)
348
+
349
+ **Breaking Changes:**
350
+ - Migrated from `/playground/*` to official `/jobs/*` API endpoints for all Nano Banana operations
351
+ - Updated status check endpoint from `/playground/recordInfo` to `/jobs/recordInfo`
352
+
353
+ **New Features:**
354
+ - Added `upscale_nano_banana` tool for image upscaling (1-4x) with optional GFPGAN face enhancement
355
+ - Added `output_format` parameter (png/jpeg) to `generate_nano_banana` and `edit_nano_banana`
356
+ - Added `image_size` parameter (11 aspect ratios) to `generate_nano_banana` and `edit_nano_banana`
357
+
358
+ **Improvements:**
359
+ - Increased prompt max length from 1,000 to 5,000 characters for Nano Banana tools
360
+ - Increased max input images from 5 to 10 for `edit_nano_banana`
361
+ - Enhanced `get_task_status` to properly parse `resultJson` and extract result URLs
362
+ - Improved task status mapping: `waiting` → `processing`, `success` → `completed`, `fail` → `failed`
363
+ - Task status now automatically updates local database with API responses
364
+ - Better error message handling from API responses
365
+
366
+ **Documentation:**
367
+ - Updated README with all new parameters and tools
368
+ - Corrected API endpoints to match official Kie.ai documentation
369
+ - Added comprehensive examples for all tools
370
+
371
+ ### v1.0.0 (2024-12-XX)
307
372
  - Initial release
308
373
  - Nano Banana image generation and editing
309
374
  - Veo3 video generation
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
4
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { KieAiClient } from './kie-ai-client.js';
6
6
  import { TaskDatabase } from './database.js';
7
- import { NanoBananaGenerateSchema, NanoBananaEditSchema, Veo3GenerateSchema } from './types.js';
7
+ import { NanoBananaGenerateSchema, NanoBananaEditSchema, NanoBananaUpscaleSchema, Veo3GenerateSchema } from './types.js';
8
8
  class KieAiMcpServer {
9
9
  server;
10
10
  client;
@@ -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.0.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 {
@@ -41,7 +75,19 @@ class KieAiMcpServer {
41
75
  type: 'string',
42
76
  description: 'Text prompt for image generation',
43
77
  minLength: 1,
44
- maxLength: 1000
78
+ maxLength: 5000
79
+ },
80
+ output_format: {
81
+ type: 'string',
82
+ enum: ['png', 'jpeg'],
83
+ description: 'Output format for the images',
84
+ default: 'png'
85
+ },
86
+ image_size: {
87
+ type: 'string',
88
+ enum: ['1:1', '9:16', '16:9', '3:4', '4:3', '3:2', '2:3', '5:4', '4:5', '21:9', 'auto'],
89
+ description: 'Aspect ratio for the output image',
90
+ default: '1:1'
45
91
  }
46
92
  },
47
93
  required: ['prompt']
@@ -57,19 +103,58 @@ class KieAiMcpServer {
57
103
  type: 'string',
58
104
  description: 'Text prompt for image editing',
59
105
  minLength: 1,
60
- maxLength: 1000
106
+ maxLength: 5000
61
107
  },
62
108
  image_urls: {
63
109
  type: 'array',
64
- description: 'URLs of input images for editing (max 5)',
110
+ description: 'URLs of input images for editing (max 10)',
65
111
  items: { type: 'string', format: 'uri' },
66
112
  minItems: 1,
67
- maxItems: 5
113
+ maxItems: 10
114
+ },
115
+ output_format: {
116
+ type: 'string',
117
+ enum: ['png', 'jpeg'],
118
+ description: 'Output format for the images',
119
+ default: 'png'
120
+ },
121
+ image_size: {
122
+ type: 'string',
123
+ enum: ['1:1', '9:16', '16:9', '3:4', '4:3', '3:2', '2:3', '5:4', '4:5', '21:9', 'auto'],
124
+ description: 'Aspect ratio for the output image',
125
+ default: '1:1'
68
126
  }
69
127
  },
70
128
  required: ['prompt', 'image_urls']
71
129
  }
72
130
  },
131
+ {
132
+ name: 'upscale_nano_banana',
133
+ description: 'Upscale images using Nano Banana Upscale with optional face enhancement',
134
+ inputSchema: {
135
+ type: 'object',
136
+ properties: {
137
+ image: {
138
+ type: 'string',
139
+ format: 'uri',
140
+ description: 'URL of the image to upscale (max 10MB, jpeg/png/webp)'
141
+ },
142
+ scale: {
143
+ type: 'integer',
144
+ description: 'Upscale factor (1-4)',
145
+ minimum: 1,
146
+ maximum: 4,
147
+ default: 2
148
+ },
149
+ face_enhance: {
150
+ type: 'boolean',
151
+ description: 'Enable GFPGAN face enhancement',
152
+ default: false
153
+ }
154
+ },
155
+ required: ['image']
156
+ }
157
+ },
73
158
  {
74
159
  name: 'generate_veo3_video',
75
160
  description: 'Generate professional-quality videos using Google\'s Veo3 API',
@@ -101,8 +186,8 @@ class KieAiMcpServer {
101
186
  },
102
187
  aspectRatio: {
103
188
  type: 'string',
104
- enum: ['16:9', '9:16'],
105
- description: 'Video aspect ratio',
189
+ enum: ['16:9', '9:16', 'Auto'],
190
+ description: 'Video aspect ratio (16:9 supports 1080P)',
106
191
  default: '16:9'
107
192
  },
108
193
  seeds: {
@@ -111,10 +196,20 @@ class KieAiMcpServer {
111
196
  minimum: 10000,
112
197
  maximum: 99999
113
198
  },
199
+ callBackUrl: {
200
+ type: 'string',
201
+ format: 'uri',
202
+ description: 'Callback URL for task completion notifications'
203
+ },
114
204
  enableFallback: {
115
205
  type: 'boolean',
116
- description: 'Enable fallback mechanism for content policy failures',
206
+ description: 'Enable fallback mechanism for content policy failures (Note: fallback videos cannot use 1080P endpoint)',
117
207
  default: false
208
+ },
209
+ enableTranslation: {
210
+ type: 'boolean',
211
+ description: 'Auto-translate prompts to English for better results',
212
+ default: true
118
213
  }
119
214
  },
120
215
  required: ['prompt']
@@ -184,6 +279,8 @@ class KieAiMcpServer {
184
279
  return await this.handleGenerateNanoBanana(args);
185
280
  case 'edit_nano_banana':
186
281
  return await this.handleEditNanoBanana(args);
282
+ case 'upscale_nano_banana':
283
+ return await this.handleUpscaleNanoBanana(args);
187
284
  case 'generate_veo3_video':
188
285
  return await this.handleGenerateVeo3Video(args);
189
286
  case 'get_task_status':
@@ -206,8 +303,8 @@ class KieAiMcpServer {
206
303
  });
207
304
  }
208
305
  async handleGenerateNanoBanana(args) {
209
- const request = NanoBananaGenerateSchema.parse(args);
210
306
  try {
307
+ const request = NanoBananaGenerateSchema.parse(args);
211
308
  const response = await this.client.generateNanoBanana(request);
212
309
  if (response.data?.taskId) {
213
310
  await this.db.createTask({
@@ -231,23 +328,16 @@ class KieAiMcpServer {
231
328
  };
232
329
  }
233
330
  catch (error) {
234
- const message = error instanceof Error ? error.message : 'Generation failed';
235
- return {
236
- content: [
237
- {
238
- type: 'text',
239
- text: JSON.stringify({
240
- success: false,
241
- error: message
242
- }, null, 2)
243
- }
244
- ]
245
- };
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
+ });
246
336
  }
247
337
  }
248
338
  async handleEditNanoBanana(args) {
249
- const request = NanoBananaEditSchema.parse(args);
250
339
  try {
340
+ const request = NanoBananaEditSchema.parse(args);
251
341
  const response = await this.client.editNanoBanana(request);
252
342
  if (response.data?.taskId) {
253
343
  await this.db.createTask({
@@ -271,23 +361,54 @@ class KieAiMcpServer {
271
361
  };
272
362
  }
273
363
  catch (error) {
274
- const message = error instanceof Error ? error.message : 'Editing failed';
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
+ });
370
+ }
371
+ }
372
+ async handleUpscaleNanoBanana(args) {
373
+ try {
374
+ const request = NanoBananaUpscaleSchema.parse(args);
375
+ const response = await this.client.upscaleNanaBanana(request);
376
+ if (response.data?.taskId) {
377
+ await this.db.createTask({
378
+ task_id: response.data.taskId,
379
+ api_type: 'nano-banana-upscale',
380
+ status: 'pending',
381
+ result_url: response.data.imageUrl
382
+ });
383
+ }
275
384
  return {
276
385
  content: [
277
386
  {
278
387
  type: 'text',
279
388
  text: JSON.stringify({
280
- success: false,
281
- error: message
389
+ success: true,
390
+ response: response,
391
+ message: 'Nano Banana upscale initiated'
282
392
  }, null, 2)
283
393
  }
284
394
  ]
285
395
  };
286
396
  }
397
+ catch (error) {
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
+ });
403
+ }
287
404
  }
288
405
  async handleGenerateVeo3Video(args) {
289
- const request = Veo3GenerateSchema.parse(args);
290
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
+ }
291
412
  const response = await this.client.generateVeo3Video(request);
292
413
  if (response.data?.taskId) {
293
414
  await this.db.createTask({
@@ -311,67 +432,90 @@ class KieAiMcpServer {
311
432
  };
312
433
  }
313
434
  catch (error) {
314
- const message = error instanceof Error ? error.message : 'Video generation failed';
315
- return {
316
- content: [
317
- {
318
- type: 'text',
319
- text: JSON.stringify({
320
- success: false,
321
- error: message
322
- }, null, 2)
323
- }
324
- ]
325
- };
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
+ });
326
446
  }
327
447
  }
328
448
  async handleGetTaskStatus(args) {
329
- const { task_id } = args;
330
- if (!task_id || typeof task_id !== 'string') {
331
- throw new McpError(ErrorCode.InvalidParams, 'task_id is required and must be a string');
332
- }
333
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
+ }
334
454
  const localTask = await this.db.getTask(task_id);
335
455
  // Always try to get updated status from API, passing api_type if available
336
456
  let apiResponse = null;
457
+ let parsedResult = null;
337
458
  try {
338
459
  apiResponse = await this.client.getTaskStatus(task_id, localTask?.api_type);
460
+ // Update local database with API response
461
+ if (apiResponse?.data) {
462
+ const { state, resultJson, failCode, failMsg } = apiResponse.data;
463
+ // Map API state to our status
464
+ let status = 'pending';
465
+ if (state === 'success')
466
+ status = 'completed';
467
+ else if (state === 'fail')
468
+ status = 'failed';
469
+ else if (state === 'waiting')
470
+ status = 'processing';
471
+ // Parse resultJson if available
472
+ if (resultJson) {
473
+ try {
474
+ parsedResult = JSON.parse(resultJson);
475
+ }
476
+ catch (e) {
477
+ // Invalid JSON in resultJson
478
+ }
479
+ }
480
+ // Update database
481
+ await this.db.updateTask(task_id, {
482
+ status,
483
+ result_url: parsedResult?.resultUrls?.[0] || undefined,
484
+ error_message: failMsg || undefined
485
+ });
486
+ }
339
487
  }
340
488
  catch (error) {
341
489
  // API call failed, use local data if available
342
490
  }
491
+ // Fetch updated local task
492
+ const updatedTask = await this.db.getTask(task_id);
343
493
  return {
344
494
  content: [
345
495
  {
346
496
  type: 'text',
347
497
  text: JSON.stringify({
348
498
  success: true,
349
- local_task: localTask,
499
+ task_id: task_id,
500
+ status: apiResponse?.data?.state || updatedTask?.status,
501
+ result_urls: parsedResult?.resultUrls || (updatedTask?.result_url ? [updatedTask.result_url] : []),
502
+ error: apiResponse?.data?.failMsg || updatedTask?.error_message,
350
503
  api_response: apiResponse,
351
- message: localTask ? 'Task found' : 'Task not found in local database'
504
+ message: updatedTask ? 'Task found' : 'Task not found in local database'
352
505
  }, null, 2)
353
506
  }
354
507
  ]
355
508
  };
356
509
  }
357
510
  catch (error) {
358
- const message = error instanceof Error ? error.message : 'Failed to get task status';
359
- return {
360
- content: [
361
- {
362
- type: 'text',
363
- text: JSON.stringify({
364
- success: false,
365
- error: message
366
- }, null, 2)
367
- }
368
- ]
369
- };
511
+ return this.formatError('get_task_status', error, {
512
+ task_id: 'Required: task ID to check status for'
513
+ });
370
514
  }
371
515
  }
372
516
  async handleListTasks(args) {
373
- const { limit = 20, status } = args;
374
517
  try {
518
+ const { limit = 20, status } = args;
375
519
  let tasks;
376
520
  if (status) {
377
521
  tasks = await this.db.getTasksByStatus(status, limit);
@@ -394,26 +538,18 @@ class KieAiMcpServer {
394
538
  };
395
539
  }
396
540
  catch (error) {
397
- const message = error instanceof Error ? error.message : 'Failed to list tasks';
398
- return {
399
- content: [
400
- {
401
- type: 'text',
402
- text: JSON.stringify({
403
- success: false,
404
- error: message
405
- }, null, 2)
406
- }
407
- ]
408
- };
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
+ });
409
545
  }
410
546
  }
411
547
  async handleGetVeo1080pVideo(args) {
412
- const { task_id, index } = args;
413
- if (!task_id || typeof task_id !== 'string') {
414
- throw new McpError(ErrorCode.InvalidParams, 'task_id is required and must be a string');
415
- }
416
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
+ }
417
553
  const response = await this.client.getVeo1080pVideo(task_id, index);
418
554
  return {
419
555
  content: [
@@ -431,18 +567,10 @@ class KieAiMcpServer {
431
567
  };
432
568
  }
433
569
  catch (error) {
434
- const message = error instanceof Error ? error.message : 'Failed to get 1080p video';
435
- return {
436
- content: [
437
- {
438
- type: 'text',
439
- text: JSON.stringify({
440
- success: false,
441
- error: message
442
- }, null, 2)
443
- }
444
- ]
445
- };
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
+ });
446
574
  }
447
575
  }
448
576
  async run() {
@@ -1,10 +1,11 @@
1
- import { KieAiConfig, KieAiResponse, NanoBananaGenerateRequest, NanaBananaEditRequest, Veo3GenerateRequest, ImageResponse, TaskResponse } from './types.js';
1
+ import { KieAiConfig, KieAiResponse, NanoBananaGenerateRequest, NanaBananaEditRequest, NanoBananaUpscaleRequest, Veo3GenerateRequest, ImageResponse, TaskResponse } from './types.js';
2
2
  export declare class KieAiClient {
3
3
  private config;
4
4
  constructor(config: KieAiConfig);
5
5
  private makeRequest;
6
6
  generateNanoBanana(request: NanoBananaGenerateRequest): Promise<KieAiResponse<ImageResponse>>;
7
7
  editNanoBanana(request: NanaBananaEditRequest): Promise<KieAiResponse<ImageResponse>>;
8
+ upscaleNanaBanana(request: NanoBananaUpscaleRequest): Promise<KieAiResponse<ImageResponse>>;
8
9
  generateVeo3Video(request: Veo3GenerateRequest): Promise<KieAiResponse<TaskResponse>>;
9
10
  getTaskStatus(taskId: string, apiType?: string): Promise<KieAiResponse<any>>;
10
11
  getVeo1080pVideo(taskId: string, index?: number): Promise<KieAiResponse<any>>;
@@ -33,23 +33,38 @@ export class KieAiClient {
33
33
  }
34
34
  }
35
35
  async generateNanoBanana(request) {
36
- const playgroundRequest = {
36
+ const jobRequest = {
37
37
  model: 'google/nano-banana',
38
38
  input: {
39
- prompt: request.prompt
39
+ prompt: request.prompt,
40
+ ...(request.output_format && { output_format: request.output_format }),
41
+ ...(request.image_size && { image_size: request.image_size })
40
42
  }
41
43
  };
42
- return this.makeRequest('/playground/createTask', 'POST', playgroundRequest);
44
+ return this.makeRequest('/jobs/createTask', 'POST', jobRequest);
43
45
  }
44
46
  async editNanoBanana(request) {
45
- const playgroundRequest = {
47
+ const jobRequest = {
46
48
  model: 'google/nano-banana-edit',
47
49
  input: {
48
50
  prompt: request.prompt,
49
- image_urls: request.image_urls
51
+ image_urls: request.image_urls,
52
+ ...(request.output_format && { output_format: request.output_format }),
53
+ ...(request.image_size && { image_size: request.image_size })
54
+ }
55
+ };
56
+ return this.makeRequest('/jobs/createTask', 'POST', jobRequest);
57
+ }
58
+ async upscaleNanaBanana(request) {
59
+ const jobRequest = {
60
+ model: 'nano-banana-upscale',
61
+ input: {
62
+ image: request.image,
63
+ ...(request.scale !== undefined && { scale: request.scale }),
64
+ ...(request.face_enhance !== undefined && { face_enhance: request.face_enhance })
50
65
  }
51
66
  };
52
- return this.makeRequest('/playground/createTask', 'POST', playgroundRequest);
67
+ return this.makeRequest('/jobs/createTask', 'POST', jobRequest);
53
68
  }
54
69
  async generateVeo3Video(request) {
55
70
  return this.makeRequest('/veo/generate', 'POST', request);
@@ -59,12 +74,12 @@ export class KieAiClient {
59
74
  if (apiType === 'veo3') {
60
75
  return this.makeRequest(`/veo/record-info?taskId=${taskId}`, 'GET');
61
76
  }
62
- else if (apiType === 'nano-banana' || apiType === 'nano-banana-edit') {
63
- return this.makeRequest(`/playground/recordInfo?taskId=${taskId}`, 'GET');
77
+ else if (apiType === 'nano-banana' || apiType === 'nano-banana-edit' || apiType === 'nano-banana-upscale') {
78
+ return this.makeRequest(`/jobs/recordInfo?taskId=${taskId}`, 'GET');
64
79
  }
65
- // Fallback: try playground first, then veo (for tasks not in database)
80
+ // Fallback: try jobs first, then veo (for tasks not in database)
66
81
  try {
67
- return await this.makeRequest(`/playground/recordInfo?taskId=${taskId}`, 'GET');
82
+ return await this.makeRequest(`/jobs/recordInfo?taskId=${taskId}`, 'GET');
68
83
  }
69
84
  catch (error) {
70
85
  try {
package/dist/types.d.ts CHANGED
@@ -1,51 +1,80 @@
1
1
  import { z } from 'zod';
2
2
  export declare const NanoBananaGenerateSchema: z.ZodObject<{
3
3
  prompt: z.ZodString;
4
+ output_format: z.ZodOptional<z.ZodDefault<z.ZodEnum<["png", "jpeg"]>>>;
5
+ image_size: z.ZodOptional<z.ZodDefault<z.ZodEnum<["1:1", "9:16", "16:9", "3:4", "4:3", "3:2", "2:3", "5:4", "4:5", "21:9", "auto"]>>>;
4
6
  }, "strip", z.ZodTypeAny, {
5
7
  prompt: string;
8
+ output_format?: "png" | "jpeg" | undefined;
9
+ image_size?: "1:1" | "9:16" | "16:9" | "3:4" | "4:3" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | "auto" | undefined;
6
10
  }, {
7
11
  prompt: string;
12
+ output_format?: "png" | "jpeg" | undefined;
13
+ image_size?: "1:1" | "9:16" | "16:9" | "3:4" | "4:3" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | "auto" | undefined;
8
14
  }>;
9
15
  export declare const NanoBananaEditSchema: z.ZodObject<{
10
16
  prompt: z.ZodString;
11
17
  image_urls: z.ZodArray<z.ZodString, "many">;
18
+ output_format: z.ZodOptional<z.ZodDefault<z.ZodEnum<["png", "jpeg"]>>>;
19
+ image_size: z.ZodOptional<z.ZodDefault<z.ZodEnum<["1:1", "9:16", "16:9", "3:4", "4:3", "3:2", "2:3", "5:4", "4:5", "21:9", "auto"]>>>;
12
20
  }, "strip", z.ZodTypeAny, {
13
21
  prompt: string;
14
22
  image_urls: string[];
23
+ output_format?: "png" | "jpeg" | undefined;
24
+ image_size?: "1:1" | "9:16" | "16:9" | "3:4" | "4:3" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | "auto" | undefined;
15
25
  }, {
16
26
  prompt: string;
17
27
  image_urls: string[];
28
+ output_format?: "png" | "jpeg" | undefined;
29
+ image_size?: "1:1" | "9:16" | "16:9" | "3:4" | "4:3" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | "auto" | undefined;
30
+ }>;
31
+ export declare const NanoBananaUpscaleSchema: z.ZodObject<{
32
+ image: z.ZodString;
33
+ scale: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
34
+ face_enhance: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
35
+ }, "strip", z.ZodTypeAny, {
36
+ image: string;
37
+ scale?: number | undefined;
38
+ face_enhance?: boolean | undefined;
39
+ }, {
40
+ image: string;
41
+ scale?: number | undefined;
42
+ face_enhance?: boolean | undefined;
18
43
  }>;
19
44
  export declare const Veo3GenerateSchema: z.ZodObject<{
20
45
  prompt: z.ZodString;
21
46
  imageUrls: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
22
47
  model: z.ZodDefault<z.ZodEnum<["veo3", "veo3_fast"]>>;
23
48
  watermark: z.ZodOptional<z.ZodString>;
24
- aspectRatio: z.ZodDefault<z.ZodEnum<["16:9", "9:16"]>>;
49
+ aspectRatio: z.ZodDefault<z.ZodEnum<["16:9", "9:16", "Auto"]>>;
25
50
  seeds: z.ZodOptional<z.ZodNumber>;
26
51
  callBackUrl: z.ZodOptional<z.ZodString>;
27
52
  enableFallback: z.ZodDefault<z.ZodBoolean>;
53
+ enableTranslation: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
28
54
  }, "strip", z.ZodTypeAny, {
29
55
  prompt: string;
30
56
  model: "veo3" | "veo3_fast";
31
- aspectRatio: "16:9" | "9:16";
57
+ aspectRatio: "9:16" | "16:9" | "Auto";
32
58
  enableFallback: boolean;
33
59
  imageUrls?: string[] | undefined;
34
60
  watermark?: string | undefined;
35
61
  seeds?: number | undefined;
36
62
  callBackUrl?: string | undefined;
63
+ enableTranslation?: boolean | undefined;
37
64
  }, {
38
65
  prompt: string;
39
66
  imageUrls?: string[] | undefined;
40
67
  model?: "veo3" | "veo3_fast" | undefined;
41
68
  watermark?: string | undefined;
42
- aspectRatio?: "16:9" | "9:16" | undefined;
69
+ aspectRatio?: "9:16" | "16:9" | "Auto" | undefined;
43
70
  seeds?: number | undefined;
44
71
  callBackUrl?: string | undefined;
45
72
  enableFallback?: boolean | undefined;
73
+ enableTranslation?: boolean | undefined;
46
74
  }>;
47
75
  export type NanoBananaGenerateRequest = z.infer<typeof NanoBananaGenerateSchema>;
48
76
  export type NanaBananaEditRequest = z.infer<typeof NanoBananaEditSchema>;
77
+ export type NanoBananaUpscaleRequest = z.infer<typeof NanoBananaUpscaleSchema>;
49
78
  export type Veo3GenerateRequest = z.infer<typeof Veo3GenerateSchema>;
50
79
  export interface KieAiResponse<T = any> {
51
80
  code: number;
@@ -62,7 +91,7 @@ export interface TaskResponse {
62
91
  export interface TaskRecord {
63
92
  id?: number;
64
93
  task_id: string;
65
- api_type: 'nano-banana' | 'nano-banana-edit' | 'veo3';
94
+ api_type: 'nano-banana' | 'nano-banana-edit' | 'nano-banana-upscale' | 'veo3';
66
95
  status: 'pending' | 'processing' | 'completed' | 'failed';
67
96
  created_at: string;
68
97
  updated_at: string;
package/dist/types.js CHANGED
@@ -1,19 +1,29 @@
1
1
  import { z } from 'zod';
2
2
  // Zod schemas for request validation
3
3
  export const NanoBananaGenerateSchema = z.object({
4
- prompt: z.string().min(1).max(1000)
4
+ prompt: z.string().min(1).max(5000),
5
+ output_format: z.enum(['png', 'jpeg']).default('png').optional(),
6
+ image_size: z.enum(['1:1', '9:16', '16:9', '3:4', '4:3', '3:2', '2:3', '5:4', '4:5', '21:9', 'auto']).default('1:1').optional()
5
7
  });
6
8
  export const NanoBananaEditSchema = z.object({
7
- prompt: z.string().min(1).max(1000),
8
- image_urls: z.array(z.string().url()).min(1).max(5)
9
+ prompt: z.string().min(1).max(5000),
10
+ image_urls: z.array(z.string().url()).min(1).max(10),
11
+ output_format: z.enum(['png', 'jpeg']).default('png').optional(),
12
+ image_size: z.enum(['1:1', '9:16', '16:9', '3:4', '4:3', '3:2', '2:3', '5:4', '4:5', '21:9', 'auto']).default('1:1').optional()
13
+ });
14
+ export const NanoBananaUpscaleSchema = z.object({
15
+ image: z.string().url(),
16
+ scale: z.number().int().min(1).max(4).default(2).optional(),
17
+ face_enhance: z.boolean().default(false).optional()
9
18
  });
10
19
  export const Veo3GenerateSchema = z.object({
11
20
  prompt: z.string().min(1).max(2000),
12
21
  imageUrls: z.array(z.string().url()).max(1).optional(),
13
22
  model: z.enum(['veo3', 'veo3_fast']).default('veo3'),
14
23
  watermark: z.string().max(100).optional(),
15
- aspectRatio: z.enum(['16:9', '9:16']).default('16:9'),
24
+ aspectRatio: z.enum(['16:9', '9:16', 'Auto']).default('16:9'),
16
25
  seeds: z.number().int().min(10000).max(99999).optional(),
17
26
  callBackUrl: z.string().url().optional(),
18
- enableFallback: z.boolean().default(false)
27
+ enableFallback: z.boolean().default(false),
28
+ enableTranslation: z.boolean().default(true).optional()
19
29
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felores/kie-ai-mcp-server",
3
- "version": "1.0.3",
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": {