@felores/kie-ai-mcp-server 1.1.3 → 1.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/README.md CHANGED
@@ -9,6 +9,7 @@ An MCP (Model Context Protocol) server that provides access to Kie.ai's AI APIs
9
9
  - **Nano Banana Image Upscaling**: Upscale images 1-4x with optional face enhancement
10
10
  - **Veo3 Video Generation**: Professional-quality video generation with text-to-video and image-to-video capabilities
11
11
  - **1080p Video Upgrade**: Get high-definition versions of Veo3 videos
12
+ - **Suno Music Generation**: AI-powered music creation with multiple models (V3_5, V4, V4_5, V4_5PLUS, V5)
12
13
  - **Task Management**: SQLite-based task tracking with status polling
13
14
  - **Smart Endpoint Routing**: Automatic detection of task types for status checking
14
15
  - **Error Handling**: Comprehensive error handling and validation
@@ -187,6 +188,50 @@ Get 1080P high-definition version of a Veo3 video.
187
188
 
188
189
  **Note**: Not available for videos generated with fallback mode.
189
190
 
191
+ ### 8. `suno_generate_music`
192
+ Generate music with AI using Suno models.
193
+
194
+ **Parameters:**
195
+ - `prompt` (string, required): Description of desired audio content (max 5000 chars for V4_5+, V5; 3000 for V3_5, V4; 500 chars for non-custom mode)
196
+ - `customMode` (boolean, required): Enable advanced parameter customization
197
+ - `instrumental` (boolean, required): Generate instrumental music (no lyrics)
198
+ - `model` (enum, required): AI model version - "V3_5", "V4", "V4_5", "V4_5PLUS", or "V5"
199
+ - `callBackUrl` (string, optional): URL to receive task completion updates (uses KIE_AI_CALLBACK_URL environment variable if not provided)
200
+ - `style` (string, optional): Music style/genre (required in custom mode, max 1000 chars for V4_5+, V5; 200 for V3_5, V4)
201
+ - `title` (string, optional): Track title (required in custom mode, max 80 chars)
202
+ - `negativeTags` (string, optional): Music styles to exclude (max 200 chars)
203
+ - `vocalGender` (enum, optional): Vocal gender preference - "m" or "f" (custom mode only)
204
+ - `styleWeight` (number, optional): Style adherence strength (0-1, up to 2 decimal places)
205
+ - `weirdnessConstraint` (number, optional): Creative deviation control (0-1, up to 2 decimal places)
206
+ - `audioWeight` (number, optional): Audio feature balance (0-1, up to 2 decimal places)
207
+
208
+ **Examples:**
209
+
210
+ With explicit callback URL:
211
+ ```json
212
+ {
213
+ "prompt": "A calm and relaxing piano track with soft melodies",
214
+ "customMode": true,
215
+ "instrumental": true,
216
+ "model": "V5",
217
+ "callBackUrl": "https://api.example.com/callback",
218
+ "style": "Classical",
219
+ "title": "Peaceful Piano Meditation"
220
+ }
221
+ ```
222
+
223
+ Using environment variable (KIE_AI_CALLBACK_URL):
224
+ ```json
225
+ {
226
+ "prompt": "A relaxing electronic music track",
227
+ "customMode": false,
228
+ "instrumental": false,
229
+ "model": "V4_5PLUS"
230
+ }
231
+ ```
232
+
233
+ **Note**: In custom mode, `style` and `title` are required. If `instrumental` is false, `prompt` is used as exact lyrics. The `callBackUrl` is optional and will use the `KIE_AI_CALLBACK_URL` environment variable if not provided.
234
+
190
235
  ## API Endpoints
191
236
 
192
237
  The server interfaces with these Kie.ai API endpoints:
@@ -198,6 +243,8 @@ The server interfaces with these Kie.ai API endpoints:
198
243
  - **Nano Banana Edit**: `POST /api/v1/jobs/createTask`
199
244
  - **Nano Banana Upscale**: `POST /api/v1/jobs/createTask`
200
245
  - **Nano Banana Status**: `GET /api/v1/jobs/recordInfo`
