Skip to content

Commit a206549

Browse files
sync: ci: unify all workflow secrets to BOT_TOKEN
Synced from monorepo directory: suno
1 parent 410d179 commit a206549

8 files changed

Lines changed: 292 additions & 45 deletions

File tree

.github/workflows/publish.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,3 +386,30 @@ jobs:
386386
fi
387387
exit 1
388388
}
389+
390+
publish-dockerhub:
391+
name: Publish to Docker Hub
392+
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
393+
needs: [build]
394+
runs-on: ubuntu-latest
395+
steps:
396+
- uses: actions/checkout@v6
397+
398+
- name: Set up Docker Buildx
399+
uses: docker/setup-buildx-action@v3
400+
401+
- name: Login to Docker Hub
402+
uses: docker/login-action@v3
403+
with:
404+
username: ${{ secrets.DOCKERHUB_USERNAME }}
405+
password: ${{ secrets.DOCKERHUB_TOKEN }}
406+
407+
- name: Build and push
408+
uses: docker/build-push-action@v6
409+
with:
410+
context: .
411+
push: true
412+
tags: |
413+
acedatacloud/mcp-suno:${{ needs.build.outputs.version }}
414+
acedatacloud/mcp-suno:latest
415+
platforms: linux/amd64,linux/arm64

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ Add to your VS Code MCP config (`.vscode/mcp.json`):
134134
}
135135
```
136136

137-
Or install the [Ace Data Cloud MCP extension](https://marketplace.visualstudio.com/items?itemName=acedatacloud.acedatacloud-mcp) for VS Code, which bundles all 11 MCP servers with one-click setup.
137+
Or install the [Ace Data Cloud MCP extension](https://marketplace.visualstudio.com/items?itemName=acedatacloud.acedatacloud-mcp) for VS Code, which bundles all 15 MCP servers with one-click setup.
138138

139139
#### JetBrains IDEs
140140

@@ -472,8 +472,8 @@ Options:
472472

473473
```bash
474474
# Clone repository
475-
git clone https://github.com/AceDataCloud/mcp-suno.git
476-
cd mcp-suno
475+
git clone https://github.com/AceDataCloud/SunoMCP.git
476+
cd SunoMCP
477477

478478
# Create virtual environment
479479
python -m venv .venv

core/oauth.py

Lines changed: 200 additions & 23 deletions
Large diffs are not rendered by default.

core/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
# Initialize FastMCP server
4343
mcp = FastMCP(
4444
settings.server_name,
45-
icons=[Icon(src="https://cdn.acedata.cloud/l3ffw7.jpg")],
45+
icons=[Icon(src="https://cdn.acedata.cloud/l3ffw7.jpg", mimeType="image/jpeg")],
4646
**mcp_kwargs,
4747
)
4848

