@felores/kie-ai-mcp-server 1.2.0 → 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
@@ -196,7 +196,7 @@ Generate music with AI using Suno models.
196
196
  - `customMode` (boolean, required): Enable advanced parameter customization
197
197
  - `instrumental` (boolean, required): Generate instrumental music (no lyrics)
198
198
  - `model` (enum, required): AI model version - "V3_5", "V4", "V4_5", "V4_5PLUS", or "V5"
199
- - `callBackUrl` (string, required): URL to receive task completion updates
199
+ - `callBackUrl` (string, optional): URL to receive task completion updates (uses KIE_AI_CALLBACK_URL environment variable if not provided)
200
200
  - `style` (string, optional): Music style/genre (required in custom mode, max 1000 chars for V4_5+, V5; 200 for V3_5, V4)
201
201
  - `title` (string, optional): Track title (required in custom mode, max 80 chars)
202
202
  - `negativeTags` (string, optional): Music styles to exclude (max 200 chars)
@@ -205,7 +205,9 @@ Generate music with AI using Suno models.
205
205
  - `weirdnessConstraint` (number, optional): Creative deviation control (0-1, up to 2 decimal places)
206
206
  - `audioWeight` (number, optional): Audio feature balance (0-1, up to 2 decimal places)
207
207
 
208
- **Example:**
208
+ **Examples:**
209
+
210
+ With explicit callback URL:
209
211
  ```json
210
212
  {
211
213
  "prompt": "A calm and relaxing piano track with soft melodies",
@@ -218,7 +220,17 @@ Generate music with AI using Suno models.
218
220
  }
219
221
  ```
220
222
 
221
- **Note**: In custom mode, `style` and `title` are required. If `instrumental` is false, `prompt` is used as exact lyrics.
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.
222
234
 
223
235
  ## API Endpoints
224
236
 
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.2.0',
15
+ version: '1.2.2',
16
16
  });
17
17
  // Initialize client with config from environment
18
18
  const config = {
@@ -295,7 +295,7 @@ class KieAiMcpServer {
295
295
  },
296
296
  callBackUrl: {
297
297
  type: 'string',
298
- description: 'URL to receive task completion updates (required for all requests)',
298
+ description: 'URL to receive task completion updates (optional, will use KIE_AI_CALLBACK_URL env var if not provided)',
299
299
  format: 'uri'
300
300
  },
301
301
  style: {
@@ -340,7 +340,7 @@ class KieAiMcpServer {
340
340
  multipleOf: 0.01
341
341
  }
342
342
  },
343
- required: ['prompt', 'customMode', 'instrumental', 'model', 'callBackUrl']
343
+ required: ['prompt', 'customMode', 'instrumental', 'model']
344
344
  }
345
345
  }
346
346
  ]
@@ -536,29 +536,57 @@ class KieAiMcpServer {
536
536
  apiResponse = await this.client.getTaskStatus(task_id, localTask?.api_type);
537
537
  // Update local database with API response
538
538
  if (apiResponse?.data) {
539
- const { state, resultJson, failCode, failMsg } = apiResponse.data;
540
- // Map API state to our status
539
+ const apiData = apiResponse.data;
540
+ // Handle different response formats for different API types
541
541
  let status = 'pending';
542
- if (state === 'success')
543
- status = 'completed';
544
- else if (state === 'fail')
545
- status = 'failed';
546
- else if (state === 'waiting')
547
- status = 'processing';
548
- // Parse resultJson if available
549
- if (resultJson) {
550
- try {
551
- 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;
552
558
  }
553
- catch (e) {
554
- // Invalid JSON in resultJson
559
+ // Extract error message for Suno
560
+ if (apiData.errorMessage) {
561
+ errorMessage = apiData.errorMessage;
555
562
  }
556
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
+ }
557
585
  // Update database
558
586
  await this.db.updateTask(task_id, {
559
587
  status,
560
- result_url: parsedResult?.resultUrls?.[0] || undefined,
561
- error_message: failMsg || undefined
588
+ result_url: resultUrl,
589
+ error_message: errorMessage
562
590
  });
563
591
  }
564
592
  }
@@ -567,19 +595,57 @@ class KieAiMcpServer {
567
595
  }
568
596
  // Fetch updated local task
569
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
+ }
570
644
  return {
571
645
  content: [
572
646
  {
573
647
  type: 'text',
574
- text: JSON.stringify({
575
- success: true,
576
- task_id: task_id,
577
- status: apiResponse?.data?.state || updatedTask?.status,
578
- result_urls: parsedResult?.resultUrls || (updatedTask?.result_url ? [updatedTask.result_url] : []),
579
- error: apiResponse?.data?.failMsg || updatedTask?.error_message,
580
- api_response: apiResponse,
581
- message: updatedTask ? 'Task found' : 'Task not found in local database'
582
- }, null, 2)
648
+ text: JSON.stringify(responseData, null, 2)
583
649
  }
584
650
  ]
585
651
  };
