@kolbo/mcp 1.12.0 → 1.13.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,55 +1,55 @@
1
- {
2
- "name": "@kolbo/mcp",
3
- "version": "1.12.0",
4
- "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
- "main": "src/index.js",
6
- "bin": {
7
- "kolbo-mcp": "./bin/kolbo-mcp.js"
8
- },
9
- "scripts": {
10
- "start": "node src/index.js",
11
- "smoke": "node scripts/smoke.js",
12
- "check-parity": "node scripts/check-parity.js",
13
- "prepublishOnly": "node scripts/smoke.js && node scripts/check-parity.js"
14
- },
15
- "keywords": [
16
- "kolbo",
17
- "mcp",
18
- "ai",
19
- "image-generation",
20
- "video-generation",
21
- "music-generation",
22
- "text-to-speech",
23
- "claude-code",
24
- "claude-desktop",
25
- "model-context-protocol"
26
- ],
27
- "license": "MIT",
28
- "repository": {
29
- "type": "git",
30
- "url": "https://github.com/Zoharvan12/kolbo-mcp"
31
- },
32
- "homepage": "https://docs.kolbo.ai/developer-api/claude-code-skill",
33
- "author": "Kolbo AI <support@kolbo.ai>",
34
- "publishConfig": {
35
- "access": "public"
36
- },
37
- "files": [
38
- "src/",
39
- "bin/",
40
- "README.md"
41
- ],
42
- "dependencies": {
43
- "@modelcontextprotocol/sdk": "1.29.0",
44
- "form-data": "^4.0.5",
45
- "zod": "^3.25.0"
46
- },
47
- "overrides": {
48
- "hono": "^4.12.12",
49
- "@hono/node-server": "^1.19.13",
50
- "path-to-regexp": "^8.4.2"
51
- },
52
- "engines": {
53
- "node": ">=18.0.0"
54
- }
55
- }
1
+ {
2
+ "name": "@kolbo/mcp",
3
+ "version": "1.13.0",
4
+ "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "kolbo-mcp": "./bin/kolbo-mcp.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node src/index.js",
11
+ "smoke": "node scripts/smoke.js",
12
+ "check-parity": "node scripts/check-parity.js",
13
+ "prepublishOnly": "node scripts/smoke.js && node scripts/check-parity.js"
14
+ },
15
+ "keywords": [
16
+ "kolbo",
17
+ "mcp",
18
+ "ai",
19
+ "image-generation",
20
+ "video-generation",
21
+ "music-generation",
22
+ "text-to-speech",
23
+ "claude-code",
24
+ "claude-desktop",
25
+ "model-context-protocol"
26
+ ],
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/Zoharvan12/kolbo-mcp"
31
+ },
32
+ "homepage": "https://docs.kolbo.ai/developer-api/claude-code-skill",
33
+ "author": "Kolbo AI <support@kolbo.ai>",
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "files": [
38
+ "src/",
39
+ "bin/",
40
+ "README.md"
41
+ ],
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "1.29.0",
44
+ "form-data": "^4.0.5",
45
+ "zod": "^3.25.0"
46
+ },
47
+ "overrides": {
48
+ "hono": "^4.12.12",
49
+ "@hono/node-server": "^1.19.13",
50
+ "path-to-regexp": "^8.4.2"
51
+ },
52
+ "engines": {
53
+ "node": ">=18.0.0"
54
+ }
55
+ }
package/src/index.js CHANGED
@@ -93,7 +93,15 @@ async function main() {
93
93
  await server.connect(transport);
94
94
  }
95
95
 
96
- main().catch(err => {
97
- console.error('Failed to start Kolbo MCP server:', err);
98
- process.exit(1);
99
- });
96
+ module.exports = { main };
97
+
98
+ // Auto-run when invoked directly (e.g. `node src/index.js` or via the published
99
+ // bin/kolbo-mcp.js wrapper). Consumers that `require()` this module to embed it
100
+ // inside another process (the Kolbo Code CLI's `kolbo mcp serve` subcommand)
101
+ // should call `main()` themselves.
102
+ if (require.main === module || require.main?.filename?.endsWith('kolbo-mcp.js')) {
103
+ main().catch(err => {
104
+ console.error('Failed to start Kolbo MCP server:', err);
105
+ process.exit(1);
106
+ });
107
+ }
package/src/polling.js CHANGED
@@ -25,6 +25,25 @@ class GenerationFailedError extends Error {
25
25
  }
