mcp-ai-music 1.0.8 → 1.0.10

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/dist/index.js CHANGED
@@ -89,7 +89,7 @@ const PROGRESS_QUERY_OUTPUT_SCHEMA = {
89
89
  // 工具定义
90
90
  const GENERATE_MUSIC_TOOL = {
91
91
  name: "generate_music",
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: 状态描述",
92
+ description: "生成音乐。根据提示词和风格生成原创音乐,支持自定义模式和纯音乐模式。\n\n注意:\n- 任务创建后,系统会自动处理,完成时会推送通知给用户,包含音频下载链接和详细信息。\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: 状态描述",
93
93
  inputSchema: {
94
94
  type: "object",
95
95
  properties: {
@@ -140,7 +140,7 @@ const GENERATE_MUSIC_TOOL = {
140
140
  };
141
141
  const COVER_MUSIC_TOOL = {
142
142
  name: "cover_music",
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: 状态描述",
143
+ description: "翻唱音乐。上传音频文件并转换为新的风格,保留核心旋律。\n\n注意:\n- 任务创建后,系统会自动处理,完成时会推送通知给用户,包含音频下载链接和详细信息。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n- 当 instrumental=false 时,可以同时提供 prompt(风格描述)和 lyrics(精确歌词)。\n\n返回字段说明:\n- taskId: 任务ID,用于备查\n- status: 任务状态 (pending)\n- message: 状态描述",
144
144
  inputSchema: {
145
145
  type: "object",
146
146
  properties: {
@@ -190,7 +190,7 @@ const COVER_MUSIC_TOOL = {
190
190
  };
191
191
  const EXTEND_MUSIC_TOOL = {
192
192
  name: "extend_music",
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: 状态描述",
193
+ description: "扩展音乐。在保留原始音频风格的同时扩展音轨长度。\n\n注意:\n- 任务创建后,系统会自动处理,完成时会推送通知给用户,包含音频下载链接和详细信息。\n- 平台仅保存音频与封面文件 15 天,请及时下载保存。\n- 当 instrumental=false 时,可以同时提供 prompt(扩展描述)和 lyrics(精确歌词)。\n\n返回字段说明:\n- taskId: 任务ID,用于备查\n- status: 任务状态 (pending)\n- message: 状态描述",
194
194
  inputSchema: {
195
195
  type: "object",
196
196
  properties: {
@@ -245,7 +245,7 @@ const EXTEND_MUSIC_TOOL = {
245
245
  };
246
246
  const QUERY_PROGRESS_TOOL = {
247
247
  name: "query_progress",
248
- description: "查询音乐生成进度。由于AI生成音乐需要时间,大模型需要轮询此接口来获取任务状态和结果。建议每10-30秒查询一次,直到状态为'complete'或'failed'。\n\n返回字段说明:\n- taskId: 任务ID\n- status: 任务状态 (pending/processing/complete/failed)\n- progress: 进度百分比\n- message: 状态描述\n- result: 完成时的详细结果,包含:\n • 基本信息:prompt(提示词)、modelName(模型)、title(标题)、tags(标签)、duration(时长秒数)\n • 音频链接:audioUrl(MP3下载)、streamAudioUrl(流媒体)、sourceAudioUrl(原始音频)\n • 图片链接:imageUrl(封面图)、sourceImageUrl(原始封面)\n • 其他:musicId(音乐ID)、taskCreateTime(创建时间)、callbackType(回调类型)",
248
+ description: "查询音乐生成进度(手动查询)。用于主动查看指定任务的当前状态和进度。\n\n注意:正常情况下无需主动调用此工具,因为任务完成后系统会自动推送通知。仅在需要实时了解任务进展或调试时使用。\n\n返回字段说明:\n- taskId: 任务ID\n- status: 任务状态 (pending/processing/complete/failed)\n- progress: 进度百分比\n- message: 状态描述\n- result: 完成时的详细结果,包含:\n • 基本信息:prompt(提示词)、modelName(模型)、title(标题)、tags(标签)、duration(时长秒数)\n • 音频链接:audioUrl(MP3下载)、streamAudioUrl(流媒体)、sourceAudioUrl(原始音频)\n • 图片链接:imageUrl(封面图)、sourceImageUrl(原始封面)\n • 其他:musicId(音乐ID)、taskCreateTime(创建时间)、callbackType(回调类型)",
249
249
  inputSchema: {
250
250
  type: "object",
251
251
  properties: {
@@ -481,17 +481,21 @@ async function handleGenerateMusic(input, chatSessionId) {
481
481
  else if (prompt) {
482
482
  requestBody.prompt = prompt;
483
483
  }
484
- const result = await makeApiRequest('/generate', 'POST', requestBody);
484
+ const result = await makeApiRequest("/generate", "POST", requestBody);
485
485
  const taskData = {
486
- taskId: result.data?.taskId || result.taskId || result.id || result.task_id || "unknown",
486
+ taskId: result.data?.taskId ||
487
+ result.taskId ||
488
+ result.id ||
489
+ result.task_id ||
490
+ "unknown",
487
491
  status: "pending",
488
- message: "音乐生成任务已提交,请使用query_progress工具查询进度。",
492
+ message: "音乐生成任务已提交后台运行,完成后会推送通知给用户",
489
493
  };
490
494
  return {
491
495
  content: [
492
496
  {
493
497
  type: "text",
494
- text: JSON.stringify([taskData])
498
+ text: JSON.stringify([taskData]),
495
499
  },
496
500
  ],
497
501
  isError: false,
@@ -511,22 +515,22 @@ async function handleGenerateMusic(input, chatSessionId) {
511
515
  // 翻唱音乐处理函数
512
516
  async function handleCoverMusic(input, chatSessionId) {
513
517
  try {
514
- if (!input || typeof input !== 'object') {
518
+ if (!input || typeof input !== "object") {
515
519
  return {
516
520
  content: [],
517
521
  isError: true,
518
522
  errorMessage: "输入参数格式错误,预期为包含uploadUrl和prompt字段的对象。",
519
523
  };
520
524
  }
521
- const { uploadUrl, prompt, lyrics = "", style = "", title = "", instrumental = false, model = "V4_5", negativeTags = "" } = input;
522
- if (!uploadUrl || typeof uploadUrl !== 'string') {
525
+ const { uploadUrl, prompt, lyrics = "", style = "", title = "", instrumental = false, model = "V4_5", negativeTags = "", } = input;
526
+ if (!uploadUrl || typeof uploadUrl !== "string") {
523
527
  return {
524
528
  content: [],
525
529
  isError: true,
526
530
  errorMessage: "uploadUrl字段是必需的,且必须为字符串类型。",
527
531
  };
528
532
  }
529
- if (!prompt || typeof prompt !== 'string') {
533
+ if (!prompt || typeof prompt !== "string") {
530
534
  return {
531
535
  content: [],
532
536
  isError: true,
@@ -572,9 +576,13 @@ async function handleCoverMusic(input, chatSessionId) {
572
576
  else {
573
577
  requestBody.prompt = prompt;
574
578
  }
575
- const result = await makeApiRequest('/generate/upload-cover', 'POST', requestBody);
579
+ const result = await makeApiRequest("/generate/upload-cover", "POST", requestBody);
576
580
  const taskData = {
577
- taskId: result.data?.taskId || result.taskId || result.id || result.task_id || "unknown",
581
+ taskId: result.data?.taskId ||
582
+ result.taskId ||
583
+ result.id ||
584
+ result.task_id ||
585
+ "unknown",
578
586
  status: "pending",
579
587
  message: "音乐翻唱任务已提交,请使用query_progress工具查询进度。",
580
588
  };
@@ -582,7 +590,7 @@ async function handleCoverMusic(input, chatSessionId) {
582
590
  content: [
583
591
  {
584
592
  type: "text",
585
- text: JSON.stringify([taskData])
593
+ text: JSON.stringify([taskData]),
586
594
  },
587
595
  ],
588
596
  isError: false,
@@ -602,29 +610,29 @@ async function handleCoverMusic(input, chatSessionId) {
602
610
  // 扩展音乐处理函数
603
611
  async function handleExtendMusic(input, chatSessionId) {
604
612
  try {
605
- if (!input || typeof input !== 'object') {
613
+ if (!input || typeof input !== "object") {
606
614
  return {
607
615
  content: [],
608
616
  isError: true,
609
617
  errorMessage: "输入参数格式错误,预期为包含uploadUrl、prompt和continueAt字段的对象。",
610
618
  };
611
619
  }
612
- const { uploadUrl, prompt, lyrics = "", style = "", title = "", continueAt, instrumental = false, model = "V4_5", negativeTags = "" } = input;
613
- if (!uploadUrl || typeof uploadUrl !== 'string') {
620
+ const { uploadUrl, prompt, lyrics = "", style = "", title = "", continueAt, instrumental = false, model = "V4_5", negativeTags = "", } = input;
621
+ if (!uploadUrl || typeof uploadUrl !== "string") {
614
622
  return {
615
623
  content: [],
616
624
  isError: true,
617
625
  errorMessage: "uploadUrl字段是必需的,且必须为字符串类型。",
618
626
  };
619
627
  }
620
- if (!prompt || typeof prompt !== 'string') {
628
+ if (!prompt || typeof prompt !== "string") {
621
629
  return {
622
630
  content: [],
623
631
  isError: true,
624
632
  errorMessage: "prompt字段是必需的,且必须为字符串类型。",
625
633
  };
626
634
  }
627
- if (typeof continueAt !== 'number' || continueAt <= 0) {
635
+ if (typeof continueAt !== "number" || continueAt <= 0) {
628
636
  return {
629
637
  content: [],
630
638
  isError: true,
@@ -671,9 +679,13 @@ async function handleExtendMusic(input, chatSessionId) {
671
679
  else {
672
680
  requestBody.prompt = prompt;
673
681
  }
674
- const result = await makeApiRequest('/generate/upload-extend', 'POST', requestBody);
682
+ const result = await makeApiRequest("/generate/upload-extend", "POST", requestBody);
675
683
  const taskData = {
676
- taskId: result.data?.taskId || result.taskId || result.id || result.task_id || "unknown",
684
+ taskId: result.data?.taskId ||
685
+ result.taskId ||
686
+ result.id ||
687
+ result.task_id ||
688
+ "unknown",
677
689
  status: "pending",
678
690
  message: "音乐扩展任务已提交,请使用query_progress工具查询进度。",
679
691
  };
@@ -681,7 +693,7 @@ async function handleExtendMusic(input, chatSessionId) {
681
693
  content: [
682
694
  {
683
695
  type: "text",
684
- text: JSON.stringify([taskData])
696
+ text: JSON.stringify([taskData]),
685
697
  },
686
698
  ],
687
699
  isError: false,
@@ -701,7 +713,7 @@ async function handleExtendMusic(input, chatSessionId) {
701
713
  // 查询进度处理函数
702
714
  async function handleQueryProgress(input) {
703
715
  try {
704
- if (!input || typeof input !== 'object') {
716
+ if (!input || typeof input !== "object") {
705
717
  return {
706
718
  content: [],
707
719
  isError: true,
@@ -709,7 +721,7 @@ async function handleQueryProgress(input) {
709
721
  };
710
722
  }
711
723
  const { taskId } = input;
712
- if (!taskId || typeof taskId !== 'string') {
724
+ if (!taskId || typeof taskId !== "string") {
713
725
  return {
714
726
  content: [],
715
727
  isError: true,
@@ -729,12 +741,12 @@ async function handleQueryProgress(input) {
729
741
  for (const ep of endpoints) {
730
742
  try {
731
743
  // 自有服务端点是完整URL,需要直接请求;官方端点仍走 API_BASE_URL
732
- if (ep.startsWith('http')) {
744
+ if (ep.startsWith("http")) {
733
745
  // 直接 fetch(沿用超时与头部设置)
734
- result = await makeApiRequestRaw(ep, 'GET');
746
+ result = await makeApiRequestRaw(ep, "GET");
735
747
  }
736
748
  else {
737
- result = await makeApiRequest(ep, 'GET');
749
+ result = await makeApiRequest(ep, "GET");
738
750
  }
739
751
  if (result)
740
752
  break;
@@ -752,41 +764,41 @@ async function handleQueryProgress(input) {
752
764
  result = result.data;
753
765
  }
754
766
  // 解析任务状态,兼容自定义服务端的状态值
755
- let status = result.status || 'unknown';
767
+ let status = result.status || "unknown";
756
768
  const progress = result.progress || 0;
757
769
  // 状态值映射,兼容自定义服务端
758
- if (status === 'completed')
759
- status = 'complete';
760
- if (status === 'running')
761
- status = 'processing';
770
+ if (status === "completed")
771
+ status = "complete";
772
+ if (status === "running")
773
+ status = "processing";
762
774
  // 仅当存在最终音频链接(audioUrl 非空)才认为完成;
763
775
  // 如果只有流式地址(streamAudioUrl)但 audioUrl 为空,则视为进行中
764
- const hasFinalAudio = typeof result.audioUrl === 'string' && result.audioUrl.trim() !== '';
765
- if (status === 'complete' && !hasFinalAudio) {
766
- status = 'pending';
776
+ const hasFinalAudio = typeof result.audioUrl === "string" && result.audioUrl.trim() !== "";
777
+ if (status === "complete" && !hasFinalAudio) {
778
+ status = "pending";
767
779
  }
768
780
  // 进行中统一返回 pending
769
- if (status === 'processing' || status === 'text' || status === 'first') {
770
- status = 'pending';
781
+ if (status === "processing" || status === "text" || status === "first") {
782
+ status = "pending";
771
783
  }
772
784
  let statusMessage = "";
773
785
  switch (status) {
774
- case 'pending':
786
+ case "pending":
775
787
  statusMessage = "正在处理中...";
776
788
  break;
777
- case 'processing':
789
+ case "processing":
778
790
  statusMessage = "正在处理中...";
779
791
  break;
780
- case 'text':
792
+ case "text":
781
793
  statusMessage = "文本生成完成,正在生成音频...";
782
794
  break;
783
- case 'first':
795
+ case "first":
784
796
  statusMessage = "第一首音乐生成完成...";
785
797
  break;
786
- case 'complete':
798
+ case "complete":
787
799
  statusMessage = "任务完成!";
788
800
  break;
789
- case 'failed':
801
+ case "failed":
790
802
  statusMessage = "任务失败";
791
803
  break;
792
804
  default:
@@ -797,37 +809,41 @@ async function handleQueryProgress(input) {
797
809
  status,
798
810
  progress,
799
811
  message: statusMessage,
800
- result: status === 'complete' && hasFinalAudio ? {
801
- // 基本信息
802
- taskId: result.taskId,
803
- status: result.status,
804
- prompt: result.prompt,
805
- modelName: result.modelName,
806
- title: result.title,
807
- tags: result.tags,
808
- duration: result.duration,
809
- callbackType: result.callbackType,
810
- taskCreateTime: result.taskCreateTime,
811
- // 音频链接
812
- audioUrl: result.audioUrl,
813
- sourceAudioUrl: result.sourceAudioUrl,
814
- streamAudioUrl: result.streamAudioUrl,
815
- sourceStreamAudioUrl: result.sourceStreamAudioUrl,
816
- // 图片链接
817
- imageUrl: result.imageUrl,
818
- sourceImageUrl: result.sourceImageUrl,
819
- // 其他信息
820
- musicId: result.musicId,
821
- userUuid: result.userUuid,
822
- errorMessage: result.errorMessage,
823
- // 原始完整数据
824
- rawData: result
825
- } : null,
812
+ result: status === "complete" && hasFinalAudio
813
+ ? {
814
+ // 基本信息
815
+ taskId: result.taskId,
816
+ status: result.status,
817
+ prompt: result.prompt,
818
+ modelName: result.modelName,
819
+ title: result.title,
820
+ tags: result.tags,
821
+ duration: result.duration,
822
+ callbackType: result.callbackType,
823
+ taskCreateTime: result.taskCreateTime,
824
+ // 音频链接
825
+ audioUrl: result.audioUrl,
826
+ sourceAudioUrl: result.sourceAudioUrl,
827
+ streamAudioUrl: result.streamAudioUrl,
828
+ sourceStreamAudioUrl: result.sourceStreamAudioUrl,
829
+ // 图片链接
830
+ imageUrl: result.imageUrl,
831
+ sourceImageUrl: result.sourceImageUrl,
832
+ // 其他信息
833
+ musicId: result.musicId,
834
+ userUuid: result.userUuid,
835
+ errorMessage: result.errorMessage,
836
+ // 原始完整数据
837
+ rawData: result,
838
+ }
839
+ : null,
826
840
  };
827
841
  // 如果任务失败,按要求返回格式化数据,并透传后端错误信息到顶层 errorMessage
828
- if (status === 'failed') {
842
+ if (status === "failed") {
829
843
  const failureReason = result.errorMessage || result.msg || "";
830
- const messageText = failureReason ? `任务失败: ${failureReason}` : "任务失败";
844
+ const messageText = failureReason
845
+ ? `任务失败: ${failureReason}`
846
+ : "任务失败";
831
847
  return {
832
848
  content: [
833
849
  {
@@ -857,13 +873,15 @@ async function handleQueryProgress(input) {
857
873
  content: [
858
874
  {
859
875
  type: "text",
860
- text: JSON.stringify([{
876
+ text: JSON.stringify([
877
+ {
861
878
  taskId,
862
879
  status: "pending",
863
880
  progress: 0,
864
881
  message: "任务正在处理中,请稍后再查询...",
865
882
  result: null,
866
- }])
883
+ },
884
+ ]),
867
885
  },
868
886
  ],
869
887
  isError: false,
@@ -873,7 +891,7 @@ async function handleQueryProgress(input) {
873
891
  return {
874
892
  content: [],
875
893
  isError: true,
876
- errorMessage: `查询失败: ${result.msg || '未知错误'}`,
894
+ errorMessage: `查询失败: ${result.msg || "未知错误"}`,
877
895
  };
878
896
  }
879
897
  // 如果成功但data中有错误信息,也要处理
@@ -884,13 +902,15 @@ async function handleQueryProgress(input) {
884
902
  content: [
885
903
  {
886
904
  type: "text",
887
- text: JSON.stringify([{
905
+ text: JSON.stringify([
906
+ {
888
907
  taskId,
889
908
  status: "pending",
890
909
  progress: 0,
891
910
  message: "任务正在处理中,请稍后再查询...",
892
911
  result: null,
893
- }])
912
+ },
913
+ ]),
894
914
  },
895
915
  ],
896
916
  isError: false,
@@ -900,7 +920,7 @@ async function handleQueryProgress(input) {
900
920
  return {
901
921
  content: [],
902
922
  isError: true,
903
- errorMessage: `查询失败: ${result.data.error} - ${result.data.message || ''}`,
923
+ errorMessage: `查询失败: ${result.data.error} - ${result.data.message || ""}`,
904
924
  };
905
925
  }
906
926
  // 如果自定义服务端返回成功但没有实际数据,说明任务不存在或还在处理中
@@ -909,13 +929,15 @@ async function handleQueryProgress(input) {
909
929
  content: [
910
930
  {
911
931
  type: "text",
912
- text: JSON.stringify([{
932
+ text: JSON.stringify([
933
+ {
913
934
  taskId,
914
935
  status: "pending",
915
936
  progress: 0,
916
937
  message: "任务正在处理中,请稍后再查询...",
917
938
  result: null,
918
- }])
939
+ },
940
+ ]),
919
941
  },
920
942
  ],
921
943
  isError: false,
@@ -929,7 +951,7 @@ async function handleQueryProgress(input) {
929
951
  content: [
930
952
  {
931
953
  type: "text",
932
- text: JSON.stringify([responseData])
954
+ text: JSON.stringify([responseData]),
933
955
  },
934
956
  ],
935
957
  isError: false,
@@ -950,7 +972,7 @@ async function handleQueryProgress(input) {
950
972
  const server = new index_js_1.Server({
951
973
  name: "mcp-ai-music",
952
974
  version: "1.0.0",
953
- description: "AI作曲MCP服务 - 基于Suno4.5 API的音乐生成、翻唱、扩展和进度查询工具"
975
+ description: "AI作曲MCP服务 - 基于Suno4.5 API的音乐生成、翻唱、扩展和进度查询工具",
954
976
  }, {
955
977
  capabilities: {
956
978
  tools: TOOLS.reduce((acc, tool) => {
@@ -968,9 +990,9 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
968
990
  const toolName = request.params.name;
969
991
  const toolInput = request.params.arguments;
970
992
  // 解析 meta.chatSessionId (如果存在)
971
- const chatSessionId = request?.meta?.chatSessionId
972
- ?? request?.params?.meta?.chatSessionId
973
- ?? undefined;
993
+ const chatSessionId = request?.meta?.chatSessionId ??
994
+ request?.params?.meta?.chatSessionId ??
995
+ undefined;
974
996
  if (chatSessionId) {
975
997
  console.error(`接收到 chatSessionId: ${chatSessionId}`);
976
998
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-ai-music",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "AI作曲MCP服务 - 基于Suno4.5 API的音乐生成、翻唱、扩展和进度查询工具",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {