@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 +15 -3
- package/dist/index.js +99 -29
- package/dist/kie-ai-client.js +2 -2
- package/dist/types.d.ts +5 -5
- package/dist/types.js +8 -3
- package/package.json +1 -1
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,
|
|
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
|
-
**
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
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'
|
|
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
|
|
540
|
-
//
|
|
539
|
+
const apiData = apiResponse.data;
|
|
540
|
+
// Handle different response formats for different API types
|
|
541
541
|
let status = 'pending';
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
status
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
|
|
554
|
-
|
|
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:
|
|
561
|
-
error_message:
|
|
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: '
|
|
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)',
|
package/dist/kie-ai-client.js
CHANGED
|
@@ -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: ["
|
|
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
|
});
|