246
+ - **Suno Music Generation**: `POST /api/v1/generate` ✅ **VALIDATED**
247
+ - **Suno Music Status**: `GET /api/v1/generate?taskId=XXX` ✅ **VALIDATED**
201
248
 
202
249
  All endpoints follow official Kie.ai API documentation.
203
250
 
@@ -209,7 +256,7 @@ The server uses SQLite to track tasks:
209
256
  CREATE TABLE tasks (
210
257
  id INTEGER PRIMARY KEY AUTOINCREMENT,
211
258
  task_id TEXT UNIQUE NOT NULL,
212
- api_type TEXT NOT NULL, -- 'nano-banana', 'nano-banana-edit', 'veo3'
259
+ api_type TEXT NOT NULL, -- 'nano-banana', 'nano-banana-edit', 'veo3', 'suno'
213
260
  status TEXT DEFAULT 'pending',
214
261
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
215
262
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
@@ -335,44 +382,4 @@ MIT License - see LICENSE file for details.
335
382
 
336
383
  ## Changelog
337
384
 
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)
372
- - Initial release
373
- - Nano Banana image generation and editing
374
- - Veo3 video generation
375
- - 1080p video upgrade support
376
- - SQLite task tracking
377
- - Smart endpoint routing
378
- - Comprehensive error handling
385
+ See [CHANGELOG.md](CHANGELOG.md) for detailed version history and release notes.
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, NanoBananaUpscaleSchema, Veo3GenerateSchema } from './types.js';
7
+ import { NanoBananaGenerateSchema, NanoBananaEditSchema, NanoBananaUpscaleSchema, Veo3GenerateSchema, SunoGenerateSchema } 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.1.3',
15
+ version: '1.2.2',
16
16
  });
17
17
  // Initialize client with config from environment
18
18
  const config = {
@@ -267,6 +267,81 @@ class KieAiMcpServer {
267
267
  },
268
268
  required: ['task_id']
269
269
  }
270
+ },
271
+ {
272
+ name: 'suno_generate_music',
273
+ description: 'Generate music with AI using Suno models (V3_5, V4, V4_5, V4_5PLUS, V5)',
274
+ inputSchema: {
275
+ type: 'object',
276
+ properties: {
277
+ prompt: {
278
+ type: 'string',
279
+ description: 'Description of the desired audio content. In custom mode: used as exact lyrics (max 5000 chars for V4_5+, V5; 3000 for V3_5, V4). In non-custom mode: core idea for auto-generated lyrics (max 500 chars)',
280
+ minLength: 1,
281
+ maxLength: 5000
282
+ },
283
+ customMode: {
284
+ type: 'boolean',
285
+ description: 'Enable advanced parameter customization. If true: requires style and title. If false: simplified mode with only prompt required'
286
+ },
287
+ instrumental: {
288
+ type: 'boolean',
289
+ description: 'Generate instrumental music (no lyrics). In custom mode: if true, only style and title required; if false, prompt used as exact lyrics'
290
+ },
291
+ model: {
292
+ type: 'string',
293
+ description: 'AI model version for generation',
294
+ enum: ['V3_5', 'V4', 'V4_5', 'V4_5PLUS', 'V5']
295
+ },
296
+ callBackUrl: {
297
+ type: 'string',
298
+ description: 'URL to receive task completion updates (optional, will use KIE_AI_CALLBACK_URL env var if not provided)',
299
+ format: 'uri'
300
+ },
301
+ style: {
302
+ type: 'string',
303
+ description: 'Music style/genre (required in custom mode, max 1000 chars for V4_5+, V5; 200 for V3_5, V4)',
304
+ maxLength: 1000
305
+ },
306
+ title: {
307
+ type: 'string',
308
+ description: 'Track title (required in custom mode, max 80 chars)',
309
+ maxLength: 80
310
+ },
311
+ negativeTags: {
312
+ type: 'string',
313
+ description: 'Music styles to exclude (optional, max 200 chars)',
314
+ maxLength: 200
315
+ },
316
+ vocalGender: {
317
+ type: 'string',
318
+ description: 'Vocal gender preference (optional, only effective in custom mode)',
319
+ enum: ['m', 'f']
320
+ },
321
+ styleWeight: {
322
+ type: 'number',
323
+ description: 'Strength of style adherence (optional, range 0-1, up to 2 decimal places)',
324
+ minimum: 0,
325
+ maximum: 1,
326
+ multipleOf: 0.01
327
+ },
328
+ weirdnessConstraint: {
329
+ type: 'number',
330
+ description: 'Controls experimental/creative deviation (optional, range 0-1, up to 2 decimal places)',
331
+ minimum: 0,
332
+ maximum: 1,
333
+ multipleOf: 0.01
334
+ },
335
+ audioWeight: {
336
+ type: 'number',
337
+ description: 'Balance weight for audio features (optional, range 0-1, up to 2 decimal places)',
338
+ minimum: 0,
339
+ maximum: 1,
340
+ multipleOf: 0.01
341
+ }
342
+ },
343
+ required: ['prompt', 'customMode', 'instrumental', 'model']
344
+ }
270
345
  }
