@kolbo/mcp 1.0.0 → 1.1.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 +46 -11
- package/package.json +9 -5
- package/src/client.js +85 -2
- package/src/index.js +64 -0
- package/src/polling.js +32 -6
- package/src/tools/chat.js +135 -0
- package/src/tools/generate.js +371 -224
- package/src/tools/models.js +5 -0
- package/src/tools/moodboards.js +45 -0
- package/src/tools/visual_dna.js +198 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use [Kolbo AI](https://kolbo.ai) as native tools in Claude Code and Claude Desktop via MCP (Model Context Protocol).
|
|
4
4
|
|
|
5
|
-
Generate images, videos, music, speech,
|
|
5
|
+
Generate images, videos, music, speech, sound effects, multi-scene campaigns, and conversational chat — all from natural language in your coding environment. 100+ AI models behind Smart Select routing, with reusable Visual DNA profiles for character/style consistency.
|
|
6
6
|
|
|
7
7
|
## Quick Setup
|
|
8
8
|
|
|
@@ -34,22 +34,57 @@ Just ask Claude naturally:
|
|
|
34
34
|
|
|
35
35
|
- *"Generate an image of a sunset over mountains"*
|
|
36
36
|
- *"Create a 5-second video of waves crashing"*
|
|
37
|
+
- *"Build a 4-scene storyboard for a coffee shop ad"*
|
|
38
|
+
- *"Remove the background from this image"*
|
|
37
39
|
- *"Make a lo-fi hip hop beat"*
|
|
38
|
-
- *"
|
|
40
|
+
- *"Read this out loud with a British female voice"*
|
|
41
|
+
- *"Ask Claude about the latest AI news with web search on"*
|
|
42
|
+
- *"Create a Visual DNA profile called 'Alex' from these images"*
|
|
39
43
|
|
|
40
|
-
## Available Tools
|
|
44
|
+
## Available Tools (21)
|
|
41
45
|
|
|
46
|
+
**Generation**
|
|
42
47
|
| Tool | Description |
|
|
43
48
|
|------|-------------|
|
|
44
|
-
| `generate_image` |
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
49
|
+
| `generate_image` | Text → image |
|
|
50
|
+
| `generate_image_edit` | Existing image(s) + prompt → edited image |
|
|
51
|
+
| `generate_video` | Text → video |
|
|
52
|
+
| `generate_video_from_image` | Still image + motion prompt → video |
|
|
53
|
+
| `generate_creative_director` | One brief → N coordinated scenes (image or video) |
|
|
54
|
+
| `generate_music` | Text (+ optional lyrics) → song |
|
|
55
|
+
| `generate_speech` | Text + voice → spoken audio |
|
|
56
|
+
| `generate_sound` | Text → sound effect |
|
|
57
|
+
|
|
58
|
+
Every image/video/creative-director tool accepts `visual_dna_ids` and `moodboard_id` for character/style consistency across outputs — you can compose `create_visual_dna` → `generate_image` (with the DNA applied server-side) in a single agent turn. `generate_creative_director` also accepts `moodboard_ids` plural for blending.
|
|
59
|
+
|
|
60
|
+
**Chat**
|
|
61
|
+
| Tool | Description |
|
|
62
|
+
|------|-------------|
|
|
63
|
+
| `chat_send_message` | Multi-turn chat with any Kolbo model; supports web search and deep think |
|
|
64
|
+
| `chat_list_conversations` | List past chat threads |
|
|
65
|
+
| `chat_get_messages` | Fetch messages in a conversation |
|
|
66
|
+
|
|
67
|
+
**Visual DNA** (reusable character/style/product profiles)
|
|
68
|
+
| Tool | Description |
|
|
69
|
+
|------|-------------|
|
|
70
|
+
| `create_visual_dna` | Create a profile from URLs or local files |
|
|
71
|
+
| `list_visual_dnas` | List your profiles |
|
|
72
|
+
| `get_visual_dna` | Fetch one profile |
|
|
73
|
+
| `delete_visual_dna` | Delete a profile |
|
|
74
|
+
|
|
75
|
+
**Moodboards**
|
|
76
|
+
| Tool | Description |
|
|
77
|
+
|------|-------------|
|
|
78
|
+
| `list_moodboards` | Browse presets + your moodboards |
|
|
79
|
+
| `get_moodboard` | Fetch one moodboard with all image URLs |
|
|
80
|
+
|
|
81
|
+
**Discovery & Account**
|
|
82
|
+
| Tool | Description |
|
|
83
|
+
|------|-------------|
|
|
84
|
+
| `list_models` | Current model catalog with costs and capabilities |
|
|
85
|
+
| `list_voices` | TTS voices (presets + cloned) |
|
|
51
86
|
| `check_credits` | Check credit balance |
|
|
52
|
-
| `get_generation_status` |
|
|
87
|
+
| `get_generation_status` | Poll a generation by ID (fallback if a tool times out) |
|
|
53
88
|
|
|
54
89
|
## Environment Variables
|
|
55
90
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kolbo/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.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": {
|
|
7
7
|
"kolbo-mcp": "./bin/kolbo-mcp.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"start": "node src/index.js"
|
|
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"
|
|
11
14
|
},
|
|
12
15
|
"keywords": [
|
|
13
16
|
"kolbo",
|
|
@@ -24,9 +27,9 @@
|
|
|
24
27
|
"license": "MIT",
|
|
25
28
|
"repository": {
|
|
26
29
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/
|
|
30
|
+
"url": "https://github.com/Zoharvan12/kolbo-mcp"
|
|
28
31
|
},
|
|
29
|
-
"homepage": "https://docs.kolbo.ai/developer-api/claude-code-
|
|
32
|
+
"homepage": "https://docs.kolbo.ai/developer-api/claude-code-skill",
|
|
30
33
|
"author": "Kolbo AI <support@kolbo.ai>",
|
|
31
34
|
"publishConfig": {
|
|
32
35
|
"access": "public"
|
|
@@ -37,7 +40,8 @@
|
|
|
37
40
|
"README.md"
|
|
38
41
|
],
|
|
39
42
|
"dependencies": {
|
|
40
|
-
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
44
|
+
"form-data": "^4.0.5"
|
|
41
45
|
},
|
|
42
46
|
"engines": {
|
|
43
47
|
"node": ">=18.0.0"
|
package/src/client.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Kolbo API HTTP client wrapper
|
|
3
3
|
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Structured error thrown when the Kolbo API returns a non-OK response.
|
|
7
|
+
* Preserves the SDK's error code, HTTP status, and full response data so
|
|
8
|
+
* MCP tools (and the LLM consuming them) can distinguish NOT_FOUND from
|
|
9
|
+
* INSUFFICIENT_CREDITS from VALIDATION_ERROR etc.
|
|
10
|
+
*/
|
|
11
|
+
class KolboApiError extends Error {
|
|
12
|
+
constructor(message, { code, status, data } = {}) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'KolboApiError';
|
|
15
|
+
this.code = code || null;
|
|
16
|
+
this.status = status || null;
|
|
17
|
+
this.data = data || null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
class KolboClient {
|
|
5
22
|
constructor() {
|
|
6
23
|
this.apiKey = process.env.KOLBO_API_KEY;
|
|
@@ -26,10 +43,27 @@ class KolboClient {
|
|
|
26
43
|
}
|
|
27
44
|
|
|
28
45
|
const response = await fetch(url, options);
|
|
29
|
-
|
|
46
|
+
let data;
|
|
47
|
+
try {
|
|
48
|
+
data = await response.json();
|
|
49
|
+
} catch (_) {
|
|
50
|
+
// Non-JSON body (gateway error, HTML etc.)
|
|
51
|
+
throw new KolboApiError(`API error: ${response.status} ${response.statusText}`, {
|
|
52
|
+
status: response.status,
|
|
53
|
+
data: null
|
|
54
|
+
});
|
|
55
|
+
}
|
|
30
56
|
|
|
31
57
|
if (!response.ok || data.success === false) {
|
|
32
|
-
|
|
58
|
+
const message = data.error || data.message || `API error: ${response.status}`;
|
|
59
|
+
const code = data.code || null;
|
|
60
|
+
// Surface the code in the message so the LLM sees it even if it ignores the .code property
|
|
61
|
+
const fullMessage = code ? `${message} [${code}]` : message;
|
|
62
|
+
throw new KolboApiError(fullMessage, {
|
|
63
|
+
code,
|
|
64
|
+
status: response.status,
|
|
65
|
+
data
|
|
66
|
+
});
|
|
33
67
|
}
|
|
34
68
|
|
|
35
69
|
return data;
|
|
@@ -42,6 +76,55 @@ class KolboClient {
|
|
|
42
76
|
async get(path) {
|
|
43
77
|
return this.request('GET', path);
|
|
44
78
|
}
|
|
79
|
+
|
|
80
|
+
async delete(path) {
|
|
81
|
+
return this.request('DELETE', path);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async postMultipart(path, formData) {
|
|
85
|
+
const url = `${this.baseUrl}${path}`;
|
|
86
|
+
const headers = {
|
|
87
|
+
'X-API-Key': this.apiKey,
|
|
88
|
+
...formData.getHeaders()
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// form-data exposes getLengthSync for known-size parts; set Content-Length when available.
|
|
92
|
+
try {
|
|
93
|
+
const len = formData.getLengthSync();
|
|
94
|
+
if (len) headers['Content-Length'] = String(len);
|
|
95
|
+
} catch (_) { /* streaming length unavailable — let fetch handle it */ }
|
|
96
|
+
|
|
97
|
+
const response = await fetch(url, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers,
|
|
100
|
+
body: formData,
|
|
101
|
+
duplex: 'half'
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
let data;
|
|
105
|
+
try {
|
|
106
|
+
data = await response.json();
|
|
107
|
+
} catch (_) {
|
|
108
|
+
throw new KolboApiError(`API error: ${response.status} ${response.statusText}`, {
|
|
109
|
+
status: response.status,
|
|
110
|
+
data: null
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!response.ok || data.success === false) {
|
|
115
|
+
const message = data.error || data.message || `API error: ${response.status}`;
|
|
116
|
+
const code = data.code || null;
|
|
117
|
+
const fullMessage = code ? `${message} [${code}]` : message;
|
|
118
|
+
throw new KolboApiError(fullMessage, {
|
|
119
|
+
code,
|
|
120
|
+
status: response.status,
|
|
121
|
+
data
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return data;
|
|
126
|
+
}
|
|
45
127
|
}
|
|
46
128
|
|
|
47
129
|
module.exports = KolboClient;
|
|
130
|
+
module.exports.KolboApiError = KolboApiError;
|
package/src/index.js
CHANGED
|
@@ -1,8 +1,69 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* @kolbo/mcp — Kolbo AI MCP Server
|
|
3
|
+
*
|
|
4
|
+
* ⛔ STOP. READ THIS BEFORE TOUCHING ANY TOOL REGISTRATION. ⛔
|
|
5
|
+
*
|
|
6
|
+
* This package is published to npm and installed via `npx -y @kolbo/mcp`.
|
|
7
|
+
* Thousands of users have it CACHED on their machines, pinned to old versions
|
|
8
|
+
* by npx's cache. Every tool name, every arg name, every response shape
|
|
9
|
+
* registered below is a PUBLIC CONTRACT. Breaking it silently strands users
|
|
10
|
+
* whose LLM will keep calling tool names their cached server no longer
|
|
11
|
+
* registers — or worse, calls new-style args that the old server can't parse.
|
|
12
|
+
*
|
|
13
|
+
* THE THREE COMMANDMENTS
|
|
14
|
+
*
|
|
15
|
+
* 1. NEVER RENAME AN EXISTING TOOL.
|
|
16
|
+
* Not `generate_image` → `create_image`. Not `list_models` → `get_models`.
|
|
17
|
+
* Not "just cleaning up the name." Old cached clients break the instant
|
|
18
|
+
* you rename. If you must rename, keep the OLD name as an alias that
|
|
19
|
+
* forwards to the new implementation for at least one full major version.
|
|
20
|
+
*
|
|
21
|
+
* 2. NEVER REMOVE AN EXISTING TOOL.
|
|
22
|
+
* Deprecate it in the description ("[DEPRECATED: use X]") and keep it
|
|
23
|
+
* working. Only remove in a major version bump with release notes.
|
|
24
|
+
*
|
|
25
|
+
* 3. NEVER CHANGE AN EXISTING TOOL'S ARG NAMES, TYPES, OR REQUIRED STATUS
|
|
26
|
+
* IN A BACKWARD-INCOMPATIBLE WAY.
|
|
27
|
+
* Adding a new OPTIONAL arg with a sensible default is fine. Everything
|
|
28
|
+
* else below is forbidden in a minor release:
|
|
29
|
+
* - renaming `prompt` to `text`
|
|
30
|
+
* - making a previously-optional arg required
|
|
31
|
+
* - changing `aspect_ratio: string` to `aspect_ratio: { w, h }`
|
|
32
|
+
* - removing an arg (even one you think nobody uses)
|
|
33
|
+
*
|
|
34
|
+
* VERSION BUMPS
|
|
35
|
+
*
|
|
36
|
+
* - minor (1.1.0 → 1.2.0): new tool, new optional arg, description tweak
|
|
37
|
+
* - patch (1.1.0 → 1.1.1): internal refactor, bug fix with no user impact
|
|
38
|
+
* - major (1.1.0 → 2.0.0): ANY breaking change from commandments 1–3 above,
|
|
39
|
+
* AND only after going through the deprecation path in CLAUDE.md.
|
|
40
|
+
*
|
|
41
|
+
* WHY THIS MATTERS
|
|
42
|
+
*
|
|
43
|
+
* Users install via `npx -y @kolbo/mcp` — npx CACHES packages. A user who
|
|
44
|
+
* installed 3 months ago may still be running v1.0 until their cache
|
|
45
|
+
* invalidates. When their Claude Desktop starts the MCP server, it
|
|
46
|
+
* registers whatever tools ITS VERSION knows about. Their LLM sees that
|
|
47
|
+
* list and calls those names. You cannot force-update them.
|
|
48
|
+
*
|
|
49
|
+
* The matching backend SDK routes in
|
|
50
|
+
* `kolbo-api/src/modules/sdk/index.js` are the same kind of public
|
|
51
|
+
* contract and follow the same rules — never rename, never remove.
|
|
52
|
+
*
|
|
53
|
+
* Full rules, deprecation path, and parity-audit instructions: CLAUDE.md
|
|
54
|
+
*
|
|
55
|
+
* If you are a coding agent about to rename/remove a tool or arg: STOP and
|
|
56
|
+
* ask the human first. This is not optional.
|
|
57
|
+
* ==========================================================================*/
|
|
58
|
+
|
|
1
59
|
const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
|
|
2
60
|
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
3
61
|
const KolboClient = require('./client');
|
|
4
62
|
const { registerGenerateTools } = require('./tools/generate');
|
|
5
63
|
const { registerModelTools } = require('./tools/models');
|
|
64
|
+
const { registerChatTools } = require('./tools/chat');
|
|
65
|
+
const { registerVisualDnaTools } = require('./tools/visual_dna');
|
|
66
|
+
const { registerMoodboardTools } = require('./tools/moodboards');
|
|
6
67
|
|
|
7
68
|
async function main() {
|
|
8
69
|
const client = new KolboClient();
|
|
@@ -15,6 +76,9 @@ async function main() {
|
|
|
15
76
|
// Register all tools
|
|
16
77
|
registerGenerateTools(server, client);
|
|
17
78
|
registerModelTools(server, client);
|
|
79
|
+
registerChatTools(server, client);
|
|
80
|
+
registerVisualDnaTools(server, client);
|
|
81
|
+
registerMoodboardTools(server, client);
|
|
18
82
|
|
|
19
83
|
// Start the server with stdio transport
|
|
20
84
|
const transport = new StdioServerTransport();
|
package/src/polling.js
CHANGED
|
@@ -1,31 +1,57 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Poll a generation until it reaches a terminal state
|
|
3
3
|
*/
|
|
4
|
+
|
|
5
|
+
class PollingTimeoutError extends Error {
|
|
6
|
+
constructor(generationId, timeoutMs) {
|
|
7
|
+
const seconds = Math.round(timeoutMs / 1000);
|
|
8
|
+
super(
|
|
9
|
+
`Generation timed out after ${seconds}s of polling. The generation may STILL be running on the server — ` +
|
|
10
|
+
`call get_generation_status with generation_id="${generationId}" to check its current state. ` +
|
|
11
|
+
`Videos, deep-think chat, and large batches can take longer than the default polling window.`
|
|
12
|
+
);
|
|
13
|
+
this.name = 'PollingTimeoutError';
|
|
14
|
+
this.generationId = generationId;
|
|
15
|
+
this.timeoutMs = timeoutMs;
|
|
16
|
+
this.timedOut = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class GenerationFailedError extends Error {
|
|
21
|
+
constructor(generationId, reason) {
|
|
22
|
+
super(`Generation failed: ${reason || 'unknown error'} (generation_id="${generationId}")`);
|
|
23
|
+
this.name = 'GenerationFailedError';
|
|
24
|
+
this.generationId = generationId;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
4
28
|
async function pollUntilDone(client, generationId, options = {}) {
|
|
5
29
|
const {
|
|
6
30
|
interval = 5000,
|
|
7
|
-
timeout = 300000 // 5 minutes default
|
|
31
|
+
timeout = 300000, // 5 minutes default
|
|
32
|
+
statusUrl
|
|
8
33
|
} = options;
|
|
9
34
|
|
|
10
35
|
const startTime = Date.now();
|
|
36
|
+
const url = statusUrl || `/v1/generate/${generationId}/status`;
|
|
11
37
|
|
|
12
38
|
while (true) {
|
|
13
39
|
if (Date.now() - startTime > timeout) {
|
|
14
|
-
throw new
|
|
40
|
+
throw new PollingTimeoutError(generationId, timeout);
|
|
15
41
|
}
|
|
16
42
|
|
|
17
|
-
const result = await client.get(
|
|
43
|
+
const result = await client.get(url);
|
|
18
44
|
|
|
19
45
|
if (result.state === 'completed') {
|
|
20
46
|
return result;
|
|
21
47
|
}
|
|
22
48
|
|
|
23
49
|
if (result.state === 'failed') {
|
|
24
|
-
throw new
|
|
50
|
+
throw new GenerationFailedError(generationId, result.error);
|
|
25
51
|
}
|
|
26
52
|
|
|
27
53
|
if (result.state === 'cancelled') {
|
|
28
|
-
throw new
|
|
54
|
+
throw new GenerationFailedError(generationId, 'generation was cancelled');
|
|
29
55
|
}
|
|
30
56
|
|
|
31
57
|
// Wait before next poll
|
|
@@ -33,4 +59,4 @@ async function pollUntilDone(client, generationId, options = {}) {
|
|
|
33
59
|
}
|
|
34
60
|
}
|
|
35
61
|
|
|
36
|
-
module.exports = { pollUntilDone };
|
|
62
|
+
module.exports = { pollUntilDone, PollingTimeoutError, GenerationFailedError };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* ⛔ BACKWARD COMPATIBILITY: Tool names and arg names below are a PUBLIC
|
|
2
|
+
* CONTRACT. Never rename, remove, or break an existing tool/arg — old cached
|
|
3
|
+
* `npx @kolbo/mcp` installs in the wild will break silently. Add new tools or
|
|
4
|
+
* new OPTIONAL args only. Full rules: ../index.js top-of-file and CLAUDE.md. */
|
|
5
|
+
|
|
6
|
+
const { pollUntilDone } = require('../polling');
|
|
7
|
+
|
|
8
|
+
function registerChatTools(server, client) {
|
|
9
|
+
// ─── chat_send_message ─────────────────────────────────────
|
|
10
|
+
server.tool(
|
|
11
|
+
'chat_send_message',
|
|
12
|
+
'Send a chat message to Kolbo AI. Starts a new conversation (omit session_id) or continues an existing one. Returns the assistant response when complete. Supports web search and deep think modes.',
|
|
13
|
+
{
|
|
14
|
+
message: { type: 'string', description: 'The user message to send' },
|
|
15
|
+
model: { type: 'string', description: 'Model identifier (e.g. "gpt-4o", "claude-sonnet-4-5"). Omit for Smart Select (auto).' },
|
|
16
|
+
session_id: { type: 'string', description: 'Existing chat session ID to continue. Omit to start a new conversation.' },
|
|
17
|
+
system_prompt: { type: 'string', description: 'System prompt for the conversation. Only applied when creating a new session.' },
|
|
18
|
+
web_search: { type: 'boolean', description: 'Enable web search for this message. Default: false' },
|
|
19
|
+
deep_think: { type: 'boolean', description: 'Enable deep think (extended reasoning). Default: false' },
|
|
20
|
+
enhance_prompt: { type: 'boolean', description: 'Enhance the prompt. Default: true' }
|
|
21
|
+
},
|
|
22
|
+
async ({ message, model, session_id, system_prompt, web_search, deep_think, enhance_prompt }) => {
|
|
23
|
+
const gen = await client.post('/v1/chat', {
|
|
24
|
+
message,
|
|
25
|
+
model,
|
|
26
|
+
session_id,
|
|
27
|
+
system_prompt,
|
|
28
|
+
web_search,
|
|
29
|
+
deep_think,
|
|
30
|
+
enhance_prompt
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Deep think reasoning can run far longer than normal chat. Also grant
|
|
34
|
+
// extra time when web_search is on (may fetch + analyze multiple pages).
|
|
35
|
+
const timeout = deep_think ? 600000 : (web_search ? 240000 : 120000);
|
|
36
|
+
|
|
37
|
+
const result = await pollUntilDone(client, gen.message_id, {
|
|
38
|
+
interval: (gen.poll_interval_hint || 2) * 1000,
|
|
39
|
+
timeout
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Chat status shape (from extractResult in kolbo-api sdk/controller.js):
|
|
43
|
+
// { content, reasoning_content, image_urls?, video_urls?, audio_urls?, model, created_at }
|
|
44
|
+
const r = result.result || {};
|
|
45
|
+
return {
|
|
46
|
+
content: [{
|
|
47
|
+
type: 'text',
|
|
48
|
+
text: JSON.stringify({
|
|
49
|
+
session_id: gen.session_id,
|
|
50
|
+
message_id: gen.message_id,
|
|
51
|
+
model: r.model || gen.model,
|
|
52
|
+
content: r.content || '',
|
|
53
|
+
reasoning_content: r.reasoning_content || null,
|
|
54
|
+
image_urls: r.image_urls || null,
|
|
55
|
+
video_urls: r.video_urls || null,
|
|
56
|
+
audio_urls: r.audio_urls || null
|
|
57
|
+
}, null, 2)
|
|
58
|
+
}]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// ─── chat_list_conversations ───────────────────────────────
|
|
64
|
+
server.tool(
|
|
65
|
+
'chat_list_conversations',
|
|
66
|
+
'List your SDK chat conversations, most-recent first. Returns session_id, name, and activity timestamps.',
|
|
67
|
+
{
|
|
68
|
+
page: { type: 'number', description: 'Page number, 1-indexed. Default: 1' },
|
|
69
|
+
limit: { type: 'number', description: 'Results per page, max 50. Default: 20' }
|
|
70
|
+
},
|
|
71
|
+
async ({ page, limit }) => {
|
|
72
|
+
const params = new URLSearchParams();
|
|
73
|
+
if (page) params.set('page', String(page));
|
|
74
|
+
if (limit) params.set('limit', String(limit));
|
|
75
|
+
|
|
76
|
+
const qs = params.toString();
|
|
77
|
+
const result = await client.get(`/v1/chat/conversations${qs ? '?' + qs : ''}`);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
content: [{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: JSON.stringify({
|
|
83
|
+
conversations: result.conversations || [],
|
|
84
|
+
pagination: result.pagination || null
|
|
85
|
+
}, null, 2)
|
|
86
|
+
}]
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// ─── chat_get_messages ─────────────────────────────────────
|
|
92
|
+
server.tool(
|
|
93
|
+
'chat_get_messages',
|
|
94
|
+
'Fetch messages in a chat conversation. Returns role, content, model, and any media URLs attached to each message.',
|
|
95
|
+
{
|
|
96
|
+
session_id: { type: 'string', description: 'The chat session ID' },
|
|
97
|
+
page: { type: 'number', description: 'Page number, 1-indexed. Default: 1' },
|
|
98
|
+
limit: { type: 'number', description: 'Messages per page, max 100. Default: 50' }
|
|
99
|
+
},
|
|
100
|
+
async ({ session_id, page, limit }) => {
|
|
101
|
+
const params = new URLSearchParams();
|
|
102
|
+
if (page) params.set('page', String(page));
|
|
103
|
+
if (limit) params.set('limit', String(limit));
|
|
104
|
+
|
|
105
|
+
const qs = params.toString();
|
|
106
|
+
const result = await client.get(
|
|
107
|
+
`/v1/chat/conversations/${encodeURIComponent(session_id)}/messages${qs ? '?' + qs : ''}`
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Trim each message to avoid flooding context.
|
|
111
|
+
const messages = (result.messages || []).map(m => ({
|
|
112
|
+
role: m.role,
|
|
113
|
+
content: m.content,
|
|
114
|
+
model: m.model?.name || m.model?.identifier || null,
|
|
115
|
+
status: m.status,
|
|
116
|
+
created_at: m.createdAt || m.created_at,
|
|
117
|
+
image_url: m.image_url || null,
|
|
118
|
+
video_url: m.video_url || null,
|
|
119
|
+
audio_url: m.audio_url || null
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
content: [{
|
|
124
|
+
type: 'text',
|
|
125
|
+
text: JSON.stringify({
|
|
126
|
+
messages,
|
|
127
|
+
pagination: result.pagination || null
|
|
128
|
+
}, null, 2)
|
|
129
|
+
}]
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = { registerChatTools };
|