core/utils.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,31 @@ def _with_task_guidance(
3131
if not task_id:
3232
return payload
3333

34-
payload["mcp_task_polling"] = {
35-
"task_id": task_id,
36-
"poll_tool": poll_tool,
37-
"batch_poll_tool": batch_poll_tool,
38-
"next_step": f'If the task is still pending or processing, call {poll_tool}(task_id="{task_id}") again later.',
39-
}
34+
# Determine task state for explicit guidance
35+
state = payload.get("state", "")
36+
response = payload.get("response", {})
37+
success = response.get("success", False) if isinstance(response, dict) else False
38+
39+
if state == "complete" and success:
40+
payload["mcp_task_polling"] = {
41+
"task_id": task_id,
42+
"state": state,
43+
"is_complete": True,
44+
"note": "Task is complete. The audio URLs are final and ready to present to the user.",
45+
}
46+
else:
47+
payload["mcp_task_polling"] = {
48+
"task_id": task_id,
49+
"poll_tool": poll_tool,
50+
"batch_poll_tool": batch_poll_tool,
51+
"state": state,
52+
"is_complete": False,
53+
"next_step": f'Task is NOT complete yet (state: "{state}"). '
54+
f'IMPORTANT: Only state="complete" with success=true means the task is finished. '
55+
f"Ignore any intermediate audio_url values (e.g. audiopipe.suno.ai URLs) — "
56+
f"these are streaming previews, NOT final results. "
57+
f'Call {poll_tool}(task_id="{task_id}") again after a few seconds.',
58+
}
4059
return payload
4160

4261

prompts/__init__.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ def suno_music_generation_guide() -> str:
6565
## Important Notes:
6666
1. Music generation is async in MCP - generation tools should return quickly with a task_id
6767
2. After any generate/extend/cover/remaster/stems/media conversion call, use `suno_get_task` to poll for the final result
68-
2. Default model is chirp-v4-5 (good balance of quality and speed)
69-
3. For longest songs (8 min), use chirp-v5 or chirp-v4-5-plus
70-
4. Vocal gender only works on v4.5+ models
68+
3. **CRITICAL POLLING RULE:** You MUST check the `state` field in the response — only `state: "complete"` with `success: true` means the task is done. During the `pending` state, the API may return intermediate `audio_url` values (e.g. audiopipe.suno.ai streaming URLs). These are NOT final results. Do NOT stop polling just because `audio_url` is non-empty — always check `state` first.
69+
4. Default model is chirp-v5-5 (good balance of quality and speed)
70+
5. For longest songs (8 min), use chirp-v5 or chirp-v4-5-plus
71+
6. Vocal gender only works on v4.5+ models
7172
"""
7273

7374

@@ -80,13 +81,13 @@ def suno_workflow_examples() -> str:
8081
1. User: "Make me a rock song about freedom"
8182
2. Call `suno_generate_music(prompt="Rock song about freedom, electric guitars, powerful drums, anthemic")`
8283
3. Return the task_id from the submission response
83-
4. Poll with `suno_get_task(task_id)` until the task finishes and audio URLs appear
84+
4. Poll with `suno_get_task(task_id)` — check the `state` field. Only stop when `state` is `"complete"` and `success` is `true`. Ignore any intermediate `audio_url` during `pending` state.
8485
8586
## Workflow 2: Custom Song with User's Lyrics
8687
1. User provides lyrics
8788
2. Ask for title and style preferences if not provided
8889
3. Call `suno_generate_custom_music(lyric=user_lyrics, title="...", style="...")`
89-
4. Poll with `suno_get_task(task_id)` for the completed audio
90+
4. Poll with `suno_get_task(task_id)` until `state` is `"complete"` (not just until `audio_url` appears)
9091
9192
## Workflow 3: Creating a Long Song (>4 minutes)
9293
1. Generate initial song with `suno_generate_music`
@@ -104,7 +105,7 @@ def suno_workflow_examples() -> str:
104105
1. User has a song_id they want to remix
105106
2. User describes the new style
106107
3. Call `suno_cover_music(audio_id, prompt="jazz version", style="smooth jazz, saxophone")`
107-
4. Poll with `suno_get_task(task_id)` for the completed cover
108+
4. Poll with `suno_get_task(task_id)` until `state` is `"complete"`
108109
109110
## Tips:
110111
- Always be descriptive in prompts - include genre, mood, instruments, tempo

server.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
"url": "https://suno.mcp.acedata.cloud/mcp"
3333
}
3434
],
35-
"websiteUrl": "https://platform.acedata.cloud"
35+
"websiteUrl": "https://suno.mcp.acedata.cloud"
3636
}

tools/task_tools.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,17 @@ async def suno_get_task(
2929
- You want to get the full details of a generated song
3030
3131
Task states:
32-
- 'pending': Generation is still in progress
33-
- 'complete': Generation finished successfully
32+
- 'pending': Generation is still in progress — KEEP POLLING
33+
- 'processing': Generation is being processed — KEEP POLLING
34+
- 'complete': Generation finished successfully — this is the ONLY state that means done
3435
- 'failed': Generation failed (check error message)
3536
37+
CRITICAL: During the 'pending' state, the response may already contain intermediate
38+
audio_url values (e.g. URLs from audiopipe.suno.ai). These are STREAMING PREVIEW URLs,
39+
NOT final results. You MUST check the 'state' field — only present the results to the
40+
user when state is 'complete' and success is true. Do NOT stop polling just because
41+
audio_url is non-empty.
42+
3643
Returns:
3744
Task status and generated audio information including URLs, title, lyrics, and duration.
3845
"""
@@ -60,6 +67,10 @@ async def suno_get_tasks_batch(
6067
- You want to get status of several songs at once
6168
- You're tracking a batch of generations
6269
70+
CRITICAL: Same as suno_get_task — only consider a task complete when its state
71+
is 'complete' and success is true. Intermediate audio_url values during 'pending'
72+
state are streaming previews, NOT final results.
73+
6374
Returns:
6475
Status and audio information for all queried tasks.
6576
"""
@@ -76,16 +87,28 @@ async def suno_get_tasks_batch(
7687

7788
for item in result.get("items", []):
7889
response_info = item.get("response", {})
90+
state = item.get("state", "unknown")
91+
success = response_info.get("success", False)
7992
lines.extend(
8093
[
8194
f"=== Task: {item.get('id', 'N/A')} ===",
95+
f"State: {state}",
8296
f"Created At: {item.get('created_at', 'N/A')}",
83-
f"Success: {response_info.get('success', False)}",
97+
f"Success: {success}",
8498
]
8599
)
86100

87-
for audio in response_info.get("data", []):
88-
lines.append(f" - {audio.get('title', 'Untitled')}: {audio.get('audio_url', 'N/A')}")
101+
if state == "complete" and success:
102+
for audio in response_info.get("data", []):
103+
lines.append(
104+
f" - {audio.get('title', 'Untitled')}: {audio.get('audio_url', 'N/A')}"
105+
)
106+
elif state == "failed":
107+
lines.append(f" Error: {response_info.get('error', 'Unknown error')}")
108+
else:
109+
lines.append(
110+
f" ⏳ Still {state} — keep polling. Any audio_url values are intermediate previews."
111+
)
89112

90113
lines.append("")
91114

0 commit comments

Comments
 (0)