@lobehub/lobehub 2.0.0-next.314 → 2.0.0-next.315
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/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/server/services/aiAgent/index.ts +28 -11
- package/src/server/services/klavis/index.ts +228 -0
- package/src/server/services/toolExecution/builtin.ts +12 -0
- package/src/server/services/toolExecution/index.ts +70 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.315](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.314...v2.0.0-next.315)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-19**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Add the cloudEndpoint & Klavis Tools Call in Excuation Task.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Add the cloudEndpoint & Klavis Tools Call in Excuation Task, closes [#11627](https://github.com/lobehub/lobe-chat/issues/11627) ([0ffe6c4](https://github.com/lobehub/lobe-chat/commit/0ffe6c4))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.314](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.313...v2.0.0-next.314)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-19**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.315",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import { AgentService } from '@/server/services/agent';
|
|
29
29
|
import { AgentRuntimeService } from '@/server/services/agentRuntime';
|
|
30
30
|
import type { StepLifecycleCallbacks } from '@/server/services/agentRuntime/types';
|
|
31
|
+
import { KlavisService } from '@/server/services/klavis';
|
|
31
32
|
import { MarketService } from '@/server/services/market';
|
|
32
33
|
|
|
33
34
|
const log = debug('lobe-server:ai-agent-service');
|
|
@@ -93,6 +94,7 @@ export class AiAgentService {
|
|
|
93
94
|
private readonly topicModel: TopicModel;
|
|
94
95
|
private readonly agentRuntimeService: AgentRuntimeService;
|
|
95
96
|
private readonly marketService: MarketService;
|
|
97
|
+
private readonly klavisService: KlavisService;
|
|
96
98
|
|
|
97
99
|
constructor(db: LobeChatDatabase, userId: string) {
|
|
98
100
|
this.userId = userId;
|
|
@@ -105,6 +107,7 @@ export class AiAgentService {
|
|
|
105
107
|
this.topicModel = new TopicModel(db, userId);
|
|
106
108
|
this.agentRuntimeService = new AgentRuntimeService(db, userId);
|
|
107
109
|
this.marketService = new MarketService({ userInfo: { userId } });
|
|
110
|
+
this.klavisService = new KlavisService({ db, userId });
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
/**
|
|
@@ -205,7 +208,16 @@ export class AiAgentService {
|
|
|
205
208
|
}
|
|
206
209
|
log('execAgent: got %d lobehub skill manifests', lobehubSkillManifests.length);
|
|
207
210
|
|
|
208
|
-
// 6.
|
|
211
|
+
// 6. Fetch Klavis tool manifests from database
|
|
212
|
+
let klavisManifests: LobeToolManifest[] = [];
|
|
213
|
+
try {
|
|
214
|
+
klavisManifests = await this.klavisService.getKlavisManifests();
|
|
215
|
+
} catch (error) {
|
|
216
|
+
log('execAgent: failed to fetch klavis manifests: %O', error);
|
|
217
|
+
}
|
|
218
|
+
log('execAgent: got %d klavis manifests', klavisManifests.length);
|
|
219
|
+
|
|
220
|
+
// 7. Create tools using Server AgentToolsEngine
|
|
209
221
|
const hasEnabledKnowledgeBases =
|
|
210
222
|
agentConfig.knowledgeBases?.some((kb: { enabled?: boolean | null }) => kb.enabled === true) ??
|
|
211
223
|
false;
|
|
@@ -216,7 +228,7 @@ export class AiAgentService {
|
|
|
216
228
|
};
|
|
217
229
|
|
|
218
230
|
const toolsEngine = createServerAgentToolsEngine(toolsContext, {
|
|
219
|
-
additionalManifests: lobehubSkillManifests,
|
|
231
|
+
additionalManifests: [...lobehubSkillManifests, ...klavisManifests],
|
|
220
232
|
agentConfig: {
|
|
221
233
|
chatConfig: agentConfig.chatConfig ?? undefined,
|
|
222
234
|
plugins: agentConfig.plugins ?? undefined,
|
|
@@ -254,15 +266,20 @@ export class AiAgentService {
|
|
|
254
266
|
for (const manifest of lobehubSkillManifests) {
|
|
255
267
|
toolSourceMap[manifest.identifier] = 'lobehubSkill';
|
|
256
268
|
}
|
|
269
|
+
// Mark klavis tools
|
|
270
|
+
for (const manifest of klavisManifests) {
|
|
271
|
+
toolSourceMap[manifest.identifier] = 'klavis';
|
|
272
|
+
}
|
|
257
273
|
|
|
258
274
|
log(
|
|
259
|
-
'execAgent: generated %d tools from %d configured plugins, %d lobehub skills',
|
|
275
|
+
'execAgent: generated %d tools from %d configured plugins, %d lobehub skills, %d klavis tools',
|
|
260
276
|
tools?.length ?? 0,
|
|
261
277
|
pluginIds.length,
|
|
262
278
|
lobehubSkillManifests.length,
|
|
279
|
+
klavisManifests.length,
|
|
263
280
|
);
|
|
264
281
|
|
|
265
|
-
//
|
|
282
|
+
// 8. Get existing messages if provided
|
|
266
283
|
let historyMessages: any[] = [];
|
|
267
284
|
if (existingMessageIds.length > 0) {
|
|
268
285
|
historyMessages = await this.messageModel.query({
|
|
@@ -275,7 +292,7 @@ export class AiAgentService {
|
|
|
275
292
|
}
|
|
276
293
|
}
|
|
277
294
|
|
|
278
|
-
//
|
|
295
|
+
// 9. Create user message in database
|
|
279
296
|
// Include threadId if provided (for SubAgent task execution in isolated Thread)
|
|
280
297
|
const userMessageRecord = await this.messageModel.create({
|
|
281
298
|
agentId: resolvedAgentId,
|
|
@@ -286,7 +303,7 @@ export class AiAgentService {
|
|
|
286
303
|
});
|
|
287
304
|
log('execAgent: created user message %s', userMessageRecord.id);
|
|
288
305
|
|
|
289
|
-
//
|
|
306
|
+
// 10. Create assistant message placeholder in database
|
|
290
307
|
// Include threadId if provided (for SubAgent task execution in isolated Thread)
|
|
291
308
|
const assistantMessageRecord = await this.messageModel.create({
|
|
292
309
|
agentId: resolvedAgentId,
|
|
@@ -306,7 +323,7 @@ export class AiAgentService {
|
|
|
306
323
|
// Combine history messages with user message
|
|
307
324
|
const allMessages = [...historyMessages, userMessage];
|
|
308
325
|
|
|
309
|
-
//
|
|
326
|
+
// 11. Process messages using Server ContextEngineering
|
|
310
327
|
const processedMessages = await serverMessagesEngine({
|
|
311
328
|
capabilities: {
|
|
312
329
|
isCanUseFC: isModelSupportToolUse,
|
|
@@ -341,11 +358,11 @@ export class AiAgentService {
|
|
|
341
358
|
|
|
342
359
|
log('execAgent: processed %d messages', processedMessages.length);
|
|
343
360
|
|
|
344
|
-
//
|
|
361
|
+
// 12. Generate operation ID: agt_{timestamp}_{agentId}_{topicId}_{random}
|
|
345
362
|
const timestamp = Date.now();
|
|
346
363
|
const operationId = `op_${timestamp}_${resolvedAgentId}_${topicId}_${nanoid(8)}`;
|
|
347
364
|
|
|
348
|
-
//
|
|
365
|
+
// 13. Create initial context
|
|
349
366
|
const initialContext: AgentRuntimeContext = {
|
|
350
367
|
payload: {
|
|
351
368
|
// Pass assistant message ID so agent runtime knows which message to update
|
|
@@ -366,7 +383,7 @@ export class AiAgentService {
|
|
|
366
383
|
},
|
|
367
384
|
};
|
|
368
385
|
|
|
369
|
-
//
|
|
386
|
+
// 14. Log final operation parameters summary
|
|
370
387
|
log(
|
|
371
388
|
'execAgent: creating operation %s with params: model=%s, provider=%s, tools=%d, messages=%d, manifests=%d',
|
|
372
389
|
operationId,
|
|
@@ -377,7 +394,7 @@ export class AiAgentService {
|
|
|
377
394
|
Object.keys(toolManifestMap).length,
|
|
378
395
|
);
|
|
379
396
|
|
|
380
|
-
//
|
|
397
|
+
// 15. Create operation using AgentRuntimeService
|
|
381
398
|
// Wrap in try-catch to handle operation startup failures (e.g., QStash unavailable)
|
|
382
399
|
// If createOperation fails, we still have valid messages that need error info
|
|
383
400
|
try {
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { LobeToolManifest } from '@lobechat/context-engine';
|
|
2
|
+
import type { LobeChatDatabase } from '@lobechat/database';
|
|
3
|
+
import debug from 'debug';
|
|
4
|
+
|
|
5
|
+
import { PluginModel } from '@/database/models/plugin';
|
|
6
|
+
import { getKlavisClient, isKlavisClientAvailable } from '@/libs/klavis';
|
|
7
|
+
import { type ToolExecutionResult } from '@/server/services/toolExecution/types';
|
|
8
|
+
|
|
9
|
+
const log = debug('lobe-server:klavis-service');
|
|
10
|
+
|
|
11
|
+
export interface KlavisToolExecuteParams {
|
|
12
|
+
args: Record<string, any>;
|
|
13
|
+
/** Tool identifier (same as Klavis server identifier, e.g., 'google-calendar') */
|
|
14
|
+
identifier: string;
|
|
15
|
+
toolName: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface KlavisServiceOptions {
|
|
19
|
+
db?: LobeChatDatabase;
|
|
20
|
+
userId?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Klavis Service
|
|
25
|
+
*
|
|
26
|
+
* Provides a unified interface to Klavis Client with business logic encapsulation.
|
|
27
|
+
* This service wraps Klavis Client methods to execute tools and fetch manifests.
|
|
28
|
+
*
|
|
29
|
+
* Usage:
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // With database and userId (for manifest fetching)
|
|
32
|
+
* const service = new KlavisService({ db, userId });
|
|
33
|
+
* await service.executeKlavisTool({ identifier, toolName, args });
|
|
34
|
+
*
|
|
35
|
+
* // Without database (for tool execution only if you have serverUrl)
|
|
36
|
+
* const service = new KlavisService();
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class KlavisService {
|
|
40
|
+
private db?: LobeChatDatabase;
|
|
41
|
+
private userId?: string;
|
|
42
|
+
private pluginModel?: PluginModel;
|
|
43
|
+
|
|
44
|
+
constructor(options: KlavisServiceOptions = {}) {
|
|
45
|
+
const { db, userId } = options;
|
|
46
|
+
|
|
47
|
+
this.db = db;
|
|
48
|
+
this.userId = userId;
|
|
49
|
+
|
|
50
|
+
if (db && userId) {
|
|
51
|
+
this.pluginModel = new PluginModel(db, userId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
log(
|
|
55
|
+
'KlavisService initialized: hasDB=%s, hasUserId=%s, isClientAvailable=%s',
|
|
56
|
+
!!db,
|
|
57
|
+
!!userId,
|
|
58
|
+
isKlavisClientAvailable(),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Execute a Klavis tool
|
|
64
|
+
* @param params - Tool execution parameters
|
|
65
|
+
* @returns Tool execution result
|
|
66
|
+
*/
|
|
67
|
+
async executeKlavisTool(params: KlavisToolExecuteParams): Promise<ToolExecutionResult> {
|
|
68
|
+
const { identifier, toolName, args } = params;
|
|
69
|
+
|
|
70
|
+
log('executeKlavisTool: %s/%s with args: %O', identifier, toolName, args);
|
|
71
|
+
|
|
72
|
+
// Check if Klavis client is available
|
|
73
|
+
if (!isKlavisClientAvailable()) {
|
|
74
|
+
return {
|
|
75
|
+
content: 'Klavis service is not configured on server',
|
|
76
|
+
error: { code: 'KLAVIS_NOT_CONFIGURED', message: 'Klavis API key not found' },
|
|
77
|
+
success: false,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Get serverUrl from plugin database
|
|
82
|
+
if (!this.pluginModel) {
|
|
83
|
+
return {
|
|
84
|
+
content: 'Klavis service is not properly initialized',
|
|
85
|
+
error: {
|
|
86
|
+
code: 'KLAVIS_NOT_INITIALIZED',
|
|
87
|
+
message: 'Database and userId are required for Klavis tool execution',
|
|
88
|
+
},
|
|
89
|
+
success: false,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Get plugin from database to retrieve serverUrl
|
|
95
|
+
const plugin = await this.pluginModel.findById(identifier);
|
|
96
|
+
if (!plugin) {
|
|
97
|
+
return {
|
|
98
|
+
content: `Klavis server "${identifier}" not found in database`,
|
|
99
|
+
error: { code: 'KLAVIS_SERVER_NOT_FOUND', message: `Server ${identifier} not found` },
|
|
100
|
+
success: false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const klavisParams = plugin.customParams?.klavis;
|
|
105
|
+
if (!klavisParams || !klavisParams.serverUrl) {
|
|
106
|
+
return {
|
|
107
|
+
content: `Klavis configuration not found for server "${identifier}"`,
|
|
108
|
+
error: {
|
|
109
|
+
code: 'KLAVIS_CONFIG_NOT_FOUND',
|
|
110
|
+
message: `Klavis configuration missing for ${identifier}`,
|
|
111
|
+
},
|
|
112
|
+
success: false,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const { serverUrl } = klavisParams;
|
|
117
|
+
|
|
118
|
+
log('executeKlavisTool: calling Klavis API with serverUrl=%s', serverUrl);
|
|
119
|
+
|
|
120
|
+
// Call Klavis client
|
|
121
|
+
const klavisClient = getKlavisClient();
|
|
122
|
+
const response = await klavisClient.mcpServer.callTools({
|
|
123
|
+
serverUrl,
|
|
124
|
+
toolArgs: args,
|
|
125
|
+
toolName,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
log('executeKlavisTool: response: %O', response);
|
|
129
|
+
|
|
130
|
+
// Handle error case
|
|
131
|
+
if (!response.success || !response.result) {
|
|
132
|
+
return {
|
|
133
|
+
content: response.error || 'Unknown error',
|
|
134
|
+
error: { code: 'KLAVIS_EXECUTION_ERROR', message: response.error || 'Unknown error' },
|
|
135
|
+
success: false,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Process the response
|
|
140
|
+
const content = response.result.content || [];
|
|
141
|
+
const isError = response.result.isError || false;
|
|
142
|
+
|
|
143
|
+
// Convert content array to string
|
|
144
|
+
let resultContent = '';
|
|
145
|
+
if (Array.isArray(content)) {
|
|
146
|
+
resultContent = content
|
|
147
|
+
.map((item: any) => {
|
|
148
|
+
if (typeof item === 'string') return item;
|
|
149
|
+
if (item.type === 'text' && item.text) return item.text;
|
|
150
|
+
return JSON.stringify(item);
|
|
151
|
+
})
|
|
152
|
+
.join('\n');
|
|
153
|
+
} else if (typeof content === 'string') {
|
|
154
|
+
resultContent = content;
|
|
155
|
+
} else {
|
|
156
|
+
resultContent = JSON.stringify(content);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
content: resultContent,
|
|
161
|
+
success: !isError,
|
|
162
|
+
};
|
|
163
|
+
} catch (error) {
|
|
164
|
+
const err = error as Error;
|
|
165
|
+
console.error('KlavisService.executeKlavisTool error %s/%s: %O', identifier, toolName, err);
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
content: err.message,
|
|
169
|
+
error: { code: 'KLAVIS_ERROR', message: err.message },
|
|
170
|
+
success: false,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Fetch Klavis tool manifests from database
|
|
177
|
+
* Gets user's connected Klavis servers and builds tool manifests for agent execution
|
|
178
|
+
*
|
|
179
|
+
* @returns Array of tool manifests for connected Klavis servers
|
|
180
|
+
*/
|
|
181
|
+
async getKlavisManifests(): Promise<LobeToolManifest[]> {
|
|
182
|
+
if (!this.pluginModel) {
|
|
183
|
+
log('getKlavisManifests: pluginModel not available, returning empty array');
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
// Get all plugins from database
|
|
189
|
+
const allPlugins = await this.pluginModel.query();
|
|
190
|
+
|
|
191
|
+
// Filter plugins that have klavis customParams and are authenticated
|
|
192
|
+
const klavisPlugins = allPlugins.filter(
|
|
193
|
+
(plugin) => plugin.customParams?.klavis?.isAuthenticated === true,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
log('getKlavisManifests: found %d authenticated Klavis plugins', klavisPlugins.length);
|
|
197
|
+
|
|
198
|
+
// Convert to LobeToolManifest format
|
|
199
|
+
const manifests: LobeToolManifest[] = klavisPlugins
|
|
200
|
+
.map((plugin) => {
|
|
201
|
+
if (!plugin.manifest) return null;
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
api: plugin.manifest.api || [],
|
|
205
|
+
author: 'Klavis',
|
|
206
|
+
homepage: 'https://klavis.ai',
|
|
207
|
+
identifier: plugin.identifier,
|
|
208
|
+
meta: plugin.manifest.meta || {
|
|
209
|
+
avatar: '☁️',
|
|
210
|
+
description: `Klavis MCP Server: ${plugin.customParams?.klavis?.serverName}`,
|
|
211
|
+
tags: ['klavis', 'mcp'],
|
|
212
|
+
title: plugin.customParams?.klavis?.serverName || plugin.identifier,
|
|
213
|
+
},
|
|
214
|
+
type: 'builtin',
|
|
215
|
+
version: '1.0.0',
|
|
216
|
+
};
|
|
217
|
+
})
|
|
218
|
+
.filter(Boolean) as LobeToolManifest[];
|
|
219
|
+
|
|
220
|
+
log('getKlavisManifests: returning %d manifests', manifests.length);
|
|
221
|
+
|
|
222
|
+
return manifests;
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('KlavisService.getKlavisManifests error: %O', error);
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -3,6 +3,7 @@ import { type ChatToolPayload } from '@lobechat/types';
|
|
|
3
3
|
import { safeParseJSON } from '@lobechat/utils';
|
|
4
4
|
import debug from 'debug';
|
|
5
5
|
|
|
6
|
+
import { KlavisService } from '@/server/services/klavis';
|
|
6
7
|
import { MarketService } from '@/server/services/market';
|
|
7
8
|
|
|
8
9
|
import { getServerRuntime, hasServerRuntime } from './serverRuntimes';
|
|
@@ -12,9 +13,11 @@ const log = debug('lobe-server:builtin-tools-executor');
|
|
|
12
13
|
|
|
13
14
|
export class BuiltinToolsExecutor implements IToolExecutor {
|
|
14
15
|
private marketService: MarketService;
|
|
16
|
+
private klavisService: KlavisService;
|
|
15
17
|
|
|
16
18
|
constructor(db: LobeChatDatabase, userId: string) {
|
|
17
19
|
this.marketService = new MarketService({ userInfo: { userId } });
|
|
20
|
+
this.klavisService = new KlavisService({ db, userId });
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
async execute(
|
|
@@ -41,6 +44,15 @@ export class BuiltinToolsExecutor implements IToolExecutor {
|
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
// Route Klavis tools to KlavisService
|
|
48
|
+
if (source === 'klavis') {
|
|
49
|
+
return this.klavisService.executeKlavisTool({
|
|
50
|
+
args,
|
|
51
|
+
identifier,
|
|
52
|
+
toolName: apiName,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
// Use server runtime registry (handles both pre-instantiated and per-request runtimes)
|
|
45
57
|
if (!hasServerRuntime(identifier)) {
|
|
46
58
|
throw new Error(`Builtin tool "${identifier}" is not implemented`);
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type ChatToolPayload } from '@lobechat/types';
|
|
2
|
+
import { safeParseJSON } from '@lobechat/utils';
|
|
2
3
|
import debug from 'debug';
|
|
3
4
|
|
|
5
|
+
import { type CloudMCPParams, type ToolCallContent } from '@/libs/mcp';
|
|
6
|
+
import { contentBlocksToString } from '@/server/services/mcp/contentProcessor';
|
|
7
|
+
|
|
8
|
+
import { DiscoverService } from '../discover';
|
|
4
9
|
import { type MCPService } from '../mcp';
|
|
5
10
|
import { type PluginGatewayService } from '../pluginGateway';
|
|
6
11
|
import { type BuiltinToolsExecutor } from './builtin';
|
|
@@ -121,12 +126,20 @@ export class ToolExecutionService {
|
|
|
121
126
|
};
|
|
122
127
|
}
|
|
123
128
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
log(
|
|
130
|
+
'Calling MCP service with params for: %s:%s (type: %s)',
|
|
131
|
+
identifier,
|
|
132
|
+
apiName,
|
|
133
|
+
mcpParams.type,
|
|
134
|
+
);
|
|
127
135
|
|
|
128
136
|
try {
|
|
129
|
-
//
|
|
137
|
+
// Check if this is a cloud MCP endpoint
|
|
138
|
+
if (mcpParams.type === 'cloud') {
|
|
139
|
+
return await this.executeCloudMCPTool(payload, context, mcpParams);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// For stdio/http/sse types, use standard MCP service
|
|
130
143
|
const result = await this.mcpService.callTool({
|
|
131
144
|
argsStr: args,
|
|
132
145
|
clientParams: mcpParams,
|
|
@@ -152,6 +165,59 @@ export class ToolExecutionService {
|
|
|
152
165
|
};
|
|
153
166
|
}
|
|
154
167
|
}
|
|
168
|
+
|
|
169
|
+
private async executeCloudMCPTool(
|
|
170
|
+
payload: ChatToolPayload,
|
|
171
|
+
context: ToolExecutionContext,
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
173
|
+
_mcpParams: CloudMCPParams,
|
|
174
|
+
): Promise<ToolExecutionResult> {
|
|
175
|
+
const { identifier, apiName, arguments: args } = payload;
|
|
176
|
+
|
|
177
|
+
log('Executing Cloud MCP tool: %s:%s via cloud gateway', identifier, apiName);
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
// Create DiscoverService with user context
|
|
181
|
+
const discoverService = new DiscoverService({
|
|
182
|
+
userInfo: context.userId ? { userId: context.userId } : undefined,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Parse arguments
|
|
186
|
+
const apiParams = safeParseJSON(args) || {};
|
|
187
|
+
|
|
188
|
+
// Call cloud MCP endpoint via Market API
|
|
189
|
+
// Returns CloudGatewayResponse: { content: ToolCallContent[], isError?: boolean }
|
|
190
|
+
const cloudResult = await discoverService.callCloudMcpEndpoint({
|
|
191
|
+
apiParams,
|
|
192
|
+
identifier,
|
|
193
|
+
toolName: apiName,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const cloudResultContent = (cloudResult?.content ?? []) as ToolCallContent[];
|
|
197
|
+
|
|
198
|
+
// Convert content blocks to string (same as market router does)
|
|
199
|
+
const content = contentBlocksToString(cloudResultContent);
|
|
200
|
+
const state = { ...cloudResult, content: cloudResultContent };
|
|
201
|
+
|
|
202
|
+
log('Cloud MCP tool execution successful for: %s:%s', identifier, apiName);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
content,
|
|
206
|
+
state,
|
|
207
|
+
success: !cloudResult?.isError,
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
log('Cloud MCP tool execution failed for %s:%s: %O', identifier, apiName, error);
|
|
211
|
+
return {
|
|
212
|
+
content: (error as Error).message,
|
|
213
|
+
error: {
|
|
214
|
+
code: 'CLOUD_MCP_EXECUTION_ERROR',
|
|
215
|
+
message: (error as Error).message,
|
|
216
|
+
},
|
|
217
|
+
success: false,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
155
221
|
}
|
|
156
222
|
|
|
157
223
|
export * from './types';
|