mcp-ai-music 1.0.4 → 1.0.7
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 +260 -260
- package/dist/index.js +220 -58
- package/package.json +40 -40
package/README.md
CHANGED
|
@@ -1,260 +1,260 @@
|
|
|
1
|
-
# AI作曲 MCP 服务
|
|
2
|
-
|
|
3
|
-
**版本 (Version):** 1.0.0
|
|
4
|
-
|
|
5
|
-
## 描述 (Description)
|
|
6
|
-
|
|
7
|
-
`mcp-ai-music` 是一个基于 Suno4.5 API 的 AI 作曲 MCP 服务,提供音乐生成、翻唱、扩展和进度查询功能。该服务支持多种音乐风格,可以生成原创音乐、对现有音乐进行翻唱转换,以及扩展音乐长度。
|
|
8
|
-
|
|
9
|
-
## 功能特点
|
|
10
|
-
|
|
11
|
-
- **原创音乐生成**:根据提示词和风格生成全新的音乐作品
|
|
12
|
-
- **音乐翻唱**:将现有音乐转换为不同风格,保留核心旋律
|
|
13
|
-
- **音乐扩展**:在保持原始风格的基础上延长音乐时长
|
|
14
|
-
- **实时进度查询**:支持轮询查询任务进度,实时了解生成状态
|
|
15
|
-
- **多模型支持**:支持 V3_5、V4、V4_5 多个AI模型版本
|
|
16
|
-
- **纯音乐模式**:支持生成无歌词的纯音乐版本
|
|
17
|
-
|
|
18
|
-
## 环境配置
|
|
19
|
-
|
|
20
|
-
> **重要**:请配置以下必填环境变量:
|
|
21
|
-
> - `SUNO_API_KEY`:Suno API 密钥
|
|
22
|
-
> - `SUNO_SERVICE_BASE`:服务网关地址,默认 `https://www.mcpcn.cc`
|
|
23
|
-
|
|
24
|
-
### 费用说明
|
|
25
|
-
- 生成音乐:¥0.438/次
|
|
26
|
-
- 翻唱音乐:¥0.438/次
|
|
27
|
-
- 扩展音乐:¥0.438/次
|
|
28
|
-
- 查询进度:免费
|
|
29
|
-
|
|
30
|
-
## 使用方法
|
|
31
|
-
|
|
32
|
-
```json
|
|
33
|
-
{
|
|
34
|
-
"mcpServers": {
|
|
35
|
-
"mcp-ai-music": {
|
|
36
|
-
"command": "node",
|
|
37
|
-
"args": [
|
|
38
|
-
"dist/index.js"
|
|
39
|
-
],
|
|
40
|
-
"env": {
|
|
41
|
-
"SUNO_API_KEY": "您的Suno API密钥",
|
|
42
|
-
"SUNO_SERVICE_BASE": "https://www.mcpcn.cc/api"
|
|
43
|
-
},
|
|
44
|
-
"autoApprove": [
|
|
45
|
-
"generate_music",
|
|
46
|
-
"cover_music",
|
|
47
|
-
"extend_music",
|
|
48
|
-
"query_progress"
|
|
49
|
-
]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## 可用工具 (Available Tools)
|
|
56
|
-
|
|
57
|
-
该服务提供以下4个工具:
|
|
58
|
-
|
|
59
|
-
### 1. `generate_music` - 生成音乐
|
|
60
|
-
|
|
61
|
-
生成原创音乐作品。
|
|
62
|
-
|
|
63
|
-
**输入参数:**
|
|
64
|
-
- `prompt` (必需): 音乐描述提示词,详细描述想要的音乐风格、情感、乐器等
|
|
65
|
-
- V3_5/V4 模型:最多 3000 字符
|
|
66
|
-
- V4_5 模型:最多 5000 字符
|
|
67
|
-
- `style` (可选): 音乐风格,如"古典"、"流行"、"摇滚"等
|
|
68
|
-
- V3_5/V4 模型:最多 200 字符
|
|
69
|
-
- V4_5 模型:最多 1000 字符
|
|
70
|
-
- `title` (可选): 音乐标题,最多 80 字符
|
|
71
|
-
- `instrumental` (可选): 是否生成纯音乐(无歌词),默认 false
|
|
72
|
-
- `model` (可选): AI模型版本,可选 "V3_5"、"V4"、"V4_5",默认 "V4_5"
|
|
73
|
-
- `negativeTags` (可选): 负面标签,描述不想要的音乐元素
|
|
74
|
-
|
|
75
|
-
**调用示例:**
|
|
76
|
-
```json
|
|
77
|
-
{
|
|
78
|
-
"name": "generate_music",
|
|
79
|
-
"arguments": {
|
|
80
|
-
"prompt": "一段平静舒缓的钢琴曲,带有柔和的旋律,适合冥想和放松",
|
|
81
|
-
"style": "古典",
|
|
82
|
-
"title": "宁静钢琴冥想",
|
|
83
|
-
"instrumental": true,
|
|
84
|
-
"model": "V4_5",
|
|
85
|
-
"negativeTags": "重金属, 强节奏鼓点"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### 2. `cover_music` - 翻唱音乐
|
|
91
|
-
|
|
92
|
-
将现有音乐转换为新的风格,保留核心旋律。
|
|
93
|
-
|
|
94
|
-
**输入参数:**
|
|
95
|
-
- `uploadUrl` (必需): 要翻唱的音频文件URL,音频长度不超过2分钟
|
|
96
|
-
- `prompt` (必需): 翻唱风格描述
|
|
97
|
-
- `style` (可选): 目标音乐风格
|
|
98
|
-
- `title` (可选): 翻唱版本标题
|
|
99
|
-
- `instrumental` (可选): 是否生成纯音乐版本,默认 false
|
|
100
|
-
- `model` (可选): AI模型版本,默认 "V4_5"
|
|
101
|
-
- `negativeTags` (可选): 负面标签
|
|
102
|
-
|
|
103
|
-
**调用示例:**
|
|
104
|
-
```json
|
|
105
|
-
{
|
|
106
|
-
"name": "cover_music",
|
|
107
|
-
"arguments": {
|
|
108
|
-
"uploadUrl": "https://example.com/audio.mp3",
|
|
109
|
-
"prompt": "将这首歌转换为爵士风格",
|
|
110
|
-
"style": "爵士",
|
|
111
|
-
"title": "爵士翻唱版",
|
|
112
|
-
"instrumental": false
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### 3. `extend_music` - 扩展音乐
|
|
118
|
-
|
|
119
|
-
在保留原始音频风格的同时扩展音轨长度。
|
|
120
|
-
|
|
121
|
-
**输入参数:**
|
|
122
|
-
- `uploadUrl` (必需): 要扩展的音频文件URL,音频长度不超过2分钟
|
|
123
|
-
- `prompt` (必需): 扩展描述,如"用更多舒缓的音符延长音乐"
|
|
124
|
-
- `continueAt` (必需): 从音频的第几秒开始扩展,必须大于0且小于音频总时长
|
|
125
|
-
- `style` (可选): 保持的音乐风格
|
|
126
|
-
- `title` (可选): 扩展版本标题
|
|
127
|
-
- `instrumental` (可选): 是否生成纯音乐版本,默认 false
|
|
128
|
-
- `model` (可选): AI模型版本,必须与源音乐保持一致,默认 "V4_5"
|
|
129
|
-
- `negativeTags` (可选): 负面标签
|
|
130
|
-
|
|
131
|
-
**调用示例:**
|
|
132
|
-
```json
|
|
133
|
-
{
|
|
134
|
-
"name": "extend_music",
|
|
135
|
-
"arguments": {
|
|
136
|
-
"uploadUrl": "https://example.com/audio.mp3",
|
|
137
|
-
"prompt": "用更多舒缓的音符延长音乐,保持原有的宁静氛围",
|
|
138
|
-
"continueAt": 60,
|
|
139
|
-
"style": "古典",
|
|
140
|
-
"title": "宁静钢琴延长版",
|
|
141
|
-
"instrumental": true
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### 4. `query_progress` - 查询进度
|
|
147
|
-
|
|
148
|
-
查询音乐生成任务的进度状态。
|
|
149
|
-
|
|
150
|
-
> **重要提示**:由于AI生成音乐需要时间,大模型需要轮询此接口来获取任务状态和结果。建议每10-30秒查询一次,直到状态为'complete'或'failed'。
|
|
151
|
-
|
|
152
|
-
**输入参数:**
|
|
153
|
-
- `taskId` (必需): 音乐生成任务的ID(从其他工具的返回结果中获取)
|
|
154
|
-
|
|
155
|
-
**调用示例:**
|
|
156
|
-
```json
|
|
157
|
-
{
|
|
158
|
-
"name": "query_progress",
|
|
159
|
-
"arguments": {
|
|
160
|
-
"taskId": "task_12345"
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
**状态说明:**
|
|
166
|
-
- `processing`: 正在处理中
|
|
167
|
-
- `text`: 文本生成完成,正在生成音频
|
|
168
|
-
- `first`: 第一首音乐生成完成
|
|
169
|
-
- `complete`: 任务完成
|
|
170
|
-
- `failed`: 任务失败
|
|
171
|
-
|
|
172
|
-
## 工作流程示例
|
|
173
|
-
|
|
174
|
-
### 生成原创音乐的完整流程:
|
|
175
|
-
|
|
176
|
-
1. **发起生成请求**
|
|
177
|
-
```json
|
|
178
|
-
{
|
|
179
|
-
"name": "generate_music",
|
|
180
|
-
"arguments": {
|
|
181
|
-
"prompt": "一首欢快的流行歌曲,适合夏天",
|
|
182
|
-
"style": "流行",
|
|
183
|
-
"title": "夏日阳光"
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
2. **获取任务ID**
|
|
189
|
-
```json
|
|
190
|
-
{
|
|
191
|
-
"taskId": "task_abc123",
|
|
192
|
-
"status": "pending",
|
|
193
|
-
"message": "音乐生成任务已提交,请使用query_progress工具查询进度。",
|
|
194
|
-
"cost": "¥0.438"
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
3. **轮询查询进度**
|
|
199
|
-
```json
|
|
200
|
-
{
|
|
201
|
-
"name": "query_progress",
|
|
202
|
-
"arguments": {
|
|
203
|
-
"taskId": "task_abc123"
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
4. **获取最终结果**
|
|
209
|
-
当状态变为'complete'时,会返回包含音乐文件信息的完整结果。
|
|
210
|
-
|
|
211
|
-
## 注意事项
|
|
212
|
-
|
|
213
|
-
> 也需要设置 `SUNO_SERVICE_BASE`(默认 `https://www.mcpcn.cc`),否则将无法正常访问服务。
|
|
214
|
-
|
|
215
|
-
1. **API密钥安全**:请确保 `SUNO_API_KEY` 环境变量已正确设置,不要在代码中硬编码API密钥。
|
|
216
|
-
|
|
217
|
-
2. **文件大小限制**:上传的音频文件长度不得超过2分钟。
|
|
218
|
-
|
|
219
|
-
3. **文件保存期限**:生成的音乐文件在服务器上保留15天后会被删除。
|
|
220
|
-
|
|
221
|
-
4. **费用控制**:每次调用生成、翻唱、扩展功能都会产生¥0.438的费用,请合理使用。
|
|
222
|
-
|
|
223
|
-
5. **轮询间隔**:建议查询进度的间隔为10-30秒,避免过于频繁的请求。
|
|
224
|
-
|
|
225
|
-
6. **模型兼容性**:在扩展音乐时,使用的模型版本必须与源音乐的生成模型保持一致。
|
|
226
|
-
|
|
227
|
-
7. **字符限制**:请注意各个参数的字符长度限制,超出限制会导致请求失败。
|
|
228
|
-
|
|
229
|
-
## 错误处理
|
|
230
|
-
|
|
231
|
-
服务会返回详细的错误信息,包括:
|
|
232
|
-
- 参数验证错误
|
|
233
|
-
- API请求失败
|
|
234
|
-
- 字符长度超限
|
|
235
|
-
- 任务状态异常
|
|
236
|
-
|
|
237
|
-
所有错误都会在响应中通过 `isError` 字段标识,并在 `errorMessage` 字段中提供具体的错误描述。
|
|
238
|
-
|
|
239
|
-
## 技术实现
|
|
240
|
-
|
|
241
|
-
- 基于 Model Context Protocol (MCP) SDK 构建
|
|
242
|
-
- 使用 Suno4.5 API 进行音乐生成
|
|
243
|
-
- 支持 TypeScript 开发
|
|
244
|
-
- 提供完整的类型定义和输入验证
|
|
245
|
-
|
|
246
|
-
## 开发和构建
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
# 安装依赖
|
|
250
|
-
npm install
|
|
251
|
-
|
|
252
|
-
# 开发模式运行
|
|
253
|
-
npm run dev
|
|
254
|
-
|
|
255
|
-
# 构建项目
|
|
256
|
-
npm run build
|
|
257
|
-
|
|
258
|
-
# 启动服务
|
|
259
|
-
npm start
|
|
260
|
-
```
|
|
1
|
+
# AI作曲 MCP 服务
|
|
2
|
+
|
|
3
|
+
**版本 (Version):** 1.0.0
|
|
4
|
+
|
|
5
|
+
## 描述 (Description)
|
|
6
|
+
|
|
7
|
+
`mcp-ai-music` 是一个基于 Suno4.5 API 的 AI 作曲 MCP 服务,提供音乐生成、翻唱、扩展和进度查询功能。该服务支持多种音乐风格,可以生成原创音乐、对现有音乐进行翻唱转换,以及扩展音乐长度。
|
|
8
|
+
|
|
9
|
+
## 功能特点
|
|
10
|
+
|
|
11
|
+
- **原创音乐生成**:根据提示词和风格生成全新的音乐作品
|
|
12
|
+
- **音乐翻唱**:将现有音乐转换为不同风格,保留核心旋律
|
|
13
|
+
- **音乐扩展**:在保持原始风格的基础上延长音乐时长
|
|
14
|
+
- **实时进度查询**:支持轮询查询任务进度,实时了解生成状态
|
|
15
|
+
- **多模型支持**:支持 V3_5、V4、V4_5 多个AI模型版本
|
|
16
|
+
- **纯音乐模式**:支持生成无歌词的纯音乐版本
|
|
17
|
+
|
|
18
|
+
## 环境配置
|
|
19
|
+
|
|
20
|
+
> **重要**:请配置以下必填环境变量:
|
|
21
|
+
> - `SUNO_API_KEY`:Suno API 密钥
|
|
22
|
+
> - `SUNO_SERVICE_BASE`:服务网关地址,默认 `https://www.mcpcn.cc`
|
|
23
|
+
|
|
24
|
+
### 费用说明
|
|
25
|
+
- 生成音乐:¥0.438/次
|
|
26
|
+
- 翻唱音乐:¥0.438/次
|
|
27
|
+
- 扩展音乐:¥0.438/次
|
|
28
|
+
- 查询进度:免费
|
|
29
|
+
|
|
30
|
+
## 使用方法
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"mcp-ai-music": {
|
|
36
|
+
"command": "node",
|
|
37
|
+
"args": [
|
|
38
|
+
"dist/index.js"
|
|
39
|
+
],
|
|
40
|
+
"env": {
|
|
41
|
+
"SUNO_API_KEY": "您的Suno API密钥",
|
|
42
|
+
"SUNO_SERVICE_BASE": "https://www.mcpcn.cc/api"
|
|
43
|
+
},
|
|
44
|
+
"autoApprove": [
|
|
45
|
+
"generate_music",
|
|
46
|
+
"cover_music",
|
|
47
|
+
"extend_music",
|
|
48
|
+
"query_progress"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 可用工具 (Available Tools)
|
|
56
|
+
|
|
57
|
+
该服务提供以下4个工具:
|
|
58
|
+
|
|
59
|
+
### 1. `generate_music` - 生成音乐
|
|
60
|
+
|
|
61
|
+
生成原创音乐作品。
|
|
62
|
+
|
|
63
|
+
**输入参数:**
|
|
64
|
+
- `prompt` (必需): 音乐描述提示词,详细描述想要的音乐风格、情感、乐器等
|
|
65
|
+
- V3_5/V4 模型:最多 3000 字符
|
|
66
|
+
- V4_5 模型:最多 5000 字符
|
|
67
|
+
- `style` (可选): 音乐风格,如"古典"、"流行"、"摇滚"等
|
|
68
|
+
- V3_5/V4 模型:最多 200 字符
|
|
69
|
+
- V4_5 模型:最多 1000 字符
|
|
70
|
+
- `title` (可选): 音乐标题,最多 80 字符
|
|
71
|
+
- `instrumental` (可选): 是否生成纯音乐(无歌词),默认 false
|
|
72
|
+
- `model` (可选): AI模型版本,可选 "V3_5"、"V4"、"V4_5",默认 "V4_5"
|
|
73
|
+
- `negativeTags` (可选): 负面标签,描述不想要的音乐元素
|
|
74
|
+
|
|
75
|
+
**调用示例:**
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"name": "generate_music",
|
|
79
|
+
"arguments": {
|
|
80
|
+
"prompt": "一段平静舒缓的钢琴曲,带有柔和的旋律,适合冥想和放松",
|
|
81
|
+
"style": "古典",
|
|
82
|
+
"title": "宁静钢琴冥想",
|
|
83
|
+
"instrumental": true,
|
|
84
|
+
"model": "V4_5",
|
|
85
|
+
"negativeTags": "重金属, 强节奏鼓点"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 2. `cover_music` - 翻唱音乐
|
|
91
|
+
|
|
92
|
+
将现有音乐转换为新的风格,保留核心旋律。
|
|
93
|
+
|
|
94
|
+
**输入参数:**
|
|
95
|
+
- `uploadUrl` (必需): 要翻唱的音频文件URL,音频长度不超过2分钟
|
|
96
|
+
- `prompt` (必需): 翻唱风格描述
|
|
97
|
+
- `style` (可选): 目标音乐风格
|
|
98
|
+
- `title` (可选): 翻唱版本标题
|
|
99
|
+
- `instrumental` (可选): 是否生成纯音乐版本,默认 false
|
|
100
|
+
- `model` (可选): AI模型版本,默认 "V4_5"
|
|
101
|
+
- `negativeTags` (可选): 负面标签
|
|
102
|
+
|
|
103
|
+
**调用示例:**
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"name": "cover_music",
|
|
107
|
+
"arguments": {
|
|
108
|
+
"uploadUrl": "https://example.com/audio.mp3",
|
|
109
|
+
"prompt": "将这首歌转换为爵士风格",
|
|
110
|
+
"style": "爵士",
|
|
111
|
+
"title": "爵士翻唱版",
|
|
112
|
+
"instrumental": false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3. `extend_music` - 扩展音乐
|
|
118
|
+
|
|
119
|
+
在保留原始音频风格的同时扩展音轨长度。
|
|
120
|
+
|
|
121
|
+
**输入参数:**
|
|
122
|
+
- `uploadUrl` (必需): 要扩展的音频文件URL,音频长度不超过2分钟
|
|
123
|
+
- `prompt` (必需): 扩展描述,如"用更多舒缓的音符延长音乐"
|
|
124
|
+
- `continueAt` (必需): 从音频的第几秒开始扩展,必须大于0且小于音频总时长
|
|
125
|
+
- `style` (可选): 保持的音乐风格
|
|
126
|
+
- `title` (可选): 扩展版本标题
|
|
127
|
+
- `instrumental` (可选): 是否生成纯音乐版本,默认 false
|
|
128
|
+
- `model` (可选): AI模型版本,必须与源音乐保持一致,默认 "V4_5"
|
|
129
|
+
- `negativeTags` (可选): 负面标签
|
|
130
|
+
|
|
131
|
+
**调用示例:**
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"name": "extend_music",
|
|
135
|
+
"arguments": {
|
|
136
|
+
"uploadUrl": "https://example.com/audio.mp3",
|
|
137
|
+
"prompt": "用更多舒缓的音符延长音乐,保持原有的宁静氛围",
|
|
138
|
+
"continueAt": 60,
|
|
139
|
+
"style": "古典",
|
|
140
|
+
"title": "宁静钢琴延长版",
|
|
141
|
+
"instrumental": true
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 4. `query_progress` - 查询进度
|
|
147
|
+
|
|
148
|
+
查询音乐生成任务的进度状态。
|
|
149
|
+
|
|
150
|
+
> **重要提示**:由于AI生成音乐需要时间,大模型需要轮询此接口来获取任务状态和结果。建议每10-30秒查询一次,直到状态为'complete'或'failed'。
|
|
151
|
+
|
|
152
|
+
**输入参数:**
|
|
153
|
+
- `taskId` (必需): 音乐生成任务的ID(从其他工具的返回结果中获取)
|
|
154
|
+
|
|
155
|
+
**调用示例:**
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"name": "query_progress",
|
|
159
|
+
"arguments": {
|
|
160
|
+
"taskId": "task_12345"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**状态说明:**
|
|
166
|
+
- `processing`: 正在处理中
|
|
167
|
+
- `text`: 文本生成完成,正在生成音频
|
|
168
|
+
- `first`: 第一首音乐生成完成
|
|
169
|
+
- `complete`: 任务完成
|
|
170
|
+
- `failed`: 任务失败
|
|
171
|
+
|
|
172
|
+
## 工作流程示例
|
|
173
|
+
|
|
174
|
+
### 生成原创音乐的完整流程:
|
|
175
|
+
|
|
176
|
+
1. **发起生成请求**
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"name": "generate_music",
|
|
180
|
+
"arguments": {
|
|
181
|
+
"prompt": "一首欢快的流行歌曲,适合夏天",
|
|
182
|
+
"style": "流行",
|
|
183
|
+
"title": "夏日阳光"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
2. **获取任务ID**
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"taskId": "task_abc123",
|
|
192
|
+
"status": "pending",
|
|
193
|
+
"message": "音乐生成任务已提交,请使用query_progress工具查询进度。",
|
|
194
|
+
"cost": "¥0.438"
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
3. **轮询查询进度**
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"name": "query_progress",
|
|
202
|
+
"arguments": {
|
|
203
|
+
"taskId": "task_abc123"
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
4. **获取最终结果**
|
|
209
|
+
当状态变为'complete'时,会返回包含音乐文件信息的完整结果。
|
|
210
|
+
|
|
211
|
+
## 注意事项
|
|
212
|
+
|
|
213
|
+
> 也需要设置 `SUNO_SERVICE_BASE`(默认 `https://www.mcpcn.cc`),否则将无法正常访问服务。
|
|
214
|
+
|
|
215
|
+
1. **API密钥安全**:请确保 `SUNO_API_KEY` 环境变量已正确设置,不要在代码中硬编码API密钥。
|
|
216
|
+
|
|
217
|
+
2. **文件大小限制**:上传的音频文件长度不得超过2分钟。
|
|
218
|
+
|
|
219
|
+
3. **文件保存期限**:生成的音乐文件在服务器上保留15天后会被删除。
|
|
220
|
+
|
|
221
|
+
4. **费用控制**:每次调用生成、翻唱、扩展功能都会产生¥0.438的费用,请合理使用。
|
|
222
|
+
|
|
223
|
+
5. **轮询间隔**:建议查询进度的间隔为10-30秒,避免过于频繁的请求。
|
|
224
|
+
|
|
225
|
+
6. **模型兼容性**:在扩展音乐时,使用的模型版本必须与源音乐的生成模型保持一致。
|
|
226
|
+
|
|
227
|
+
7. **字符限制**:请注意各个参数的字符长度限制,超出限制会导致请求失败。
|
|
228
|
+
|
|
229
|
+
## 错误处理
|
|
230
|
+
|
|
231
|
+
服务会返回详细的错误信息,包括:
|
|
232
|
+
- 参数验证错误
|
|
233
|
+
- API请求失败
|
|
234
|
+
- 字符长度超限
|
|
235
|
+
- 任务状态异常
|
|
236
|
+
|
|
237
|
+
所有错误都会在响应中通过 `isError` 字段标识,并在 `errorMessage` 字段中提供具体的错误描述。
|
|
238
|
+
|
|
239
|
+
## 技术实现
|
|
240
|
+
|
|
241
|
+
- 基于 Model Context Protocol (MCP) SDK 构建
|
|
242
|
+
- 使用 Suno4.5 API 进行音乐生成
|
|
243
|
+
- 支持 TypeScript 开发
|
|
244
|
+
- 提供完整的类型定义和输入验证
|
|
245
|
+
|
|
246
|
+
## 开发和构建
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# 安装依赖
|
|
250
|
+
npm install
|
|
251
|
+
|
|
252
|
+
# 开发模式运行
|
|
253
|
+
npm run dev
|
|
254
|
+
|
|
255
|
+
# 构建项目
|
|
256
|
+
npm run build
|
|
257
|
+
|
|
258
|
+
# 启动服务
|
|
259
|
+
npm start
|
|
260
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -26,8 +26,8 @@ function getServiceBase() {
|
|
|
26
26
|
process.env.SUNO_QUERY_BASE_URL ||
|
|
27
27
|
process.env.SUNO_CALLBACK_URL);
|
|
28
28
|
}
|
|
29
|
-
//
|
|
30
|
-
function buildCallbackUrl(kind) {
|
|
29
|
+
// 构造回调地址,可附带 chatSessionId
|
|
30
|
+
function buildCallbackUrl(kind, chatSessionId) {
|
|
31
31
|
const base = getServiceBase();
|
|
32
32
|
if (!base) {
|
|
33
33
|
throw new Error("未配置 SUNO_SERVICE_BASE(或兼容变量),用于设置服务端基地址");
|
|
@@ -37,7 +37,11 @@ function buildCallbackUrl(kind) {
|
|
|
37
37
|
"upload-cover": "/suno/callback/upload-cover",
|
|
38
38
|
"upload-extend": "/suno/callback/upload-extend",
|
|
39
39
|
};
|
|
40
|
-
|
|
40
|
+
const url = `${base.replace(/\/$/, "")}${pathMap[kind]}`;
|
|
41
|
+
if (chatSessionId) {
|
|
42
|
+
return `${url}?chat_session_id=${encodeURIComponent(chatSessionId)}`;
|
|
43
|
+
}
|
|
44
|
+
return url;
|
|
41
45
|
}
|
|
42
46
|
// 查询同样使用统一基地址
|
|
43
47
|
function getQueryBase() {
|
|
@@ -85,17 +89,22 @@ const PROGRESS_QUERY_OUTPUT_SCHEMA = {
|
|
|
85
89
|
// 工具定义
|
|
86
90
|
const GENERATE_MUSIC_TOOL = {
|
|
87
91
|
name: "generate_music",
|
|
88
|
-
description: "生成音乐。根据提示词和风格生成原创音乐,支持自定义模式和纯音乐模式。\n\n注意:\n- 生成需要时间,请每10-30秒轮询调用 query_progress 查询进度工具,直到状态为 'complete' 或 'failed'。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n\n参数行为说明:\n- customMode: 是否启用自定义模式(默认 true)。\n- instrumental: 是否为纯音乐。\n- 当 customMode=true 且 instrumental=false
|
|
92
|
+
description: "生成音乐。根据提示词和风格生成原创音乐,支持自定义模式和纯音乐模式。\n\n注意:\n- 生成需要时间,请每10-30秒轮询调用 query_progress 查询进度工具,直到状态为 'complete' 或 'failed'。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n\n参数行为说明:\n- customMode: 是否启用自定义模式(默认 true)。\n- instrumental: 是否为纯音乐。\n- 当 customMode=true 且 instrumental=false 时,可以同时提供 prompt(音乐描述)和 lyrics(精确歌词);\n 当 customMode=false 且 instrumental=false 时,只需 prompt,将自动生成歌词;\n 当 instrumental=true 时,始终为纯音乐(不含歌词),只需提供 prompt。\n\n返回字段说明:\n- taskId: 任务ID,用于查询进度\n- status: 任务状态 (pending)\n- message: 状态描述",
|
|
89
93
|
inputSchema: {
|
|
90
94
|
type: "object",
|
|
91
95
|
properties: {
|
|
92
96
|
prompt: {
|
|
93
97
|
type: "string",
|
|
94
|
-
description: "音乐描述提示词,详细描述想要的音乐风格、情感、乐器等。长度限制:V3_5和V4模型3000字符,V4_5模型5000
|
|
98
|
+
description: "音乐描述提示词,详细描述想要的音乐风格、情感、乐器等。长度限制:V3_5和V4模型3000字符,V4_5模型5000字符。当 instrumental=false 时,可与 lyrics 字段配合使用。",
|
|
99
|
+
},
|
|
100
|
+
lyrics: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "精确歌词内容,用于 customMode=true 且 instrumental=false 时。当提供此字段时,AI将按照此歌词演唱。长度限制:V3_5和V4模型3000字符,V4_5模型5000字符。",
|
|
103
|
+
default: "",
|
|
95
104
|
},
|
|
96
105
|
style: {
|
|
97
106
|
type: "string",
|
|
98
|
-
description: "音乐风格,如'古典'、'流行'、'摇滚'等。长度限制:V3_5和V4模型200字符,V4_5模型1000字符。",
|
|
107
|
+
description: "音乐风格,如'古典'、'流行'、'摇滚'、'电子'等。长度限制:V3_5和V4模型200字符,V4_5模型1000字符。",
|
|
99
108
|
default: "",
|
|
100
109
|
},
|
|
101
110
|
title: {
|
|
@@ -105,12 +114,12 @@ const GENERATE_MUSIC_TOOL = {
|
|
|
105
114
|
},
|
|
106
115
|
customMode: {
|
|
107
116
|
type: "boolean",
|
|
108
|
-
description: "是否启用自定义模式。为 true 时:如果 instrumental=true,仅需提供 style 和 title;如果 instrumental=false,需要提供 style、title
|
|
117
|
+
description: "是否启用自定义模式。为 true 时:如果 instrumental=true,仅需提供 style 和 title;如果 instrumental=false,需要提供 style、title,可选择提供 prompt(音乐描述)和/或 lyrics(精确歌词)。为 false 时:只需 prompt,若 instrumental=false 将自动生成歌词。",
|
|
109
118
|
default: true,
|
|
110
119
|
},
|
|
111
120
|
instrumental: {
|
|
112
121
|
type: "boolean",
|
|
113
|
-
description: "是否生成纯音乐(无歌词)。当 customMode=true 且该值=false
|
|
122
|
+
description: "是否生成纯音乐(无歌词)。当 customMode=true 且该值=false 时,可以同时提供 prompt(音乐描述)和 lyrics(精确歌词);当 customMode=false 且该值=false,将自动生成歌词。",
|
|
114
123
|
default: false,
|
|
115
124
|
},
|
|
116
125
|
model: {
|
|
@@ -131,7 +140,7 @@ const GENERATE_MUSIC_TOOL = {
|
|
|
131
140
|
};
|
|
132
141
|
const COVER_MUSIC_TOOL = {
|
|
133
142
|
name: "cover_music",
|
|
134
|
-
description: "翻唱音乐。上传音频文件并转换为新的风格,保留核心旋律。\n\n注意:\n- 生成需要时间,请每10-30秒轮询调用 query_progress 工具,直到状态为 'complete' 或 'failed'。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n\n返回字段说明:\n- taskId: 任务ID,用于查询进度\n- status: 任务状态 (pending)\n- message: 状态描述",
|
|
143
|
+
description: "翻唱音乐。上传音频文件并转换为新的风格,保留核心旋律。\n\n注意:\n- 生成需要时间,请每10-30秒轮询调用 query_progress 工具,直到状态为 'complete' 或 'failed'。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n- 当 instrumental=false 时,可以同时提供 prompt(风格描述)和 lyrics(精确歌词)。\n\n返回字段说明:\n- taskId: 任务ID,用于查询进度\n- status: 任务状态 (pending)\n- message: 状态描述",
|
|
135
144
|
inputSchema: {
|
|
136
145
|
type: "object",
|
|
137
146
|
properties: {
|
|
@@ -141,11 +150,16 @@ const COVER_MUSIC_TOOL = {
|
|
|
141
150
|
},
|
|
142
151
|
prompt: {
|
|
143
152
|
type: "string",
|
|
144
|
-
description: "
|
|
153
|
+
description: "翻唱风格描述,描述想要的音乐风格、情感、乐器等。当 instrumental=false 时,可与 lyrics 字段配合使用。",
|
|
154
|
+
},
|
|
155
|
+
lyrics: {
|
|
156
|
+
type: "string",
|
|
157
|
+
description: "精确歌词内容,用于 instrumental=false 时。当提供此字段时,AI将按照此歌词演唱。长度限制:V3_5和V4模型3000字符,V4_5模型5000字符。",
|
|
158
|
+
default: "",
|
|
145
159
|
},
|
|
146
160
|
style: {
|
|
147
161
|
type: "string",
|
|
148
|
-
description: "
|
|
162
|
+
description: "目标音乐风格,如'古典'、'流行'、'摇滚'、'电子'等。",
|
|
149
163
|
default: "",
|
|
150
164
|
},
|
|
151
165
|
title: {
|
|
@@ -155,7 +169,7 @@ const COVER_MUSIC_TOOL = {
|
|
|
155
169
|
},
|
|
156
170
|
instrumental: {
|
|
157
171
|
type: "boolean",
|
|
158
|
-
description: "
|
|
172
|
+
description: "是否生成纯音乐版本。当该值=false 时,可以同时提供 prompt(风格描述)和 lyrics(精确歌词)。",
|
|
159
173
|
default: false,
|
|
160
174
|
},
|
|
161
175
|
model: {
|
|
@@ -166,7 +180,7 @@ const COVER_MUSIC_TOOL = {
|
|
|
166
180
|
},
|
|
167
181
|
negativeTags: {
|
|
168
182
|
type: "string",
|
|
169
|
-
description: "
|
|
183
|
+
description: "负面标签,描述不想要的音乐元素。",
|
|
170
184
|
default: "",
|
|
171
185
|
},
|
|
172
186
|
},
|
|
@@ -176,7 +190,7 @@ const COVER_MUSIC_TOOL = {
|
|
|
176
190
|
};
|
|
177
191
|
const EXTEND_MUSIC_TOOL = {
|
|
178
192
|
name: "extend_music",
|
|
179
|
-
description: "扩展音乐。在保留原始音频风格的同时扩展音轨长度。\n\n注意:\n- 生成需要时间,请每10-30秒轮询调用 query_progress 工具,直到状态为 'complete' 或 'failed'。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n\n返回字段说明:\n- taskId: 任务ID,用于查询进度\n- status: 任务状态 (pending)\n- message: 状态描述",
|
|
193
|
+
description: "扩展音乐。在保留原始音频风格的同时扩展音轨长度。\n\n注意:\n- 生成需要时间,请每10-30秒轮询调用 query_progress 工具,直到状态为 'complete' 或 'failed'。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n- 当 instrumental=false 时,可以同时提供 prompt(扩展描述)和 lyrics(精确歌词)。\n\n返回字段说明:\n- taskId: 任务ID,用于查询进度\n- status: 任务状态 (pending)\n- message: 状态描述",
|
|
180
194
|
inputSchema: {
|
|
181
195
|
type: "object",
|
|
182
196
|
properties: {
|
|
@@ -186,11 +200,16 @@ const EXTEND_MUSIC_TOOL = {
|
|
|
186
200
|
},
|
|
187
201
|
prompt: {
|
|
188
202
|
type: "string",
|
|
189
|
-
description: "扩展描述,如'用更多舒缓的音符延长音乐'
|
|
203
|
+
description: "扩展描述,如'用更多舒缓的音符延长音乐'。当 instrumental=false 时,可与 lyrics 字段配合使用。",
|
|
204
|
+
},
|
|
205
|
+
lyrics: {
|
|
206
|
+
type: "string",
|
|
207
|
+
description: "精确歌词内容,用于 instrumental=false 时。当提供此字段时,AI将按照此歌词演唱扩展部分。长度限制:V3_5和V4模型3000字符,V4_5模型5000字符。",
|
|
208
|
+
default: "",
|
|
190
209
|
},
|
|
191
210
|
style: {
|
|
192
211
|
type: "string",
|
|
193
|
-
description: "
|
|
212
|
+
description: "保持的音乐风格,如'古典'、'流行'、'摇滚'、'电子'等。",
|
|
194
213
|
default: "",
|
|
195
214
|
},
|
|
196
215
|
title: {
|
|
@@ -205,7 +224,7 @@ const EXTEND_MUSIC_TOOL = {
|
|
|
205
224
|
},
|
|
206
225
|
instrumental: {
|
|
207
226
|
type: "boolean",
|
|
208
|
-
description: "
|
|
227
|
+
description: "是否生成纯音乐版本。当该值=false 时,可以同时提供 prompt(扩展描述)和 lyrics(精确歌词)。",
|
|
209
228
|
default: false,
|
|
210
229
|
},
|
|
211
230
|
model: {
|
|
@@ -216,7 +235,7 @@ const EXTEND_MUSIC_TOOL = {
|
|
|
216
235
|
},
|
|
217
236
|
negativeTags: {
|
|
218
237
|
type: "string",
|
|
219
|
-
description: "
|
|
238
|
+
description: "负面标签,描述不想要的音乐元素。",
|
|
220
239
|
default: "",
|
|
221
240
|
},
|
|
222
241
|
},
|
|
@@ -336,7 +355,7 @@ async function makeApiRequestRaw(url, method, body, retries = 3) {
|
|
|
336
355
|
}
|
|
337
356
|
}
|
|
338
357
|
// 生成音乐处理函数
|
|
339
|
-
async function handleGenerateMusic(input) {
|
|
358
|
+
async function handleGenerateMusic(input, chatSessionId) {
|
|
340
359
|
try {
|
|
341
360
|
if (!input || typeof input !== 'object') {
|
|
342
361
|
return {
|
|
@@ -345,44 +364,107 @@ async function handleGenerateMusic(input) {
|
|
|
345
364
|
errorMessage: "输入参数格式错误,预期为包含prompt字段的对象。",
|
|
346
365
|
};
|
|
347
366
|
}
|
|
348
|
-
const { prompt, style = "", title = "", customMode = true, instrumental = false, model = "V4_5", negativeTags = "" } = input;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
367
|
+
const { prompt, lyrics = "", style = "", title = "", customMode = true, instrumental = false, model = "V4_5", negativeTags = "" } = input;
|
|
368
|
+
// 根据API文档,当customMode=false时,只需要prompt
|
|
369
|
+
// 当customMode=true且instrumental=true时,只需要style和title
|
|
370
|
+
// 当customMode=true且instrumental=false时,需要style和title,可以选择提供prompt和/或lyrics
|
|
371
|
+
if (!customMode) {
|
|
372
|
+
// 非自定义模式:只需要prompt
|
|
373
|
+
if (!prompt || typeof prompt !== 'string') {
|
|
374
|
+
return {
|
|
375
|
+
content: [],
|
|
376
|
+
isError: true,
|
|
377
|
+
errorMessage: "非自定义模式下,prompt字段是必需的,且必须为字符串类型。",
|
|
378
|
+
};
|
|
379
|
+
}
|
|
355
380
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
381
|
+
else if (instrumental) {
|
|
382
|
+
// 自定义模式且为纯音乐:只需要style和title
|
|
383
|
+
if (!style || typeof style !== 'string') {
|
|
384
|
+
return {
|
|
385
|
+
content: [],
|
|
386
|
+
isError: true,
|
|
387
|
+
errorMessage: "自定义纯音乐模式下,style字段是必需的,且必须为字符串类型。",
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
if (!title || typeof title !== 'string') {
|
|
391
|
+
return {
|
|
392
|
+
content: [],
|
|
393
|
+
isError: true,
|
|
394
|
+
errorMessage: "自定义纯音乐模式下,title字段是必需的,且必须为字符串类型。",
|
|
395
|
+
};
|
|
396
|
+
}
|
|
364
397
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
398
|
+
else {
|
|
399
|
+
// 自定义模式且有歌词:需要style和title,可以选择提供prompt和/或lyrics
|
|
400
|
+
if (!style || typeof style !== 'string') {
|
|
401
|
+
return {
|
|
402
|
+
content: [],
|
|
403
|
+
isError: true,
|
|
404
|
+
errorMessage: "自定义有歌词模式下,style字段是必需的,且必须为字符串类型。",
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
if (!title || typeof title !== 'string') {
|
|
408
|
+
return {
|
|
409
|
+
content: [],
|
|
410
|
+
isError: true,
|
|
411
|
+
errorMessage: "自定义有歌词模式下,title字段是必需的,且必须为字符串类型。",
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
if (!prompt && !lyrics) {
|
|
415
|
+
return {
|
|
416
|
+
content: [],
|
|
417
|
+
isError: true,
|
|
418
|
+
errorMessage: "自定义有歌词模式下,至少需要提供prompt(音乐描述)或lyrics(精确歌词)中的一个。",
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
// 验证prompt长度(如果提供)
|
|
423
|
+
if (prompt) {
|
|
424
|
+
const maxPromptLength = model === "V4_5" ? 5000 : 3000;
|
|
425
|
+
if (prompt.length > maxPromptLength) {
|
|
426
|
+
return {
|
|
427
|
+
content: [],
|
|
428
|
+
isError: true,
|
|
429
|
+
errorMessage: `prompt长度超过限制。${model}模型最大允许${maxPromptLength}字符。`,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// 验证lyrics长度(如果提供)
|
|
434
|
+
if (lyrics) {
|
|
435
|
+
const maxLyricsLength = model === "V4_5" ? 5000 : 3000;
|
|
436
|
+
if (lyrics.length > maxLyricsLength) {
|
|
437
|
+
return {
|
|
438
|
+
content: [],
|
|
439
|
+
isError: true,
|
|
440
|
+
errorMessage: `lyrics长度超过限制。${model}模型最大允许${maxLyricsLength}字符。`,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// 验证style长度(如果提供)
|
|
445
|
+
if (style) {
|
|
446
|
+
const maxStyleLength = model === "V4_5" ? 1000 : 200;
|
|
447
|
+
if (style.length > maxStyleLength) {
|
|
448
|
+
return {
|
|
449
|
+
content: [],
|
|
450
|
+
isError: true,
|
|
451
|
+
errorMessage: `style长度超过限制。${model}模型最大允许${maxStyleLength}字符。`,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
373
454
|
}
|
|
374
|
-
// 验证title
|
|
375
|
-
if (title.length > 80) {
|
|
455
|
+
// 验证title长度(如果提供)
|
|
456
|
+
if (title && title.length > 80) {
|
|
376
457
|
return {
|
|
377
458
|
content: [],
|
|
378
459
|
isError: true,
|
|
379
460
|
errorMessage: "title长度超过限制,最大允许80字符。",
|
|
380
461
|
};
|
|
381
462
|
}
|
|
382
|
-
const callbackUrl = buildCallbackUrl("generate");
|
|
463
|
+
const callbackUrl = buildCallbackUrl("generate", chatSessionId);
|
|
383
464
|
console.error(`使用回调地址: ${callbackUrl}`);
|
|
465
|
+
// 构建请求体,根据API文档,当customMode=true且instrumental=false时,
|
|
466
|
+
// 如果提供了lyrics,则使用lyrics作为prompt字段发送给API
|
|
384
467
|
const requestBody = {
|
|
385
|
-
prompt,
|
|
386
468
|
style,
|
|
387
469
|
title,
|
|
388
470
|
customMode,
|
|
@@ -391,6 +473,14 @@ async function handleGenerateMusic(input) {
|
|
|
391
473
|
negativeTags,
|
|
392
474
|
callBackUrl: callbackUrl, // 必需的参数
|
|
393
475
|
};
|
|
476
|
+
// 根据Suno API文档,当customMode=true且instrumental=false时,
|
|
477
|
+
// 如果提供了精确歌词,应该将其作为prompt字段发送
|
|
478
|
+
if (customMode && !instrumental && lyrics) {
|
|
479
|
+
requestBody.prompt = lyrics;
|
|
480
|
+
}
|
|
481
|
+
else if (prompt) {
|
|
482
|
+
requestBody.prompt = prompt;
|
|
483
|
+
}
|
|
394
484
|
const result = await makeApiRequest('/generate', 'POST', requestBody);
|
|
395
485
|
const taskData = {
|
|
396
486
|
taskId: result.data?.taskId || result.taskId || result.id || result.task_id || "unknown",
|
|
@@ -419,7 +509,7 @@ async function handleGenerateMusic(input) {
|
|
|
419
509
|
}
|
|
420
510
|
}
|
|
421
511
|
// 翻唱音乐处理函数
|
|
422
|
-
async function handleCoverMusic(input) {
|
|
512
|
+
async function handleCoverMusic(input, chatSessionId) {
|
|
423
513
|
try {
|
|
424
514
|
if (!input || typeof input !== 'object') {
|
|
425
515
|
return {
|
|
@@ -428,7 +518,7 @@ async function handleCoverMusic(input) {
|
|
|
428
518
|
errorMessage: "输入参数格式错误,预期为包含uploadUrl和prompt字段的对象。",
|
|
429
519
|
};
|
|
430
520
|
}
|
|
431
|
-
const { uploadUrl, prompt, style = "", title = "", instrumental = false, model = "V4_5", negativeTags = "" } = input;
|
|
521
|
+
const { uploadUrl, prompt, lyrics = "", style = "", title = "", instrumental = false, model = "V4_5", negativeTags = "" } = input;
|
|
432
522
|
if (!uploadUrl || typeof uploadUrl !== 'string') {
|
|
433
523
|
return {
|
|
434
524
|
content: [],
|
|
@@ -443,17 +533,45 @@ async function handleCoverMusic(input) {
|
|
|
443
533
|
errorMessage: "prompt字段是必需的,且必须为字符串类型。",
|
|
444
534
|
};
|
|
445
535
|
}
|
|
536
|
+
// 验证prompt长度
|
|
537
|
+
if (prompt) {
|
|
538
|
+
const maxPromptLength = model === "V4_5" ? 5000 : 3000;
|
|
539
|
+
if (prompt.length > maxPromptLength) {
|
|
540
|
+
return {
|
|
541
|
+
content: [],
|
|
542
|
+
isError: true,
|
|
543
|
+
errorMessage: `prompt长度超过限制。${model}模型最大允许${maxPromptLength}字符。`,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
// 验证lyrics长度(如果提供)
|
|
548
|
+
if (lyrics) {
|
|
549
|
+
const maxLyricsLength = model === "V4_5" ? 5000 : 3000;
|
|
550
|
+
if (lyrics.length > maxLyricsLength) {
|
|
551
|
+
return {
|
|
552
|
+
content: [],
|
|
553
|
+
isError: true,
|
|
554
|
+
errorMessage: `lyrics长度超过限制。${model}模型最大允许${maxLyricsLength}字符。`,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}
|
|
446
558
|
const requestBody = {
|
|
447
559
|
uploadUrl,
|
|
448
|
-
prompt,
|
|
449
560
|
style,
|
|
450
561
|
title,
|
|
451
562
|
customMode: true,
|
|
452
563
|
instrumental,
|
|
453
564
|
model,
|
|
454
565
|
negativeTags,
|
|
455
|
-
callBackUrl: buildCallbackUrl("upload-cover"), // 必需的参数
|
|
566
|
+
callBackUrl: buildCallbackUrl("upload-cover", chatSessionId), // 必需的参数
|
|
456
567
|
};
|
|
568
|
+
// 根据Suno API文档,当instrumental=false且提供了lyrics时,使用lyrics作为prompt字段发送给API
|
|
569
|
+
if (!instrumental && lyrics) {
|
|
570
|
+
requestBody.prompt = lyrics;
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
requestBody.prompt = prompt;
|
|
574
|
+
}
|
|
457
575
|
const result = await makeApiRequest('/generate/upload-cover', 'POST', requestBody);
|
|
458
576
|
const taskData = {
|
|
459
577
|
taskId: result.data?.taskId || result.taskId || result.id || result.task_id || "unknown",
|
|
@@ -482,7 +600,7 @@ async function handleCoverMusic(input) {
|
|
|
482
600
|
}
|
|
483
601
|
}
|
|
484
602
|
// 扩展音乐处理函数
|
|
485
|
-
async function handleExtendMusic(input) {
|
|
603
|
+
async function handleExtendMusic(input, chatSessionId) {
|
|
486
604
|
try {
|
|
487
605
|
if (!input || typeof input !== 'object') {
|
|
488
606
|
return {
|
|
@@ -491,7 +609,7 @@ async function handleExtendMusic(input) {
|
|
|
491
609
|
errorMessage: "输入参数格式错误,预期为包含uploadUrl、prompt和continueAt字段的对象。",
|
|
492
610
|
};
|
|
493
611
|
}
|
|
494
|
-
const { uploadUrl, prompt, style = "", title = "", continueAt, instrumental = false, model = "V4_5", negativeTags = "" } = input;
|
|
612
|
+
const { uploadUrl, prompt, lyrics = "", style = "", title = "", continueAt, instrumental = false, model = "V4_5", negativeTags = "" } = input;
|
|
495
613
|
if (!uploadUrl || typeof uploadUrl !== 'string') {
|
|
496
614
|
return {
|
|
497
615
|
content: [],
|
|
@@ -513,18 +631,46 @@ async function handleExtendMusic(input) {
|
|
|
513
631
|
errorMessage: "continueAt字段是必需的,且必须为大于0的数字。",
|
|
514
632
|
};
|
|
515
633
|
}
|
|
634
|
+
// 验证prompt长度
|
|
635
|
+
if (prompt) {
|
|
636
|
+
const maxPromptLength = model === "V4_5" ? 5000 : 3000;
|
|
637
|
+
if (prompt.length > maxPromptLength) {
|
|
638
|
+
return {
|
|
639
|
+
content: [],
|
|
640
|
+
isError: true,
|
|
641
|
+
errorMessage: `prompt长度超过限制。${model}模型最大允许${maxPromptLength}字符。`,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// 验证lyrics长度(如果提供)
|
|
646
|
+
if (lyrics) {
|
|
647
|
+
const maxLyricsLength = model === "V4_5" ? 5000 : 3000;
|
|
648
|
+
if (lyrics.length > maxLyricsLength) {
|
|
649
|
+
return {
|
|
650
|
+
content: [],
|
|
651
|
+
isError: true,
|
|
652
|
+
errorMessage: `lyrics长度超过限制。${model}模型最大允许${maxLyricsLength}字符。`,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
}
|
|
516
656
|
const requestBody = {
|
|
517
657
|
uploadUrl,
|
|
518
658
|
defaultParamFlag: true,
|
|
519
659
|
instrumental,
|
|
520
|
-
prompt,
|
|
521
660
|
style,
|
|
522
661
|
title,
|
|
523
662
|
continueAt,
|
|
524
663
|
model,
|
|
525
664
|
negativeTags,
|
|
526
|
-
callBackUrl: buildCallbackUrl("upload-extend"), // 必需的参数
|
|
665
|
+
callBackUrl: buildCallbackUrl("upload-extend", chatSessionId), // 必需的参数
|
|
527
666
|
};
|
|
667
|
+
// 根据Suno API文档,当instrumental=false且提供了lyrics时,使用lyrics作为prompt字段发送给API
|
|
668
|
+
if (!instrumental && lyrics) {
|
|
669
|
+
requestBody.prompt = lyrics;
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
requestBody.prompt = prompt;
|
|
673
|
+
}
|
|
528
674
|
const result = await makeApiRequest('/generate/upload-extend', 'POST', requestBody);
|
|
529
675
|
const taskData = {
|
|
530
676
|
taskId: result.data?.taskId || result.taskId || result.id || result.task_id || "unknown",
|
|
@@ -613,6 +759,12 @@ async function handleQueryProgress(input) {
|
|
|
613
759
|
status = 'complete';
|
|
614
760
|
if (status === 'running')
|
|
615
761
|
status = 'processing';
|
|
762
|
+
// 仅当存在最终音频链接(audioUrl 非空)才认为完成;
|
|
763
|
+
// 如果只有流式地址(streamAudioUrl)但 audioUrl 为空,则视为进行中
|
|
764
|
+
const hasFinalAudio = typeof result.audioUrl === 'string' && result.audioUrl.trim() !== '';
|
|
765
|
+
if (status === 'complete' && !hasFinalAudio) {
|
|
766
|
+
status = 'processing';
|
|
767
|
+
}
|
|
616
768
|
let statusMessage = "";
|
|
617
769
|
switch (status) {
|
|
618
770
|
case 'pending':
|
|
@@ -641,7 +793,7 @@ async function handleQueryProgress(input) {
|
|
|
641
793
|
status,
|
|
642
794
|
progress,
|
|
643
795
|
message: statusMessage,
|
|
644
|
-
result: status === 'complete' ? {
|
|
796
|
+
result: status === 'complete' && hasFinalAudio ? {
|
|
645
797
|
// 基本信息
|
|
646
798
|
taskId: result.taskId,
|
|
647
799
|
status: result.status,
|
|
@@ -811,14 +963,24 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
811
963
|
try {
|
|
812
964
|
const toolName = request.params.name;
|
|
813
965
|
const toolInput = request.params.arguments;
|
|
966
|
+
// 解析 meta.chatSessionId (如果存在)
|
|
967
|
+
const chatSessionId = request?.meta?.chatSessionId
|
|
968
|
+
?? request?.params?.meta?.chatSessionId
|
|
969
|
+
?? undefined;
|
|
970
|
+
if (chatSessionId) {
|
|
971
|
+
console.error(`接收到 chatSessionId: ${chatSessionId}`);
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
console.error("未在请求中检测到 chatSessionId(meta.chatSessionId)");
|
|
975
|
+
}
|
|
814
976
|
console.error(`收到工具调用请求: ${toolName}, 输入: ${JSON.stringify(toolInput)}`);
|
|
815
977
|
switch (toolName) {
|
|
816
978
|
case GENERATE_MUSIC_TOOL.name:
|
|
817
|
-
return await handleGenerateMusic(toolInput);
|
|
979
|
+
return await handleGenerateMusic(toolInput, chatSessionId);
|
|
818
980
|
case COVER_MUSIC_TOOL.name:
|
|
819
|
-
return await handleCoverMusic(toolInput);
|
|
981
|
+
return await handleCoverMusic(toolInput, chatSessionId);
|
|
820
982
|
case EXTEND_MUSIC_TOOL.name:
|
|
821
|
-
return await handleExtendMusic(toolInput);
|
|
983
|
+
return await handleExtendMusic(toolInput, chatSessionId);
|
|
822
984
|
case QUERY_PROGRESS_TOOL.name:
|
|
823
985
|
return await handleQueryProgress(toolInput);
|
|
824
986
|
default:
|
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mcp-ai-music",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "AI作曲MCP服务 - 基于Suno4.5 API的音乐生成、翻唱、扩展和进度查询工具",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "tsc",
|
|
8
|
-
"start": "node dist/index.js",
|
|
9
|
-
"dev": "ts-node src/index.ts",
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
-
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
"AI",
|
|
14
|
-
"music",
|
|
15
|
-
"composition",
|
|
16
|
-
"suno",
|
|
17
|
-
"mcp",
|
|
18
|
-
"audio",
|
|
19
|
-
"generation"
|
|
20
|
-
],
|
|
21
|
-
"author": "",
|
|
22
|
-
"license": "ISC",
|
|
23
|
-
"bin": {
|
|
24
|
-
"mcp-ai-music": "dist/index.js"
|
|
25
|
-
},
|
|
26
|
-
"files": [
|
|
27
|
-
"dist",
|
|
28
|
-
"README.md"
|
|
29
|
-
],
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
32
|
-
"node-fetch": "^2.7.0"
|
|
33
|
-
},
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@types/node": "^22.14.1",
|
|
36
|
-
"@types/node-fetch": "^2.6.12",
|
|
37
|
-
"typescript": "^5.8.3",
|
|
38
|
-
"ts-node": "^10.9.2"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-ai-music",
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "AI作曲MCP服务 - 基于Suno4.5 API的音乐生成、翻唱、扩展和进度查询工具",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"start": "node dist/index.js",
|
|
9
|
+
"dev": "ts-node src/index.ts",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"AI",
|
|
14
|
+
"music",
|
|
15
|
+
"composition",
|
|
16
|
+
"suno",
|
|
17
|
+
"mcp",
|
|
18
|
+
"audio",
|
|
19
|
+
"generation"
|
|
20
|
+
],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "ISC",
|
|
23
|
+
"bin": {
|
|
24
|
+
"mcp-ai-music": "dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
32
|
+
"node-fetch": "^2.7.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^22.14.1",
|
|
36
|
+
"@types/node-fetch": "^2.6.12",
|
|
37
|
+
"typescript": "^5.8.3",
|
|
38
|
+
"ts-node": "^10.9.2"
|
|
39
|
+
}
|
|
40
|
+
}
|