@@ -653,6 +719,10 @@ class KieAiMcpServer {
653
719
  async handleSunoGenerateMusic(args) {
654
720
  try {
655
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
+ }
656
726
  const response = await this.client.generateSunoMusic(request);
657
727
  if (response.code === 200 && response.data?.taskId) {
658
728
  // Store task in database
@@ -695,7 +765,7 @@ class KieAiMcpServer {
695
765
  customMode: 'Required: Enable advanced customization (true/false)',
696
766
  instrumental: 'Required: Generate instrumental music (true/false)',
697
767
  model: 'Required: AI model version (V3_5, V4, V4_5, V4_5PLUS, V5)',
698
- callBackUrl: 'Required: URL for task completion notifications',
768
+ callBackUrl: 'Optional: URL for task completion notifications (uses KIE_AI_CALLBACK_URL env var if not provided)',
699
769
  style: 'Optional: Music style/genre (required in custom mode)',
700
770
  title: 'Optional: Track title (required in custom mode, max 80 chars)',
701
771
  negativeTags: 'Optional: Styles to exclude (max 200 chars)',
@@ -78,7 +78,7 @@ export class KieAiClient {
78
78
  return this.makeRequest(`/jobs/recordInfo?taskId=${taskId}`, 'GET');
79
79
  }
80
80
  else if (apiType === 'suno') {
81
- return this.makeRequest(`/generate?taskId=${taskId}`, 'GET');
81
+ return this.makeRequest(`/generate/record-info?taskId=${taskId}`, 'GET');
82
82
  }
83
83
  // Fallback: try jobs first, then veo, then generate (for tasks not in database)
84
84
  try {
@@ -90,7 +90,7 @@ export class KieAiClient {
90
90
  }
91
91
  catch (veoError) {
92
92
  try {
93
- return await this.makeRequest(`/generate?taskId=${taskId}`, 'GET');
93
+ return await this.makeRequest(`/generate/record-info?taskId=${taskId}`, 'GET');
94
94
  }
95
95
  catch (sunoError) {
96
96
  throw error;
package/dist/types.d.ts CHANGED
@@ -77,7 +77,7 @@ export declare const SunoGenerateSchema: z.ZodEffects<z.ZodObject<{
77
77
  customMode: z.ZodBoolean;
78
78
  instrumental: z.ZodBoolean;
79
79
  model: z.ZodEnum<["V3_5", "V4", "V4_5", "V4_5PLUS", "V5"]>;
80
- callBackUrl: z.ZodString;
80
+ callBackUrl: z.ZodOptional<z.ZodString>;
81
81
  style: z.ZodOptional<z.ZodString>;
82
82
  title: z.ZodOptional<z.ZodString>;
83
83
  negativeTags: z.ZodOptional<z.ZodString>;
@@ -88,9 +88,9 @@ export declare const SunoGenerateSchema: z.ZodEffects<z.ZodObject<{
88
88
  }, "strip", z.ZodTypeAny, {
89
89
  prompt: string;
90
90
  model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
91
- callBackUrl: string;
92
91
  customMode: boolean;
93
92
  instrumental: boolean;
93
+ callBackUrl?: string | undefined;
94
94
  style?: string | undefined;
95
95
  title?: string | undefined;
96
96
  negativeTags?: string | undefined;
@@ -101,9 +101,9 @@ export declare const SunoGenerateSchema: z.ZodEffects<z.ZodObject<{
101
101
  }, {
102
102
  prompt: string;
103
103
  model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
104
- callBackUrl: string;
105
104
  customMode: boolean;
106
105
  instrumental: boolean;
106
+ callBackUrl?: string | undefined;
107
107
  style?: string | undefined;
108
108
  title?: string | undefined;
109
109
  negativeTags?: string | undefined;
@@ -114,9 +114,9 @@ export declare const SunoGenerateSchema: z.ZodEffects<z.ZodObject<{
114
114
  }>, {
115
115
  prompt: string;
116
116
  model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
117
- callBackUrl: string;
118
117
  customMode: boolean;
119
118
  instrumental: boolean;
119
+ callBackUrl?: string | undefined;
120
120
  style?: string | undefined;
121
121
  title?: string | undefined;
122
122
  negativeTags?: string | undefined;
@@ -127,9 +127,9 @@ export declare const SunoGenerateSchema: z.ZodEffects<z.ZodObject<{
127
127
  }, {
128
128
  prompt: string;
129
129
  model: "V3_5" | "V4" | "V4_5" | "V4_5PLUS" | "V5";
130
- callBackUrl: string;
131
130
  customMode: boolean;
132
131
  instrumental: boolean;
132
+ callBackUrl?: string | undefined;
133
133
  style?: string | undefined;
134
134
  title?: string | undefined;
135
135
  negativeTags?: string | undefined;
package/dist/types.js CHANGED
@@ -32,7 +32,7 @@ export const SunoGenerateSchema = z.object({
32
32
  customMode: z.boolean(),
33
33
  instrumental: z.boolean(),
34
34
  model: z.enum(['V3_5', 'V4', 'V4_5', 'V4_5PLUS', 'V5']),
35
- callBackUrl: z.string().url(),
35
+ callBackUrl: z.string().url().optional(),
36
36
  style: z.string().max(1000).optional(),
37
37
  title: z.string().max(80).optional(),
38
38
  negativeTags: z.string().max(200).optional(),
@@ -41,6 +41,11 @@ export const SunoGenerateSchema = z.object({
41
41
  weirdnessConstraint: z.number().min(0).max(1).multipleOf(0.01).optional(),
42
42
  audioWeight: z.number().min(0).max(1).multipleOf(0.01).optional()
43
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
+ }
44
49
  if (data.customMode) {
45
50
  if (data.instrumental) {
46
51
  return data.style && data.title;
@@ -51,6 +56,6 @@ export const SunoGenerateSchema = z.object({
51
56
  }
52
57
  return true;
53
58
  }, {
54
- message: "In customMode: style and title are always required, prompt is required when instrumental is false",
55
- path: ["customMode"]
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"]
56
61
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@felores/kie-ai-mcp-server",
3
- "version": "1.2.0",
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": {