26
26
  }
27
27
 
28
+ // HTTP status codes / fetch failure modes we treat as transient when
29
+ // polling. The job is almost always still running on the server (or about
30
+ // to come back up) — bailing out makes the agent give up on a generation
31
+ // that completes 2 seconds later.
32
+ const TRANSIENT_STATUS_CODES = new Set([0, 408, 425, 429, 500, 502, 503, 504, 522, 524]);
33
+
34
+ function isTransientPollError(err) {
35
+ if (!err) return false;
36
+ // fetch() rejects with a TypeError on network failure (ECONNREFUSED,
37
+ // ECONNRESET, DNS failure, kolbo-api restart, etc.) — no `status` on
38
+ // the error object.
39
+ if (err.name === 'TypeError') return true;
40
+ if (err.code === 'ECONNRESET' || err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT' || err.code === 'EPIPE') return true;
41
+ // KolboApiError tags status on the options object.
42
+ const status = err.status ?? err.options?.status;
43
+ if (typeof status === 'number' && TRANSIENT_STATUS_CODES.has(status)) return true;
44
+ return false;
45
+ }
46
+
28
47
  async function pollUntilDone(client, generationId, options = {}) {
29
48
  const {
30
49
  interval = 5000,
@@ -34,13 +53,36 @@ async function pollUntilDone(client, generationId, options = {}) {
34
53
 
35
54
  const startTime = Date.now();
36
55
  const url = statusUrl || `/v1/generate/${encodeURIComponent(generationId)}/status`;
56
+ let transientFailures = 0;
37
57
 
38
58
  while (true) {
39
59
  if (Date.now() - startTime > timeout) {
40
60
  throw new PollingTimeoutError(generationId, timeout);
41
61
  }
42
62
 
43
- const result = await client.get(url);
63
+ let result;
64
+ try {
65
+ result = await client.get(url);
66
+ transientFailures = 0; // reset on successful poll
67
+ } catch (err) {
68
+ // kolbo-api restart, transient network blip, rate-limit, 5xx —
69
+ // the job is still alive on the server (or coming back). Don't
70
+ // abandon the polling loop just because one status check failed:
71
+ // wait a bit longer and try again. After ~30 consecutive failures
72
+ // (~2.5 minutes at the 5s base interval) we still surface the
73
+ // last error so a truly dead backend doesn't loop forever.
74
+ if (isTransientPollError(err)) {
75
+ transientFailures++;
76
+ if (transientFailures > 30) {
77
+ throw err;
78
+ }
79
+ const backoff = Math.min(interval * Math.pow(1.5, Math.min(transientFailures - 1, 5)), 30000);
80
+ await new Promise((resolve) => setTimeout(resolve, backoff));
81
+ continue;
82
+ }
83
+ // Non-transient (auth, 4xx other than 408/425/429) — bubble up.
84
+ throw err;
85
+ }
44
86
 
45
87
  if (result.state === 'completed') {
46
88
  return result;
@@ -20,7 +20,7 @@ function registerVisualDnaTools(server, client) {
20
20
  'create_visual_dna',
21
21
  'Create a Visual DNA profile from reference media. Each item in images/video/audio can be a public URL or an absolute local file path. Max 4 images, 1 video, 1 audio. Files capped at 25MB each.',
22
22
  {
23
- name: z.string().describe('Name of the Visual DNA profile'),
23
+ name: z.string().describe('Name of the Visual DNA profile. **Pick a short, lowercase, no-space single token** (e.g. `maya`, `tokyo_neon`, `brand_red`, `esther_model`) — never names with spaces (`Sarah Johnson` ❌). The user/LLM types this as `@<name>` inside generation prompts, and the @ parser stops at the first space, so `@Sarah Johnson` matches only `Sarah` and the binding silently drops. Multi-word concepts should use underscores or be a single token. Names are case-insensitive on lookup, but **reserved** values rejected on creation: `Image1`, `Image2`, …, `Video1`, …, `Audio1`, … (any-language characters allowed; max 100 chars).'),
24
24
  dna_type: z.string().optional().describe('Type: "character", "style", "product", "scene". Default: "character"'),
25
25
  prompt_helper: z.string().optional().describe('Optional description/notes to guide DNA extraction'),
26
26
  images: z.array(z.string()).optional().describe('Array of image sources (URLs or absolute local paths). Max 4.'),