271
346
  ]
272
347
  };
@@ -289,6 +364,8 @@ class KieAiMcpServer {
289
364
  return await this.handleListTasks(args);
290
365
  case 'veo3_get_1080p_video':
291
366
  return await this.handleVeo3Get1080pVideo(args);
367
+ case 'suno_generate_music':
368
+ return await this.handleSunoGenerateMusic(args);
292
369
  default:
293
370
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
294
371
  }
@@ -459,29 +536,57 @@ class KieAiMcpServer {
459
536
  apiResponse = await this.client.getTaskStatus(task_id, localTask?.api_type);
460
537
  // Update local database with API response
461
538
  if (apiResponse?.data) {
462
- const { state, resultJson, failCode, failMsg } = apiResponse.data;
463
- // Map API state to our status
539
+ const apiData = apiResponse.data;
540
+ // Handle different response formats for different API types
464
541
  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);
542
+ let resultUrl = undefined;
543
+ let errorMessage = undefined;
544
+ if (localTask?.api_type === 'suno') {
545
+ // Suno-specific status mapping
546
+ const sunoStatus = apiData.status;
547
+ if (sunoStatus === 'SUCCESS')
548
+ status = 'completed';
549
+ else if (sunoStatus === 'CREATE_TASK_FAILED' || sunoStatus === 'GENERATE_AUDIO_FAILED' ||
550
+ sunoStatus === 'CALLBACK_EXCEPTION' || sunoStatus === 'SENSITIVE_WORD_ERROR')
551
+ status = 'failed';
552
+ else if (sunoStatus === 'PENDING' || sunoStatus === 'TEXT_SUCCESS' || sunoStatus === 'FIRST_SUCCESS')
553
+ status = 'processing';
554
+ // Extract audio URLs from Suno response
555
+ if (apiData.response?.sunoData && apiData.response.sunoData.length > 0) {
556
+ // Use the first audio URL as the primary result
557
+ resultUrl = apiData.response.sunoData[0].audioUrl;
475
558
  }
476
- catch (e) {
477
- // Invalid JSON in resultJson
559
+ // Extract error message for Suno
560
+ if (apiData.errorMessage) {
561
+ errorMessage = apiData.errorMessage;
478
562
  }
479
563
  }
564
+ else {
565
+ // Original logic for other APIs (Nano Banana, Veo3)
566
+ const { state, resultJson, failCode, failMsg } = apiData;
567
+ if (state === 'success')
568
+ status = 'completed';
569
+ else if (state === 'fail')
570
+ status = 'failed';
571
+ else if (state === 'waiting')
572
+ status = 'processing';
573
+ // Parse resultJson if available
574
+ if (resultJson) {
575
+ try {
576
+ parsedResult = JSON.parse(resultJson);
577
+ }
578
+ catch (e) {
579
+ // Invalid JSON in resultJson
580
+ }
581
+ }
582
+ resultUrl = parsedResult?.resultUrls?.[0] || undefined;
583
+ errorMessage = failMsg || undefined;
584
+ }
480
585
  // Update database
481
586
  await this.db.updateTask(task_id, {
482
587
  status,
483
- result_url: parsedResult?.resultUrls?.[0] || undefined,
484
- error_message: failMsg || undefined
588
+ result_url: resultUrl,
589
+ error_message: errorMessage
485
590
  });
486
591
  }
487
592
  }
