@kolbo/mcp 1.25.0 → 1.26.0
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 +11 -0
- package/package.json +1 -1
- package/skill/SKILL.md +2 -0
- package/skill/references/workflows/music-library.md +32 -0
- package/src/index.js +2 -0
- package/src/tools/music_library.js +179 -0
package/README.md
CHANGED
|
@@ -190,6 +190,17 @@ Every generation tool also accepts an optional `project_id` arg that routes the
|
|
|
190
190
|
|------|-------------|
|
|
191
191
|
| `publish_html_artifact` | Publish an HTML page, SVG, or Mermaid diagram and get a public shareable URL on `sites.kolbo.ai`. Pass `share_token` from a prior publish to update the same URL in place (old content kept in version history). |
|
|
192
192
|
|
|
193
|
+
**Music Library** (stock / production music)
|
|
194
|
+
| Tool | Description |
|
|
195
|
+
|------|-------------|
|
|
196
|
+
| `search_music_library` | Search the licensed stock-music catalog by keyword + genre/mood/BPM/duration filters. Find a ready-made track (distinct from `generate_music`, which composes a new song). |
|
|
197
|
+
| `analyze_script_for_music` | AI: turn a video/voiceover script into a music search (`query`, `mood`, `genre`, `keywords`). |
|
|
198
|
+
| `browse_music_library` | Browse the catalog without a query (paginated). |
|
|
199
|
+
| `get_music_library_facets` | List available genres, moods, instruments + BPM/duration ranges. |
|
|
200
|
+
| `get_music_track_audio` | Get a track's downloadable 128/320/WAV URLs by id. |
|
|
201
|
+
| `get_music_track_related` | Get stems + alternate versions of a track. |
|
|
202
|
+
| `get_music_track_lyrics` | Get lyrics text, theme, and explicit flag for a track. |
|
|
203
|
+
|
|
193
204
|
**Discovery & Account**
|
|
194
205
|
| Tool | Description |
|
|
195
206
|
|------|-------------|
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -49,6 +49,7 @@ If the user is on a whitelabel build (`sapir`, etc.), they must use their brande
|
|
|
49
49
|
| Generate a **Veo 3 / 3.1** video | `references/models/veo.md` |
|
|
50
50
|
| Build a **multi-scene set** (Creative Director, storyboard, campaign batch, 4+ angles) | `references/models/creative-director.md` |
|
|
51
51
|
| Generate **music** (Suno, song, lyrics, jingle, score) | `references/models/music.md` |
|
|
52
|
+
| Find an **existing / stock / library / royalty-free track** to score a video, ad, or voiceover | `references/workflows/music-library.md` |
|
|
52
53
|
| Build an **HTML presentation / slide deck** | `references/models/html-presentation.md` |
|
|
53
54
|
| Build a **landing page / marketing site** | `references/models/landing-page.md` |
|
|
54
55
|
| Build a **dashboard / data viz / interactive widget / mini-game / UI mockup** | `references/models/visual-code.md` |
|
|
@@ -95,6 +96,7 @@ Each `references/models/*.md` mirrors the matching skill prompt in `kolbo-api/sr
|
|
|
95
96
|
| `create_visual_dna` / `list_visual_dnas` / `get_visual_dna` / `delete_visual_dna` | Visual DNA — see `workflows/visual-dna.md` |
|
|
96
97
|
| `list_moodboards` / `get_moodboard` / `list_presets` | Style overlays |
|
|
97
98
|
| `chat_send_message` / `chat_list_conversations` / `chat_get_messages` | Kolbo chat with optional `media_urls` (up to 10 per call) |
|
|
99
|
+
| `search_music_library` / `analyze_script_for_music` / `browse_music_library` / `get_music_library_facets` / `get_music_track_audio` / `get_music_track_related` / `get_music_track_lyrics` | **Stock / production music library** — find a licensed ready-made track (NOT `generate_music`, which composes a new song). See `workflows/music-library.md` |
|
|
98
100
|
| `app_builder_*` (9 tools) | Full React app generation — see `workflows/app-builder.md` |
|
|
99
101
|
| `publish_html_artifact` | Publish HTML / SVG / Mermaid to `sites.kolbo.ai`. Server dedupes by content hash. Strict CSP. |
|
|
100
102
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Music Library (stock / production music)
|
|
2
|
+
|
|
3
|
+
The music library is Kolbo's catalog of **licensed, ready-made background tracks**. Use it to score a video, ad, or voiceover with an existing track.
|
|
4
|
+
|
|
5
|
+
> **Library vs generation.** `search_music_library` finds an existing track. `generate_music` composes a brand-new song with Suno. If the user wants "a track for my ad", reach for the library first — it's free (no credits) and instant. Use `generate_music` only when they want something original/custom.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Use |
|
|
10
|
+
|------|-----|
|
|
11
|
+
| `search_music_library` | Keyword search + filters (genre, mood, bpm, duration, has_stems, has_lyrics) + sort. Returns tracks with id, title, artist, duration, BPM, key, genres, moods, preview URL. |
|
|
12
|
+
| `analyze_script_for_music` | Turn a script/scene description into `{ query, mood, genre, keywords }`. |
|
|
13
|
+
| `browse_music_library` | Paginated browse with no query. |
|
|
14
|
+
| `get_music_library_facets` | Valid genres, moods, instruments + BPM/duration ranges. |
|
|
15
|
+
| `get_music_track_audio` | A track's downloadable 128 / 320 / WAV URLs. |
|
|
16
|
+
| `get_music_track_related` | Stems + alternate versions of a master track. |
|
|
17
|
+
| `get_music_track_lyrics` | Lyrics text, theme, explicit flag. |
|
|
18
|
+
|
|
19
|
+
All tools are **free** (no credits) and read-only.
|
|
20
|
+
|
|
21
|
+
## Typical flow
|
|
22
|
+
|
|
23
|
+
1. **From a script** → call `analyze_script_for_music` to derive `query` / `mood` / `genre`.
|
|
24
|
+
2. **Search** → `search_music_library` with that query (+ optional filters). Show the user the top matches by title + vibe; include the `preview` URL so they can listen.
|
|
25
|
+
3. **Pick** → once the user chooses, call `get_music_track_audio` with the track `id` to get the final downloadable URLs (offer WAV for editing, 320 for delivery).
|
|
26
|
+
4. Optionally `get_music_track_related` for an instrumental/stems cut, or `get_music_track_lyrics` if it's a vocal track.
|
|
27
|
+
|
|
28
|
+
## Tips
|
|
29
|
+
|
|
30
|
+
- Don't dump every field — surface title, artist, duration, BPM, mood, and the preview link.
|
|
31
|
+
- If a filtered search returns nothing, call `get_music_library_facets` to use exact valid genre/mood values, then retry.
|
|
32
|
+
- `sort` options: `duration-asc`, `duration-desc`, `bpm-asc`, `bpm-desc`, `title`. Omit for relevance order.
|
package/src/index.js
CHANGED
|
@@ -70,6 +70,7 @@ const { registerAppBuilderTools } = require('./tools/app_builder');
|
|
|
70
70
|
const { registerArtifactTools } = require('./tools/artifacts');
|
|
71
71
|
const { registerProjectTools } = require('./tools/projects');
|
|
72
72
|
const { registerVoiceTools } = require('./tools/voices');
|
|
73
|
+
const { registerMusicLibraryTools } = require('./tools/music_library');
|
|
73
74
|
|
|
74
75
|
/**
|
|
75
76
|
* Build a fully-configured Kolbo MCP server (all tool groups registered)
|
|
@@ -105,6 +106,7 @@ function createServer(opts = {}) {
|
|
|
105
106
|
registerAppBuilderTools(server, client);
|
|
106
107
|
registerArtifactTools(server, client);
|
|
107
108
|
registerProjectTools(server, client);
|
|
109
|
+
registerMusicLibraryTools(server, client);
|
|
108
110
|
|
|
109
111
|
return server;
|
|
110
112
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/* ⛔ BACKWARD COMPATIBILITY: Tool names and arg names below are a PUBLIC
|
|
2
|
+
* CONTRACT. Never rename, remove, or break an existing tool/arg. Full rules: ../index.js top-of-file. */
|
|
3
|
+
|
|
4
|
+
const { z } = require('zod');
|
|
5
|
+
|
|
6
|
+
// Format a normalized track into a compact human-readable line.
|
|
7
|
+
function trackLine(t) {
|
|
8
|
+
const meta = [
|
|
9
|
+
t.durationSeconds != null ? `${Math.round(t.durationSeconds)}s` : null,
|
|
10
|
+
t.bpm != null ? `${t.bpm} BPM` : null,
|
|
11
|
+
t.musicalKey || null,
|
|
12
|
+
Array.isArray(t.genres) && t.genres.length ? t.genres.join('/') : t.genre,
|
|
13
|
+
Array.isArray(t.moodTags) && t.moodTags.length ? t.moodTags.slice(0, 3).join(', ') : null,
|
|
14
|
+
].filter(Boolean).join(' · ');
|
|
15
|
+
const flags = [t.hasStems ? 'stems' : null, t.hasLyrics ? 'lyrics' : null].filter(Boolean).join(', ');
|
|
16
|
+
return `${t.id} — ${t.title}${t.artist ? ` by ${t.artist}` : ''}\n ${meta}${flags ? ` [${flags}]` : ''}${t.audioUrl ? `\n preview: ${t.audioUrl}` : ''}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function registerMusicLibraryTools(server, client) {
|
|
20
|
+
// ─── search_music_library ─────────────────────────────────────
|
|
21
|
+
server.tool(
|
|
22
|
+
'search_music_library',
|
|
23
|
+
'Search the Kolbo stock / production music library (licensed background tracks) by keyword with optional filters. Use this to FIND an existing ready-made track to score a video, ad, or voiceover — distinct from generate_music, which composes a brand-new song with Suno. Returns matching tracks with id, title, artist, duration, BPM, key, genres, moods, and preview/download URLs (128/320/wav). To turn a script into a good query first, call analyze_script_for_music.',
|
|
24
|
+
{
|
|
25
|
+
query: z.string().max(200).optional().describe('Keyword search, e.g. "uplifting corporate", "tense cinematic", "lofi hip hop". If omitted, falls back to the mood/genre filter as the search term.'),
|
|
26
|
+
mood: z.string().optional().describe('Mood filter, e.g. "Emotional", "Energetic", "Tense". Use get_music_library_facets to see valid values.'),
|
|
27
|
+
genre: z.string().optional().describe('Genre filter, e.g. "Soundtrack", "Corporate", "Hip Hop". Use get_music_library_facets to see valid values.'),
|
|
28
|
+
bpmMin: z.number().optional().describe('Minimum beats-per-minute.'),
|
|
29
|
+
bpmMax: z.number().optional().describe('Maximum beats-per-minute.'),
|
|
30
|
+
durationMin: z.number().optional().describe('Minimum track duration in seconds.'),
|
|
31
|
+
durationMax: z.number().optional().describe('Maximum track duration in seconds.'),
|
|
32
|
+
hasStems: z.boolean().optional().describe('Only return tracks that have separated stems.'),
|
|
33
|
+
hasLyrics: z.boolean().optional().describe('Only return tracks that have lyrics.'),
|
|
34
|
+
sort: z.enum(['duration-asc', 'duration-desc', 'bpm-asc', 'bpm-desc', 'title']).optional().describe('Optional sort order. Omit for relevance order.'),
|
|
35
|
+
limit: z.number().int().min(1).max(40).optional().describe('Results per page (max 40, default 20).'),
|
|
36
|
+
offset: z.number().int().min(0).optional().describe('Pagination offset for loading more results.')
|
|
37
|
+
},
|
|
38
|
+
async (args) => {
|
|
39
|
+
const result = await client.post('/v1/music-library/search', args);
|
|
40
|
+
const tracks = result.tracks || [];
|
|
41
|
+
if (tracks.length === 0) {
|
|
42
|
+
return { content: [{ type: 'text', text: 'No tracks found matching those filters. Try a broader query or call get_music_library_facets for valid genres/moods.' }] };
|
|
43
|
+
}
|
|
44
|
+
const head = `Found ${tracks.length} track${tracks.length === 1 ? '' : 's'}${result.total ? ` (of ${result.total} sorted)` : ''}:`;
|
|
45
|
+
return { content: [{ type: 'text', text: `${head}\n\n${tracks.map(trackLine).join('\n\n')}\n\nUse the track id with get_music_track_audio to get the downloadable 128/320/wav URLs.` }] };
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// ─── analyze_script_for_music ─────────────────────────────────
|
|
50
|
+
server.tool(
|
|
51
|
+
'analyze_script_for_music',
|
|
52
|
+
'AI helper that turns a video or voiceover script into a music search. Returns { query, mood, genre, keywords } you can pass straight into search_music_library to find a fitting background track. Use this first when the user gives you a script/scene description rather than explicit music keywords.',
|
|
53
|
+
{
|
|
54
|
+
script: z.string().min(1).describe('The video or voiceover script / scene description to analyze (up to ~8000 chars).')
|
|
55
|
+
},
|
|
56
|
+
async ({ script }) => {
|
|
57
|
+
const result = await client.post('/v1/music-library/analyze-script', { script });
|
|
58
|
+
return {
|
|
59
|
+
content: [{
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: JSON.stringify({
|
|
62
|
+
query: result.query,
|
|
63
|
+
mood: result.mood,
|
|
64
|
+
genre: result.genre,
|
|
65
|
+
keywords: result.keywords,
|
|
66
|
+
_followup_hint: 'Pass query + mood + genre into search_music_library.'
|
|
67
|
+
}, null, 2)
|
|
68
|
+
}]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// ─── browse_music_library ─────────────────────────────────────
|
|
74
|
+
server.tool(
|
|
75
|
+
'browse_music_library',
|
|
76
|
+
'Browse the music library catalog without a search query (stable paginated listing). Use when the user just wants to see what is available. For a targeted search use search_music_library instead.',
|
|
77
|
+
{
|
|
78
|
+
sort: z.enum(['duration-asc', 'duration-desc', 'bpm-asc', 'bpm-desc', 'title']).optional().describe('Optional sort order.'),
|
|
79
|
+
limit: z.number().int().min(1).max(50).optional().describe('Results per page (max 50, default 50).'),
|
|
80
|
+
offset: z.number().int().min(0).optional().describe('Pagination offset for loading more results.')
|
|
81
|
+
},
|
|
82
|
+
async ({ sort, limit, offset }) => {
|
|
83
|
+
const params = new URLSearchParams();
|
|
84
|
+
if (sort) params.set('sort', sort);
|
|
85
|
+
if (limit != null) params.set('limit', String(limit));
|
|
86
|
+
if (offset != null) params.set('offset', String(offset));
|
|
87
|
+
const path = `/v1/music-library/catalog${params.toString() ? '?' + params.toString() : ''}`;
|
|
88
|
+
const result = await client.get(path);
|
|
89
|
+
const tracks = result.tracks || [];
|
|
90
|
+
if (tracks.length === 0) {
|
|
91
|
+
return { content: [{ type: 'text', text: 'No tracks returned.' }] };
|
|
92
|
+
}
|
|
93
|
+
return { content: [{ type: 'text', text: `Catalog (${tracks.length} track${tracks.length === 1 ? '' : 's'}):\n\n${tracks.map(trackLine).join('\n\n')}` }] };
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// ─── get_music_library_facets ─────────────────────────────────
|
|
98
|
+
server.tool(
|
|
99
|
+
'get_music_library_facets',
|
|
100
|
+
'List the distinct genres, moods, and instruments available in the music library, plus the BPM and duration ranges. Use these values to build precise search_music_library filters.',
|
|
101
|
+
{},
|
|
102
|
+
async () => {
|
|
103
|
+
const result = await client.get('/v1/music-library/facets');
|
|
104
|
+
return {
|
|
105
|
+
content: [{
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: JSON.stringify({
|
|
108
|
+
genres: result.genres || [],
|
|
109
|
+
moods: result.moods || [],
|
|
110
|
+
instruments: result.instruments || [],
|
|
111
|
+
bpmRange: result.bpmRange || null,
|
|
112
|
+
durationRange: result.durationRange || null
|
|
113
|
+
}, null, 2)
|
|
114
|
+
}]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// ─── get_music_track_audio ────────────────────────────────────
|
|
120
|
+
server.tool(
|
|
121
|
+
'get_music_track_audio',
|
|
122
|
+
'Get the downloadable audio URLs (128 kbps / 320 kbps / WAV) for a single music-library track by id. Call this after the user picks a track from search_music_library or browse_music_library.',
|
|
123
|
+
{
|
|
124
|
+
track_id: z.string().describe('The track id returned by search_music_library / browse_music_library.')
|
|
125
|
+
},
|
|
126
|
+
async ({ track_id }) => {
|
|
127
|
+
const result = await client.get(`/v1/music-library/track/${encodeURIComponent(track_id)}/audio`);
|
|
128
|
+
return {
|
|
129
|
+
content: [{
|
|
130
|
+
type: 'text',
|
|
131
|
+
text: JSON.stringify({ id: result.id, urls: result.urls }, null, 2)
|
|
132
|
+
}]
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// ─── get_music_track_related ──────────────────────────────────
|
|
138
|
+
server.tool(
|
|
139
|
+
'get_music_track_related',
|
|
140
|
+
'Get the stems and alternate versions of a music-library master track by id (e.g. instrumental, 30s cut, looped).',
|
|
141
|
+
{
|
|
142
|
+
track_id: z.string().describe('The master track id.')
|
|
143
|
+
},
|
|
144
|
+
async ({ track_id }) => {
|
|
145
|
+
const result = await client.get(`/v1/music-library/track/${encodeURIComponent(track_id)}/related`);
|
|
146
|
+
return {
|
|
147
|
+
content: [{
|
|
148
|
+
type: 'text',
|
|
149
|
+
text: JSON.stringify({ stems: result.stems || [], versions: result.versions || [] }, null, 2)
|
|
150
|
+
}]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// ─── get_music_track_lyrics ───────────────────────────────────
|
|
156
|
+
server.tool(
|
|
157
|
+
'get_music_track_lyrics',
|
|
158
|
+
'Get the lyrics text, lyrical theme, and explicit flag for a single music-library track by id.',
|
|
159
|
+
{
|
|
160
|
+
track_id: z.string().describe('The track id.')
|
|
161
|
+
},
|
|
162
|
+
async ({ track_id }) => {
|
|
163
|
+
const result = await client.get(`/v1/music-library/track/${encodeURIComponent(track_id)}/lyrics`);
|
|
164
|
+
return {
|
|
165
|
+
content: [{
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: JSON.stringify({
|
|
168
|
+
hasLyrics: result.hasLyrics,
|
|
169
|
+
lyrics: result.lyrics,
|
|
170
|
+
lyricalTheme: result.lyricalTheme,
|
|
171
|
+
explicit: result.explicit
|
|
172
|
+
}, null, 2)
|
|
173
|
+
}]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = { registerMusicLibraryTools };
|