@kolbo/mcp 1.48.0 → 1.50.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 +12 -10
- package/package.json +2 -2
- package/skill/SKILL.md +2 -1
- package/skill/references/models/music.md +1 -1
- package/src/apps/bridge.js +1 -1
- package/src/apps/index.js +14 -2
- package/src/apps/theme.js +18 -0
- package/src/apps/widgets/generation.js +109 -20
- package/src/apps/widgets/transcript.js +38 -9
- package/src/index.js +1 -1
- package/src/tools/_shared.js +59 -2
- package/src/tools/chat.js +4 -3
- package/src/tools/generate.js +96 -38
- package/src/tools/music_library.js +141 -174
- package/src/tools/projects.js +6 -3
- package/src/tools/stock_library.js +1 -1
|
@@ -1,236 +1,203 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* CONTRACT. Never rename, remove, or break an existing tool/arg. Full rules: ../index.js top-of-file.
|
|
3
|
-
*
|
|
4
|
-
* DEPRECATED FAMILY (2026-07-11): these tools originally fronted the dedicated
|
|
5
|
-
* Synci-only /v1/music-library/* routes. The Synci partner key expired and the
|
|
6
|
-
* unified STOCK LIBRARY covers music anyway (kolbo-ai catalog + Coverr + Synci
|
|
7
|
-
* when its key is live), so every tool here is now a thin adapter over
|
|
8
|
-
* /v1/stock/* — same tool names/args, better backend, and old cached installs
|
|
9
|
-
* keep working. New integrations should use search_stock_media /
|
|
10
|
-
* get_stock_asset with mediaType "music" directly.
|
|
11
|
-
*/
|
|
1
|
+
/* Public MCP contract: keep existing tool and argument names backward compatible. */
|
|
12
2
|
|
|
13
3
|
const { z } = require('zod');
|
|
14
4
|
const { UI, uiResult, appsEnabled } = require('../apps');
|
|
15
5
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// Format a stock music asset into a compact human-readable line.
|
|
19
|
-
function assetTrackLine(a) {
|
|
6
|
+
function trackLine(track) {
|
|
20
7
|
const meta = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
8
|
+
track.durationSeconds != null ? `${Math.round(track.durationSeconds)}s` : null,
|
|
9
|
+
track.artist || null,
|
|
10
|
+
track.bpm ? `${track.bpm} BPM` : null,
|
|
11
|
+
track.hqAvailable ? 'WAV available' : 'MP3 only',
|
|
24
12
|
].filter(Boolean).join(' · ');
|
|
25
|
-
|
|
26
|
-
return `${a.source}:${a.sourceId} — ${a.title || '(untitled)'}\n ${meta}${preview ? `\n preview: ${preview}` : ''}`;
|
|
13
|
+
return `[${track.id}] ${track.title || '(untitled)'}${meta ? `\n ${meta}` : ''}`;
|
|
27
14
|
}
|
|
28
15
|
|
|
29
|
-
|
|
30
|
-
function assetTrackItem(a) {
|
|
16
|
+
function trackItem(track) {
|
|
31
17
|
return {
|
|
32
|
-
id:
|
|
33
|
-
title:
|
|
34
|
-
subtitle: [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
a.source,
|
|
38
|
-
].filter(Boolean).join(' · '),
|
|
39
|
-
thumbnail: a.thumbnailUrl || null,
|
|
18
|
+
id: track.id,
|
|
19
|
+
title: track.title || '(untitled)',
|
|
20
|
+
subtitle: [track.artist, track.durationSeconds != null ? `${Math.round(track.durationSeconds)}s` : null]
|
|
21
|
+
.filter(Boolean).join(' · '),
|
|
22
|
+
thumbnail: track.artworkUrl || null,
|
|
40
23
|
media_type: 'audio',
|
|
41
|
-
preview_audio:
|
|
42
|
-
use_hint:
|
|
24
|
+
preview_audio: track.previewAudioUrl || track.audioUrl || track.audioUrl128 || null,
|
|
25
|
+
use_hint: `Acquire a clean track with acquire_clean_music_track track_id="${track.id}" format="mp3".`,
|
|
43
26
|
};
|
|
44
27
|
}
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (i > 0) return { source: trackId.slice(0, i), id: trackId.slice(i + 1) };
|
|
62
|
-
return { source: 'synci', id: trackId };
|
|
29
|
+
function tracksResult(ui, title, tracks, total) {
|
|
30
|
+
if (!tracks.length) return { content: [{ type: 'text', text: 'No SYNCI tracks found.' }] };
|
|
31
|
+
const text = [
|
|
32
|
+
`Found ${tracks.length} track${tracks.length === 1 ? '' : 's'}${total ? ` (of ${total})` : ''}.`,
|
|
33
|
+
'Playback URLs are watermarked previews. Use acquire_clean_music_track for final use; it consumes one SYNCI vendor credit.',
|
|
34
|
+
'',
|
|
35
|
+
tracks.map(trackLine).join('\n\n'),
|
|
36
|
+
].join('\n');
|
|
37
|
+
if (!ui()) return { content: [{ type: 'text', text }] };
|
|
38
|
+
return uiResult(UI.mediaGrid, text, {
|
|
39
|
+
widget: 'media-grid',
|
|
40
|
+
title,
|
|
41
|
+
items: tracks.slice(0, 20).map(trackItem),
|
|
42
|
+
total: total != null ? total : tracks.length,
|
|
43
|
+
});
|
|
63
44
|
}
|
|
64
45
|
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
46
|
+
function cleanResult(result, requestId) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{
|
|
49
|
+
type: 'text',
|
|
50
|
+
text: JSON.stringify({
|
|
51
|
+
track_id: result.trackId,
|
|
52
|
+
format: result.format,
|
|
53
|
+
audio_url: result.audioUrl,
|
|
54
|
+
download_url: result.downloadUrl,
|
|
55
|
+
watermarked: false,
|
|
56
|
+
credits_remaining: result.creditsRemaining,
|
|
57
|
+
request_id: requestId,
|
|
58
|
+
reused: !!result.reused,
|
|
59
|
+
}, null, 2),
|
|
60
|
+
}],
|
|
61
|
+
};
|
|
80
62
|
}
|
|
81
63
|
|
|
82
64
|
function registerMusicLibraryTools(server, client, options = {}) {
|
|
83
65
|
const ui = () => appsEnabled(server, options);
|
|
84
66
|
|
|
85
|
-
// ─── search_music_library (adapter → stock search) ─────────────
|
|
86
67
|
server.tool(
|
|
87
68
|
'search_music_library',
|
|
88
|
-
|
|
69
|
+
'Search the licensed SYNCI catalog. Results contain watermarked preview audio only. For any download or timeline use, call acquire_clean_music_track, which consumes one SYNCI vendor credit.',
|
|
89
70
|
{
|
|
90
|
-
query: z.string().max(200).optional()
|
|
91
|
-
mood: z.string().optional()
|
|
92
|
-
genre: z.string().optional()
|
|
93
|
-
bpmMin: z.number().optional()
|
|
94
|
-
bpmMax: z.number().optional()
|
|
95
|
-
durationMin: z.number().optional()
|
|
96
|
-
durationMax: z.number().optional()
|
|
97
|
-
hasStems: z.boolean().optional()
|
|
98
|
-
hasLyrics: z.boolean().optional()
|
|
99
|
-
sort: z.enum(['duration-asc', 'duration-desc', 'bpm-asc', 'bpm-desc', 'title']).optional()
|
|
100
|
-
limit: z.number().int().min(1).max(
|
|
101
|
-
offset: z.number().int().min(0).optional()
|
|
71
|
+
query: z.string().max(200).optional(),
|
|
72
|
+
mood: z.string().optional(),
|
|
73
|
+
genre: z.string().optional(),
|
|
74
|
+
bpmMin: z.number().optional(),
|
|
75
|
+
bpmMax: z.number().optional(),
|
|
76
|
+
durationMin: z.number().optional(),
|
|
77
|
+
durationMax: z.number().optional(),
|
|
78
|
+
hasStems: z.boolean().optional(),
|
|
79
|
+
hasLyrics: z.boolean().optional(),
|
|
80
|
+
sort: z.enum(['duration-asc', 'duration-desc', 'bpm-asc', 'bpm-desc', 'title']).optional(),
|
|
81
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
82
|
+
offset: z.number().int().min(0).optional(),
|
|
102
83
|
},
|
|
103
84
|
async (args) => {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
'No tracks found matching that query. Try a broader vibe description, or use search_stock_media with mediaType="music".');
|
|
108
|
-
}
|
|
85
|
+
const result = await client.post('/v1/music-library/search', args);
|
|
86
|
+
return tracksResult(ui, `SYNCI — ${args.query || 'Search'}`, result.tracks || [], result.total);
|
|
87
|
+
},
|
|
109
88
|
);
|
|
110
89
|
|
|
111
|
-
// ─── analyze_script_for_music (unchanged — doesn't touch Synci) ─
|
|
112
90
|
server.tool(
|
|
113
91
|
'analyze_script_for_music',
|
|
114
|
-
'
|
|
115
|
-
{
|
|
116
|
-
script: z.string().min(1).describe('The video or voiceover script / scene description to analyze (up to ~8000 chars).')
|
|
117
|
-
},
|
|
92
|
+
'Turn a script or scene description into a SYNCI music search.',
|
|
93
|
+
{ script: z.string().min(1).max(8000) },
|
|
118
94
|
async ({ script }) => {
|
|
119
95
|
const result = await client.post('/v1/music-library/analyze-script', { script });
|
|
120
|
-
return {
|
|
121
|
-
|
|
122
|
-
type: 'text',
|
|
123
|
-
text: JSON.stringify({
|
|
124
|
-
query: result.query,
|
|
125
|
-
mood: result.mood,
|
|
126
|
-
genre: result.genre,
|
|
127
|
-
keywords: result.keywords,
|
|
128
|
-
_followup_hint: 'Pass query + mood + genre into search_music_library (or search_stock_media mediaType="music").'
|
|
129
|
-
}, null, 2)
|
|
130
|
-
}]
|
|
131
|
-
};
|
|
132
|
-
}
|
|
96
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
97
|
+
},
|
|
133
98
|
);
|
|
134
99
|
|
|
135
|
-
// ─── browse_music_library (adapter → stock browse feed) ────────
|
|
136
100
|
server.tool(
|
|
137
101
|
'browse_music_library',
|
|
138
|
-
|
|
102
|
+
'Browse the licensed SYNCI catalog. Playback remains watermarked; final use requires acquire_clean_music_track.',
|
|
139
103
|
{
|
|
140
|
-
sort: z.enum(['duration-asc', 'duration-desc', 'bpm-asc', 'bpm-desc', 'title']).optional()
|
|
141
|
-
limit: z.number().int().min(1).max(50).optional()
|
|
142
|
-
offset: z.number().int().min(0).optional()
|
|
104
|
+
sort: z.enum(['duration-asc', 'duration-desc', 'bpm-asc', 'bpm-desc', 'title']).optional(),
|
|
105
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
106
|
+
offset: z.number().int().min(0).optional(),
|
|
107
|
+
},
|
|
108
|
+
async ({ sort, limit, offset }) => {
|
|
109
|
+
const params = new URLSearchParams();
|
|
110
|
+
if (sort) params.set('sort', sort);
|
|
111
|
+
if (limit != null) params.set('limit', String(limit));
|
|
112
|
+
if (offset != null) params.set('offset', String(offset));
|
|
113
|
+
const result = await client.get(`/v1/music-library/catalog?${params.toString()}`);
|
|
114
|
+
return tracksResult(ui, 'SYNCI Music Library', result.tracks || [], result.total);
|
|
143
115
|
},
|
|
144
|
-
async ({ limit, offset }) => {
|
|
145
|
-
const { assets, total } = await stockMusicSearch(client, '', limit, offset);
|
|
146
|
-
return musicResult(ui, null, assets, total, 'No tracks returned.');
|
|
147
|
-
}
|
|
148
116
|
);
|
|
149
117
|
|
|
150
|
-
// ─── get_music_library_facets (adapter → stock categories) ─────
|
|
151
118
|
server.tool(
|
|
152
119
|
'get_music_library_facets',
|
|
153
|
-
|
|
120
|
+
'List SYNCI genres, moods, instruments, BPM, and duration filters.',
|
|
154
121
|
{},
|
|
155
122
|
async () => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
categories = (result.categories || []).map(c => c.name || c.providerParam).filter(Boolean);
|
|
160
|
-
} catch (_) { /* categories are best-effort */ }
|
|
161
|
-
return {
|
|
162
|
-
content: [{
|
|
163
|
-
type: 'text',
|
|
164
|
-
text: JSON.stringify({
|
|
165
|
-
genres: categories,
|
|
166
|
-
moods: [],
|
|
167
|
-
instruments: [],
|
|
168
|
-
bpmRange: null,
|
|
169
|
-
durationRange: null,
|
|
170
|
-
_note: 'Semantic search: any natural-language mood/vibe works as a query — exact facet values are no longer required.'
|
|
171
|
-
}, null, 2)
|
|
172
|
-
}]
|
|
173
|
-
};
|
|
174
|
-
}
|
|
123
|
+
const result = await client.get('/v1/music-library/facets');
|
|
124
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
125
|
+
},
|
|
175
126
|
);
|
|
176
127
|
|
|
177
|
-
// ─── get_music_track_audio (adapter → stock asset) ──────────────
|
|
178
128
|
server.tool(
|
|
179
129
|
'get_music_track_audio',
|
|
180
|
-
|
|
130
|
+
'Get watermarked preview URLs for a SYNCI track. These URLs are never licensed masters; call acquire_clean_music_track for final use.',
|
|
131
|
+
{ track_id: z.string().min(1).max(64) },
|
|
132
|
+
async ({ track_id }) => {
|
|
133
|
+
const result = await client.get(`/v1/music-library/track/${encodeURIComponent(track_id)}/audio`);
|
|
134
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ...result, preview_only: true }, null, 2) }] };
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
server.tool(
|
|
139
|
+
'acquire_clean_music_track',
|
|
140
|
+
'Acquire a clean, unwatermarked SYNCI MP3 or WAV for download or Adobe timeline use. This immediately consumes one SYNCI vendor credit with no confirmation dialog. Reuse request_id when retrying the same intended action.',
|
|
181
141
|
{
|
|
182
|
-
track_id: z.string().
|
|
142
|
+
track_id: z.string().min(1).max(64),
|
|
143
|
+
format: z.enum(['mp3', 'wav']).optional().describe('Default mp3. Use wav only when the search result reports hqAvailable=true.'),
|
|
144
|
+
purpose: z.enum(['download', 'timeline']).optional(),
|
|
145
|
+
request_id: z.string().regex(/^[A-Za-z0-9_-]{8,80}$/).describe('Required idempotency key. Reuse it for retries of the same action.'),
|
|
146
|
+
project_id: z.string().optional(),
|
|
147
|
+
},
|
|
148
|
+
async ({ track_id, format = 'mp3', purpose = 'download', request_id, project_id }) => {
|
|
149
|
+
const requestId = request_id;
|
|
150
|
+
const result = await client.post(`/v1/music-library/clean/${encodeURIComponent(track_id)}`, {
|
|
151
|
+
format,
|
|
152
|
+
purpose,
|
|
153
|
+
requestId,
|
|
154
|
+
projectId: project_id,
|
|
155
|
+
});
|
|
156
|
+
return cleanResult(result, requestId);
|
|
183
157
|
},
|
|
184
|
-
async ({ track_id }) => {
|
|
185
|
-
const { source, id } = parseTrackId(track_id);
|
|
186
|
-
// mediaType hint required for sources that share ids across types (kolbo-ai).
|
|
187
|
-
const result = await client.get(`/v1/stock/asset/${encodeURIComponent(source)}/${encodeURIComponent(id)}?mediaType=music`);
|
|
188
|
-
const a = result.asset || result;
|
|
189
|
-
const urls = {};
|
|
190
|
-
(a.downloadVariants || []).forEach(v => { if (v.url) urls[v.label || 'audio'] = v.url; });
|
|
191
|
-
return {
|
|
192
|
-
content: [{
|
|
193
|
-
type: 'text',
|
|
194
|
-
text: JSON.stringify({ id: track_id, title: a.title, urls }, null, 2)
|
|
195
|
-
}]
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
158
|
);
|
|
199
159
|
|
|
200
|
-
// ─── get_music_track_related (graceful stub — no stock equivalent) ─
|
|
201
160
|
server.tool(
|
|
202
|
-
'
|
|
203
|
-
|
|
161
|
+
'import_music_track_to_library',
|
|
162
|
+
'Acquire one clean SYNCI file and copy it into the Kolbo media library. This immediately consumes one SYNCI vendor credit unless the track is already in the library. Defaults to clean MP3.',
|
|
204
163
|
{
|
|
205
|
-
track_id: z.string().
|
|
164
|
+
track_id: z.string().min(1).max(64),
|
|
165
|
+
format: z.enum(['mp3', 'wav']).optional(),
|
|
166
|
+
request_id: z.string().regex(/^[A-Za-z0-9_-]{8,80}$/).describe('Required idempotency key; reuse it for retries.'),
|
|
167
|
+
project_id: z.string().optional(),
|
|
168
|
+
track: z.record(z.string(), z.unknown()).optional().describe('Optional track snapshot from search_music_library.'),
|
|
169
|
+
},
|
|
170
|
+
async ({ track_id, format = 'mp3', request_id, project_id, track }) => {
|
|
171
|
+
const requestId = request_id;
|
|
172
|
+
const result = await client.post('/v1/music-library/import', {
|
|
173
|
+
trackId: track_id,
|
|
174
|
+
format,
|
|
175
|
+
requestId,
|
|
176
|
+
projectId: project_id,
|
|
177
|
+
track,
|
|
178
|
+
});
|
|
179
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ...result, requestId }, null, 2) }] };
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
server.tool(
|
|
184
|
+
'get_music_track_related',
|
|
185
|
+
'Get SYNCI stems and alternate versions metadata. Purchasing stems or alternate versions is not supported.',
|
|
186
|
+
{ track_id: z.string().min(1).max(64) },
|
|
187
|
+
async ({ track_id }) => {
|
|
188
|
+
const result = await client.get(`/v1/music-library/track/${encodeURIComponent(track_id)}/related`);
|
|
189
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
206
190
|
},
|
|
207
|
-
async ({ track_id }) => ({
|
|
208
|
-
content: [{
|
|
209
|
-
type: 'text',
|
|
210
|
-
text: JSON.stringify({
|
|
211
|
-
stems: [], versions: [],
|
|
212
|
-
_note: `Stems/alternate versions are not available via the stock library. Use get_music_track_audio ("${track_id}") for the downloadable variants, or generate_music to compose a custom track.`
|
|
213
|
-
}, null, 2)
|
|
214
|
-
}]
|
|
215
|
-
})
|
|
216
191
|
);
|
|
217
192
|
|
|
218
|
-
// ─── get_music_track_lyrics (graceful stub — no stock equivalent) ─
|
|
219
193
|
server.tool(
|
|
220
194
|
'get_music_track_lyrics',
|
|
221
|
-
|
|
222
|
-
{
|
|
223
|
-
|
|
195
|
+
'Get SYNCI lyrics metadata for a track.',
|
|
196
|
+
{ track_id: z.string().min(1).max(64) },
|
|
197
|
+
async ({ track_id }) => {
|
|
198
|
+
const result = await client.get(`/v1/music-library/track/${encodeURIComponent(track_id)}/lyrics`);
|
|
199
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
224
200
|
},
|
|
225
|
-
async () => ({
|
|
226
|
-
content: [{
|
|
227
|
-
type: 'text',
|
|
228
|
-
text: JSON.stringify({
|
|
229
|
-
hasLyrics: false, lyrics: null, lyricalTheme: null, explicit: null,
|
|
230
|
-
_note: 'Lyrics metadata is not available via the stock library. For a song with specific lyrics, use generate_music with the lyrics field.'
|
|
231
|
-
}, null, 2)
|
|
232
|
-
}]
|
|
233
|
-
})
|
|
234
201
|
);
|
|
235
202
|
}
|
|
236
203
|
|
package/src/tools/projects.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* new OPTIONAL args only. Full rules: ../index.js top-of-file and CLAUDE.md. */
|
|
5
5
|
|
|
6
6
|
const { z } = require('zod');
|
|
7
|
+
const { buildProjectUrl } = require('./_shared');
|
|
7
8
|
|
|
8
9
|
function registerProjectTools(server, client) {
|
|
9
10
|
// ─── list_projects ─────────────────────────────────────────
|
|
@@ -17,7 +18,8 @@ function registerProjectTools(server, client) {
|
|
|
17
18
|
id: p.id,
|
|
18
19
|
name: p.name,
|
|
19
20
|
role: p.role,
|
|
20
|
-
is_default: !!p.is_default
|
|
21
|
+
is_default: !!p.is_default,
|
|
22
|
+
open_url: buildProjectUrl(p.id, { is_default: !!p.is_default })
|
|
21
23
|
}));
|
|
22
24
|
return {
|
|
23
25
|
content: [{
|
|
@@ -25,7 +27,7 @@ function registerProjectTools(server, client) {
|
|
|
25
27
|
text: JSON.stringify({
|
|
26
28
|
projects,
|
|
27
29
|
count: projects.length,
|
|
28
|
-
_hint: 'Pass the chosen `id` as `project_id` on any generate_* tool to drop the generation into that project. Omit project_id to use the project flagged is_default:true.'
|
|
30
|
+
_hint: 'Pass the chosen `id` as `project_id` on any generate_* tool to drop the generation into that project. Omit project_id to use the project flagged is_default:true. `open_url` opens that project\'s media in the web app (share it with the user).'
|
|
29
31
|
}, null, 2)
|
|
30
32
|
}]
|
|
31
33
|
};
|
|
@@ -68,7 +70,8 @@ function registerProjectTools(server, client) {
|
|
|
68
70
|
const body = { name };
|
|
69
71
|
if (description) body.description = description;
|
|
70
72
|
const result = await client.post('/v1/projects', body);
|
|
71
|
-
|
|
73
|
+
const open_url = buildProjectUrl(result.project && result.project.id, { is_default: !!(result.project && result.project.is_default) });
|
|
74
|
+
return { content: [{ type: 'text', text: JSON.stringify({ project: result.project, open_url, _hint: 'Pass this id as project_id on every subsequent call for this work. `open_url` opens the project in the web app — share it with the user.' }, null, 2) }] };
|
|
72
75
|
}
|
|
73
76
|
);
|
|
74
77
|
|
|
@@ -181,7 +181,7 @@ function registerStockLibraryTools(server, client, options = {}) {
|
|
|
181
181
|
// ─── import_stock_asset ───────────────────────────────────────
|
|
182
182
|
server.tool(
|
|
183
183
|
'import_stock_asset',
|
|
184
|
-
"Copy a stock asset into the account's Kolbo media library (downloaded to Kolbo's CDN with a stable URL) so it can be used in projects/generations. Free. Returns the created media library item. Works for Kolbo SFX (source='kolbo-ai', mediaType='sfx') and external visual/audio sources.
|
|
184
|
+
"Copy a stock asset into the account's Kolbo media library (downloaded to Kolbo's CDN with a stable URL) so it can be used in projects/generations. Free. Returns the created media library item. Works for Kolbo SFX (source='kolbo-ai', mediaType='sfx') and supported external visual/audio sources. For SYNCI music use import_music_track_to_library; that paid action acquires a clean licensed file.",
|
|
185
185
|
{
|
|
186
186
|
source: z.enum(['kolbo-ai', 'pexels', 'pixabay', 'sketchfab', 'freesound']).describe('The asset source.'),
|
|
187
187
|
id: z.string().describe('The provider asset id (sourceId).'),
|