@kolbo/mcp 1.3.1 → 1.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/client.js CHANGED
@@ -267,16 +267,16 @@ class KolboClient {
267
267
  ...formData.getHeaders()
268
268
  };
269
269
 
270
- try {
271
- const len = formData.getLengthSync();
272
- if (len) headers['Content-Length'] = String(len);
273
- } catch (_) { /* streaming length unavailable */ }
270
+ // Serialize form-data to a Buffer before passing to fetch(). Node's
271
+ // built-in fetch (undici) can't consume legacy Node.js streams from
272
+ // the `form-data` package, causing "fetch failed" on local file uploads.
273
+ const body = formData.getBuffer();
274
+ headers['Content-Length'] = String(body.length);
274
275
 
275
276
  const response = await fetch(url, {
276
277
  method: 'POST',
277
278
  headers,
278
- body: formData,
279
- duplex: 'half'
279
+ body
280
280
  });
281
281
 
282
282
  let data;
@@ -9,10 +9,15 @@ function registerMoodboardTools(server, client) {
9
9
  // ─── list_moodboards ───────────────────────────────────────
10
10
  server.tool(
11
11
  'list_moodboards',
12
- 'List moodboards available to you: your own, system presets, and any organization moodboards. Returns id, name, master_prompt, thumbnail, and image URLs for each.',
13
- {},
14
- async () => {
15
- const result = await client.get('/v1/moodboards');
12
+ 'List moodboards. By default returns ALL (personal + system presets + organization). Use "scope" to filter: "personal" (user\'s own), "preset" or "global" (system presets), or "organization" (org-shared). Returns id, name, master_prompt, thumbnail, and image URLs for each.',
13
+ {
14
+ scope: z.enum(['all', 'personal', 'preset', 'global', 'organization']).optional().describe('Filter by scope. Default: "all" (everything accessible). "personal" = only your own. "preset"/"global" = system presets. "organization" = org-shared.')
15
+ },
16
+ async ({ scope } = {}) => {
17
+ const params = new URLSearchParams();
18
+ if (scope && scope !== 'all') params.set('scope', scope);
19
+ const qs = params.toString();
20
+ const result = await client.get(`/v1/moodboards${qs ? '?' + qs : ''}`);
16
21
  return {
17
22
  content: [{
18
23
  type: 'text',
@@ -76,10 +76,21 @@ function registerVisualDnaTools(server, client) {
76
76
  // ─── list_visual_dnas ──────────────────────────────────────
77
77
  server.tool(
78
78
  'list_visual_dnas',
79
- 'List your Visual DNA profiles. Returns id, name, type, and thumbnail for each.',
80
- {},
81
- async () => {
82
- const result = await client.get('/v1/visual-dna');
79
+ 'List Visual DNA profiles. By default returns ALL (personal + global cast presets + organization). Use "scope" to filter: "personal" (user\'s own), "global" (system cast/presets), or "organization" (org-shared). Use "search" to filter by name/tags/description. Use "collection" to filter global presets by collection (cast, influencers, props, locations, styles, glamour, street).',
80
+ {
81
+ scope: z.enum(['all', 'personal', 'global', 'organization']).optional().describe('Filter by scope. Default: "all" (everything accessible). "personal" = only your own. "global" = system presets/cast. "organization" = org-shared.'),
82
+ search: z.string().optional().describe('Search by name, tags, or description (case-insensitive)'),
83
+ collection: z.string().optional().describe('Filter global presets by collection: cast, influencers, props, locations, styles, glamour, street'),
84
+ tags: z.string().optional().describe('Comma-separated tags to filter by (OR logic)')
85
+ },
86
+ async ({ scope, search, collection, tags } = {}) => {
87
+ const params = new URLSearchParams();
88
+ if (scope && scope !== 'all') params.set('scope', scope);
89
+ if (search) params.set('search', search);
90
+ if (collection) params.set('collection', collection);
91
+ if (tags) params.set('tags', tags);
92
+ const qs = params.toString();
93
+ const result = await client.get(`/v1/visual-dna${qs ? '?' + qs : ''}`);
83
94
  return {
84
95
  content: [{
85
96
  type: 'text',