@@ -490,19 +595,57 @@ class KieAiMcpServer {
490
595
  }
491
596
  // Fetch updated local task
492
597
  const updatedTask = await this.db.getTask(task_id);
598
+ // Prepare response based on API type
599
+ let responseData = {
600
+ success: true,
601
+ task_id: task_id,
602
+ status: updatedTask?.status,
603
+ result_urls: updatedTask?.result_url ? [updatedTask.result_url] : [],
604
+ error: updatedTask?.error_message,
605
+ api_response: apiResponse,
606
+ message: updatedTask ? 'Task found' : 'Task not found in local database'
607
+ };
608
+ // Add Suno-specific information if applicable
609
+ if (localTask?.api_type === 'suno' && apiResponse?.data) {
610
+ const sunoData = apiResponse.data;
611
+ responseData.status = sunoData.status; // Use Suno's status directly
612
+ // Add detailed Suno information
613
+ if (sunoData.response?.sunoData) {
614
+ responseData.audio_files = sunoData.response.sunoData.map((audio) => ({
615
+ id: audio.id,
616
+ audio_url: audio.audioUrl,
617
+ stream_url: audio.streamAudioUrl,
618
+ image_url: audio.imageUrl,
619
+ title: audio.title,
620
+ duration: audio.duration,
621
+ model_name: audio.modelName,
622
+ tags: audio.tags,
623
+ create_time: audio.createTime
624
+ }));
625
+ // Update result_urls with all audio URLs
626
+ responseData.result_urls = sunoData.response.sunoData.map((audio) => audio.audioUrl);
627
+ }
628
+ // Add Suno-specific metadata
629
+ responseData.suno_metadata = {
630
+ task_type: sunoData.type,
631
+ operation_type: sunoData.operationType,
632
+ parent_music_id: sunoData.parentMusicId,
633
+ parameters: sunoData.param ? JSON.parse(sunoData.param) : null,
634
+ error_code: sunoData.errorCode,
635
+ error_message: sunoData.errorMessage
636
+ };
637
+ }
638
+ else {
639
+ // Use original logic for other APIs
640
+ responseData.status = apiResponse?.data?.state || updatedTask?.status;
641
+ responseData.result_urls = parsedResult?.resultUrls || (updatedTask?.result_url ? [updatedTask.result_url] : []);
642
+ responseData.error = apiResponse?.data?.failMsg || updatedTask?.error_message;
643
+ }
493
644
  return {
494
645
  content: [
495
646
  {
496
647
  type: 'text',
497
- text: JSON.stringify({
498
- success: true,
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,
503
- api_response: apiResponse,
504
- message: updatedTask ? 'Task found' : 'Task not found in local database'
505
- }, null, 2)
648
+ text: JSON.stringify(responseData, null, 2)
506
649
  }
507
650
  ]
508
651
  };
@@ -573,6 +716,66 @@ class KieAiMcpServer {
573
716
  });
574
717
  }
575
718
  }
719
+ async handleSunoGenerateMusic(args) {
720
+ try {
721
+ const request = SunoGenerateSchema.parse(args);
722
+ // Use environment variable as fallback if callBackUrl not provided
723
+ if (!request.callBackUrl && process.env.KIE_AI_CALLBACK_URL) {
724
+ request.callBackUrl = process.env.KIE_AI_CALLBACK_URL;
725
+ }
726
+ const response = await this.client.generateSunoMusic(request);
727
+ if (response.code === 200 && response.data?.taskId) {
728
+ // Store task in database
729
+ await this.db.createTask({
730
+ task_id: response.data.taskId,
731
+ api_type: 'suno',
732
+ status: 'pending'
733
+ });
734
+ return {
735
+ content: [
736
+ {
737
+ type: 'text',
738
+ text: JSON.stringify({
739
+ success: true,
740
+ task_id: response.data.taskId,
741
+ message: 'Music generation task created successfully',
742
+ parameters: {
743
+ model: request.model,
744
+ customMode: request.customMode,
745
+ instrumental: request.instrumental,
746
+ callBackUrl: request.callBackUrl
747
+ },
748
+ next_steps: [
749
+ 'Use get_task_status to check generation progress',
750
+ 'Task completion will be sent to the provided callback URL',
751
+ 'Generation typically takes 1-3 minutes depending on model and length'
752
+ ]
753
+ }, null, 2)
754
+ }
755
+ ]
756
+ };
757
+ }
758
+ else {
759
+ throw new Error(response.msg || 'Failed to create music generation task');
760
+ }
761
+ }
762
+ catch (error) {
763
+ return this.formatError('suno_generate_music', error, {
764
+ prompt: 'Required: Description of desired audio content',
765
+ customMode: 'Required: Enable advanced customization (true/false)',
766
+ instrumental: 'Required: Generate instrumental music (true/false)',
767
+ model: 'Required: AI model version (V3_5, V4, V4_5, V4_5PLUS, V5)',
768
+ callBackUrl: 'Optional: URL for task completion notifications (uses KIE_AI_CALLBACK_URL env var if not provided)',
769
+ style: 'Optional: Music style/genre (required in custom mode)',
770
+ title: 'Optional: Track title (required in custom mode, max 80 chars)',
771
+ negativeTags: 'Optional: Styles to exclude (max 200 chars)',
772
+ vocalGender: 'Optional: Vocal gender preference (m/f, custom mode only)',
773
+ styleWeight: 'Optional: Style adherence strength (0-1, 2 decimal places)',
774
+ weirdnessConstraint: 'Optional: Creative deviation control (0-1, 2 decimal places)',
775
+ audioWeight: 'Optional: Audio feature balance (0-1, 2 decimal places)'
776
+ });
777
+ }
778
+ }
576
779
  async run() {
577
780
  const transport = new StdioServerTransport();
578
781
  await this.server.connect(transport);
@@ -1,4 +1,4 @@
1
- import { KieAiConfig, KieAiResponse, NanoBananaGenerateRequest, NanaBananaEditRequest, NanoBananaUpscaleRequest, Veo3GenerateRequest, ImageResponse, TaskResponse } from './types.js';
1
+ import { KieAiConfig, KieAiResponse, NanoBananaGenerateRequest, NanaBananaEditRequest, NanoBananaUpscaleRequest, Veo3GenerateRequest, SunoGenerateRequest, ImageResponse, TaskResponse } from './types.js';
2
2
  export declare class KieAiClient {
3
3
  private config;
4
4
  constructor(config: KieAiConfig);
@@ -8,5 +8,6 @@ export declare class KieAiClient {
8
8
  upscaleNanaBanana(request: NanoBananaUpscaleRequest): Promise<KieAiResponse<ImageResponse>>;
9
9
  generateVeo3Video(request: Veo3GenerateRequest): Promise<KieAiResponse<TaskResponse>>;
10
10
  getTaskStatus(taskId: string, apiType?: string): Promise<KieAiResponse<any>>;
11
+ generateSunoMusic(request: SunoGenerateRequest): Promise<KieAiResponse<TaskResponse>>;
11
12
  getVeo1080pVideo(taskId: string, index?: number): Promise<KieAiResponse<any>>;
12
13
  }
@@ -77,7 +77,10 @@ export class KieAiClient {
77
77
  else if (apiType === 'nano-banana' || apiType === 'nano-banana-edit' || apiType === 'nano-banana-upscale') {
78
78
  return this.makeRequest(`/jobs/recordInfo?taskId=${taskId}`, 'GET');
79
79
  }
80
- // Fallback: try jobs first, then veo (for tasks not in database)
80
+ else if (apiType === 'suno') {
81
+ return this.makeRequest(`/generate/record-info?taskId=${taskId}`, 'GET');
82
+ }
83
+ // Fallback: try jobs first, then veo, then generate (for tasks not in database)
81
84
  try {
82
85
  return await this.makeRequest(`/jobs/recordInfo?taskId=${taskId}`, 'GET');
83
86
  }
@@ -86,10 +89,18 @@ export class KieAiClient {
86
89
  return await this.makeRequest(`/veo/record-info?taskId=${taskId}`, 'GET');
87
90
  }
88
91
  catch (veoError) {
89
- throw error;
92
+ try {
93
+ return await this.makeRequest(`/generate/record-info?taskId=${taskId}`, 'GET');
94
+ }
95
+ catch (sunoError) {
96
+ throw error;
97
+ }
90
98
  }
91
99
  }
92
100
  }
101
+ async generateSunoMusic(request) {
102
+ return this.makeRequest('/generate', 'POST', request);
103
+ }
93
104
  async getVeo1080pVideo(taskId, index) {
94
105
  const params = new URLSearchParams({ taskId });
95
106
  if (index !== undefined) {
package/dist/types.d.ts CHANGED
@@ -72,10 +72,77 @@ export declare const Veo3GenerateSchema: z.ZodObject<{
72
72
  enableFallback?: boolean | undefined;
73
73
  enableTranslation?: boolean | undefined;
74
74
  }>;
75
+ export declare const SunoGenerateSchema: z.ZodEffects<z.ZodObject<{
76
+ prompt: z.ZodString;
77
+ customMode: z.ZodBoolean;
78
+ instrumental: z.ZodBoolean;
79
+ model: z.ZodEnum<["V3_5", "V4", "V4_5", "V4_5PLUS", "V5"]>;
80
+ callBackUrl: z.ZodOptional<z.ZodString>;
81
+ style: z.ZodOptional<z.ZodString>;
82
+ title: z.ZodOptional<z.ZodString>;
83
+ negativeTags: z.ZodOptional<z.ZodString>;
84
+ vocalGender: z.ZodOptional<z.ZodEnum<["m", "f"]>>;
85
+ styleWeight: z.ZodOptional<z.ZodNumber>;
86
+ weirdnessConstraint: z.ZodOptional<z.ZodNumber>;
87
+ audioWeight: z.ZodOptional<z.ZodNumber>;
88
+ }, "strip", z.ZodTypeAny, {
89
+ prompt: string;
90
+ model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
91
+ customMode: boolean;
92
+ instrumental: boolean;
93
+ callBackUrl?: string | undefined;
94
+ style?: string | undefined;
95
+ title?: string | undefined;
96
+ negativeTags?: string | undefined;
97
+ vocalGender?: "m" | "f" | undefined;
98
+ styleWeight?: number | undefined;
99
+ weirdnessConstraint?: number | undefined;
100
+ audioWeight?: number | undefined;
101
+ }, {
102
+ prompt: string;
103
+ model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
104
+ customMode: boolean;
105
+ instrumental: boolean;
106
+ callBackUrl?: string | undefined;
107
+ style?: string | undefined;
108
+ title?: string | undefined;
109
+ negativeTags?: string | undefined;
110
+ vocalGender?: "m" | "f" | undefined;
111
+ styleWeight?: number | undefined;
112
+ weirdnessConstraint?: number | undefined;
113
+ audioWeight?: number | undefined;
114
+ }>, {
115
+ prompt: string;
116
+ model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
117
+ customMode: boolean;
118
+ instrumental: boolean;
119
+ callBackUrl?: string | undefined;
120
+ style?: string | undefined;
121
+ title?: string | undefined;
122
+ negativeTags?: string | undefined;
123
+ vocalGender?: "m" | "f" | undefined;
124
+ styleWeight?: number | undefined;
125
+ weirdnessConstraint?: number | undefined;
126
+ audioWeight?: number | undefined;
127
+ }, {
128
+ prompt: string;
129
+ model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
130
+ customMode: boolean;
131
+ instrumental: boolean;
132
+ callBackUrl?: string | undefined;
133
+ style?: string | undefined;
134
+ title?: string | undefined;
135
+ negativeTags?: string | undefined;
136
+ vocalGender?: "m" | "f" | undefined;
137
+ styleWeight?: number | undefined;
138
+ weirdnessConstraint?: number | undefined;
139
+ audioWeight?: number | undefined;
140
+ }>;
75
141
  export type NanoBananaGenerateRequest = z.infer<typeof NanoBananaGenerateSchema>;
76
142
  export type NanaBananaEditRequest = z.infer<typeof NanoBananaEditSchema>;
77
143
  export type NanoBananaUpscaleRequest = z.infer<typeof NanoBananaUpscaleSchema>;
78
144
  export type Veo3GenerateRequest = z.infer<typeof Veo3GenerateSchema>;
145
+ export type SunoGenerateRequest = z.infer<typeof SunoGenerateSchema>;
79
146
  export interface KieAiResponse<T = any> {
80
147
  code: number;
81
148
  msg: string;
@@ -91,7 +158,7 @@ export interface TaskResponse {
91
158
  export interface TaskRecord {
92
159
  id?: number;
93
160
  task_id: string;
94
- api_type: 'nano-banana' | 'nano-banana-edit' | 'nano-banana-upscale' | 'veo3';
161
+ api_type: 'nano-banana' | 'nano-banana-edit' | 'nano-banana-upscale' | 'veo3' | 'suno';
95
162
  status: 'pending' | 'processing' | 'completed' | 'failed';
96
163
  created_at: string;
97
164
  updated_at: string;
package/dist/types.js CHANGED
@@ -27,3 +27,35 @@ export const Veo3GenerateSchema = z.object({
27
27
  enableFallback: z.boolean().default(false),
28
28
  enableTranslation: z.boolean().default(true).optional()
29
29
  });
30
+ export const SunoGenerateSchema = z.object({
31
+ prompt: z.string().min(1).max(5000),
32
+ customMode: z.boolean(),
33
+ instrumental: z.boolean(),
34
+ model: z.enum(['V3_5', 'V4', 'V4_5', 'V4_5PLUS', 'V5']),
35
+ callBackUrl: z.string().url().optional(),
36
+ style: z.string().max(1000).optional(),
37
+ title: z.string().max(80).optional(),
38
+ negativeTags: z.string().max(200).optional(),
39
+ vocalGender: z.enum(['m', 'f']).optional(),
40
+ styleWeight: z.number().min(0).max(1).multipleOf(0.01).optional(),
41
+ weirdnessConstraint: z.number().min(0).max(1).multipleOf(0.01).optional(),
42
+ audioWeight: z.number().min(0).max(1).multipleOf(0.01).optional()
43
+ }).refine((data) => {
44
+ // Check if callBackUrl is provided directly or via environment variable
45
+ const hasCallBackUrl = data.callBackUrl || process.env.KIE_AI_CALLBACK_URL;
46
+ if (!hasCallBackUrl) {
47
+ return false;
48
+ }
49
+ if (data.customMode) {
50
+ if (data.instrumental) {
51
+ return data.style && data.title;
52
+ }
53
+ else {
54
+ return data.style && data.title && data.prompt;
55
+ }
56
+ }
57
+ return true;
58
+ }, {
59
+ message: "callBackUrl is required (either directly or via KIE_AI_CALLBACK_URL environment variable). In customMode: style and title are always required, prompt is required when instrumental is false",
60
+ path: ["callBackUrl"]
61
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felores/kie-ai-mcp-server",
3
- "version": "1.1.3",
3
+ "version": "1.2.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": {
@@ -34,6 +34,13 @@
34
34
  "bugs": {
35
35
  "url": "https://github.com/felores/kie-ai-mcp-server/issues"
36
36
  },
37
+ "publishConfig": {
38
+ "registry": "https://registry.npmjs.org/"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/felores/kie-ai-mcp-server.git"
43
+ },
37
44
  "dependencies": {
38
45
  "@modelcontextprotocol/sdk": "^0.4.0",
39
46
  "sqlite3": "^5.1.6",