@lobehub/lobehub 2.0.0-next.160 → 2.0.0-next.162

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.
Files changed (96) hide show
  1. package/.env.example +10 -0
  2. package/CHANGELOG.md +42 -0
  3. package/changelog/v1.json +14 -0
  4. package/e2e/src/steps/hooks.ts +1 -0
  5. package/locales/ar/authError.json +40 -0
  6. package/locales/ar/setting.json +25 -0
  7. package/locales/bg-BG/authError.json +40 -0
  8. package/locales/bg-BG/setting.json +25 -0
  9. package/locales/de-DE/authError.json +40 -0
  10. package/locales/de-DE/setting.json +25 -0
  11. package/locales/en-US/authError.json +40 -0
  12. package/locales/en-US/setting.json +25 -0
  13. package/locales/es-ES/authError.json +40 -0
  14. package/locales/es-ES/setting.json +25 -0
  15. package/locales/fa-IR/authError.json +40 -0
  16. package/locales/fa-IR/setting.json +25 -0
  17. package/locales/fr-FR/authError.json +40 -0
  18. package/locales/fr-FR/setting.json +25 -0
  19. package/locales/it-IT/authError.json +40 -0
  20. package/locales/it-IT/setting.json +25 -0
  21. package/locales/ja-JP/authError.json +40 -0
  22. package/locales/ja-JP/setting.json +25 -0
  23. package/locales/ko-KR/authError.json +40 -0
  24. package/locales/ko-KR/setting.json +25 -0
  25. package/locales/nl-NL/authError.json +40 -0
  26. package/locales/nl-NL/setting.json +25 -0
  27. package/locales/pl-PL/authError.json +40 -0
  28. package/locales/pl-PL/setting.json +25 -0
  29. package/locales/pt-BR/authError.json +40 -0
  30. package/locales/pt-BR/setting.json +25 -0
  31. package/locales/ru-RU/authError.json +40 -0
  32. package/locales/ru-RU/setting.json +25 -0
  33. package/locales/tr-TR/authError.json +40 -0
  34. package/locales/tr-TR/setting.json +25 -0
  35. package/locales/vi-VN/authError.json +40 -0
  36. package/locales/vi-VN/setting.json +25 -0
  37. package/locales/zh-CN/authError.json +40 -0
  38. package/locales/zh-CN/setting.json +25 -0
  39. package/locales/zh-TW/authError.json +40 -0
  40. package/locales/zh-TW/setting.json +25 -0
  41. package/next.config.ts +13 -1
  42. package/package.json +3 -1
  43. package/packages/const/src/index.ts +1 -0
  44. package/packages/const/src/klavis.ts +163 -0
  45. package/packages/database/migrations/meta/_journal.json +1 -1
  46. package/packages/database/src/core/migrations.json +1 -1
  47. package/packages/database/src/models/plugin.ts +1 -1
  48. package/packages/types/src/message/common/tools.ts +9 -0
  49. package/packages/types/src/serverConfig.ts +1 -0
  50. package/packages/types/src/tool/plugin.ts +10 -0
  51. package/src/app/[variants]/(auth)/auth-error/page.tsx +59 -0
  52. package/src/auth.ts +13 -48
  53. package/src/config/klavis.ts +41 -0
  54. package/src/envs/redis.ts +1 -1
  55. package/src/features/ChatInput/ActionBar/Tools/KlavisServerItem.tsx +351 -0
  56. package/src/features/ChatInput/ActionBar/Tools/index.tsx +56 -4
  57. package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +174 -6
  58. package/src/features/ChatInput/ActionBar/components/ActionDropdown.tsx +3 -1
  59. package/src/helpers/toolEngineering/index.test.ts +3 -0
  60. package/src/helpers/toolEngineering/index.ts +13 -2
  61. package/src/libs/better-auth/utils/config.ts +91 -0
  62. package/src/libs/klavis/index.ts +36 -0
  63. package/src/libs/redis/manager.ts +5 -1
  64. package/src/libs/redis/redis.test.ts +1 -1
  65. package/src/libs/redis/upstash.test.ts +9 -5
  66. package/src/libs/redis/upstash.ts +44 -20
  67. package/src/locales/default/authError.ts +40 -0
  68. package/src/locales/default/index.ts +2 -0
  69. package/src/locales/default/setting.ts +25 -0
  70. package/src/proxy.ts +1 -0
  71. package/src/server/globalConfig/index.ts +2 -0
  72. package/src/server/routers/lambda/index.ts +2 -0
  73. package/src/server/routers/lambda/klavis.ts +249 -0
  74. package/src/server/routers/tools/index.ts +2 -0
  75. package/src/server/routers/tools/klavis.ts +80 -0
  76. package/src/server/services/mcp/index.ts +61 -15
  77. package/src/services/import/index.test.ts +658 -0
  78. package/src/services/mcp.test.ts +1 -1
  79. package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +2 -3
  80. package/src/store/chat/slices/plugin/action.test.ts +0 -1
  81. package/src/store/chat/slices/plugin/actions/internals.ts +22 -2
  82. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +108 -0
  83. package/src/store/serverConfig/index.ts +1 -1
  84. package/src/store/serverConfig/selectors.ts +1 -0
  85. package/src/store/tool/initialState.ts +4 -1
  86. package/src/store/tool/selectors/index.ts +1 -0
  87. package/src/store/tool/slices/builtin/selectors.ts +25 -3
  88. package/src/store/tool/slices/klavisStore/action.test.ts +512 -0
  89. package/src/store/tool/slices/klavisStore/action.ts +375 -0
  90. package/src/store/tool/slices/klavisStore/index.ts +4 -0
  91. package/src/store/tool/slices/klavisStore/initialState.ts +25 -0
  92. package/src/store/tool/slices/klavisStore/selectors.test.ts +371 -0
  93. package/src/store/tool/slices/klavisStore/selectors.ts +123 -0
  94. package/src/store/tool/slices/klavisStore/types.ts +100 -0
  95. package/src/store/tool/slices/plugin/selectors.ts +16 -13
  96. package/src/store/tool/store.ts +4 -1
@@ -779,12 +779,37 @@ export default {
779
779
  groupName: '内置插件',
780
780
  },
781
781
  disabled: '当前模型不支持函数调用,无法使用插件',
782
+ klavis: {
783
+ addServer: '添加服务器',
784
+ authCompleted: '认证完成',
785
+ authFailed: '认证失败',
786
+ authRequired: '需要认证',
787
+ connected: '已连接',
788
+ error: '错误',
789
+ groupName: 'Klavis 工具',
790
+ manage: '管理 Klavis',
791
+ manageTitle: '管理 Klavis 集成',
792
+ noServers: '暂无连接的服务器',
793
+ notEnabled: 'Klavis 服务未启用',
794
+ oauthRequired: '请在新窗口中完成 OAuth 认证',
795
+ pendingAuth: '待认证',
796
+ serverCreated: '服务器创建成功',
797
+ serverCreatedFailed: '服务器创建失败',
798
+ serverRemoved: '服务器已删除',
799
+ servers: '个服务器',
800
+ tools: '个工具',
801
+ verifyAuth: '我已完成认证',
802
+ },
782
803
  plugins: {
783
804
  enabled: '已启用 {{num}}',
784
805
  groupName: '三方插件',
785
806
  noEnabled: '暂无启用插件',
786
807
  store: '插件商店',
787
808
  },
809
+ tabs: {
810
+ all: '全部',
811
+ installed: '已启用',
812
+ },
788
813
  title: '扩展插件',
789
814
  },
790
815
  };
package/src/proxy.ts CHANGED
@@ -52,6 +52,7 @@ export const config = {
52
52
  '/signin(.*)',
53
53
  '/verify-email(.*)',
54
54
  '/reset-password(.*)',
55
+ '/auth-error(.*)',
55
56
  '/next-auth/(.*)',
56
57
  '/oauth(.*)',
57
58
  '/oidc(.*)',
@@ -1,3 +1,4 @@
1
+ import { klavisEnv } from '@/config/klavis';
1
2
  import { isDesktop } from '@/const/version';
2
3
  import { appEnv, getAppConfig } from '@/envs/app';
3
4
  import { authEnv } from '@/envs/auth';
@@ -66,6 +67,7 @@ export const getServerGlobalConfig = async () => {
66
67
  defaultAgent: {
67
68
  config: parseAgentConfig(DEFAULT_AGENT_CONFIG),
68
69
  },
70
+ enableKlavis: !!klavisEnv.KLAVIS_API_KEY,
69
71
  enableUploadFileToServer: !!fileEnv.S3_SECRET_ACCESS_KEY,
70
72
  enabledAccessCode: ACCESS_CODES?.length > 0,
71
73
 
@@ -20,6 +20,7 @@ import { generationTopicRouter } from './generationTopic';
20
20
  import { groupRouter } from './group';
21
21
  import { imageRouter } from './image';
22
22
  import { importerRouter } from './importer';
23
+ import { klavisRouter } from './klavis';
23
24
  import { knowledgeBaseRouter } from './knowledgeBase';
24
25
  import { marketRouter } from './market';
25
26
  import { messageRouter } from './message';
@@ -52,6 +53,7 @@ export const lambdaRouter = router({
52
53
  healthcheck: publicProcedure.query(() => "i'm live!"),
53
54
  image: imageRouter,
54
55
  importer: importerRouter,
56
+ klavis: klavisRouter,
55
57
  knowledgeBase: knowledgeBaseRouter,
56
58
  market: marketRouter,
57
59
  message: messageRouter,
@@ -0,0 +1,249 @@
1
+ import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
2
+ import { z } from 'zod';
3
+
4
+ import { PluginModel } from '@/database/models/plugin';
5
+ import { getKlavisClient } from '@/libs/klavis';
6
+ import { authedProcedure, router } from '@/libs/trpc/lambda';
7
+ import { serverDatabase } from '@/libs/trpc/lambda/middleware';
8
+
9
+ /**
10
+ * Klavis procedure with API key validation and database access
11
+ */
12
+ const klavisProcedure = authedProcedure.use(serverDatabase).use(async (opts) => {
13
+ const client = getKlavisClient();
14
+ const pluginModel = new PluginModel(opts.ctx.serverDB, opts.ctx.userId);
15
+
16
+ return opts.next({
17
+ ctx: { ...opts.ctx, klavisClient: client, pluginModel },
18
+ });
19
+ });
20
+
21
+ export const klavisRouter = router({
22
+ /**
23
+ * Create a single MCP server instance and save to database
24
+ * Returns: { serverUrl, instanceId, oauthUrl?, identifier, serverName }
25
+ */
26
+ createServerInstance: klavisProcedure
27
+ .input(
28
+ z.object({
29
+ /** Identifier for storage (e.g., 'google-calendar') */
30
+ identifier: z.string(),
31
+ /** Server name for Klavis API (e.g., 'Google Calendar') */
32
+ serverName: z.string(),
33
+ userId: z.string(),
34
+ }),
35
+ )
36
+ .mutation(async ({ input, ctx }) => {
37
+ const { serverName, userId, identifier } = input;
38
+
39
+ // 创建单个服务器实例
40
+ const response = await ctx.klavisClient.mcpServer.createServerInstance({
41
+ serverName: serverName as any,
42
+ userId,
43
+ });
44
+
45
+ const { serverUrl, instanceId, oauthUrl } = response;
46
+
47
+ // 获取该服务器的工具列表
48
+ const toolsResponse = await ctx.klavisClient.mcpServer.getTools(serverName as any);
49
+ const tools = toolsResponse.tools || [];
50
+
51
+ // 保存到数据库,使用传入的 identifier(格式:小写,空格替换为连字符)
52
+ const manifest: LobeChatPluginManifest = {
53
+ api: tools.map((tool: any) => ({
54
+ description: tool.description || '',
55
+ name: tool.name,
56
+ parameters: tool.inputSchema || { properties: {}, type: 'object' },
57
+ })),
58
+ identifier,
59
+ meta: {
60
+ avatar: '🔌',
61
+ description: `Klavis MCP Server: ${serverName}`,
62
+ title: serverName,
63
+ },
64
+ type: 'default',
65
+ };
66
+
67
+ // 保存到数据库,包含 oauthUrl 和 isAuthenticated 状态
68
+ const isAuthenticated = !oauthUrl; // 如果没有 oauthUrl,说明不需要认证或已认证
69
+ await ctx.pluginModel.create({
70
+ customParams: {
71
+ klavis: {
72
+ instanceId,
73
+ isAuthenticated,
74
+ oauthUrl,
75
+ serverName,
76
+ serverUrl,
77
+ },
78
+ },
79
+ identifier,
80
+ manifest,
81
+ source: 'klavis',
82
+ type: 'plugin',
83
+ });
84
+
85
+ return {
86
+ identifier,
87
+ instanceId,
88
+ isAuthenticated,
89
+ oauthUrl,
90
+ serverName,
91
+ serverUrl,
92
+ };
93
+ }),
94
+
95
+ /**
96
+ * Delete a server instance
97
+ */
98
+ deleteServerInstance: klavisProcedure
99
+ .input(
100
+ z.object({
101
+ /** Identifier for storage (e.g., 'google-calendar') */
102
+ identifier: z.string(),
103
+ instanceId: z.string(),
104
+ }),
105
+ )
106
+ .mutation(async ({ input, ctx }) => {
107
+ // 调用 Klavis API 删除服务器实例
108
+ await ctx.klavisClient.mcpServer.deleteServerInstance(input.instanceId);
109
+
110
+ // 从数据库删除(使用 identifier)
111
+ await ctx.pluginModel.delete(input.identifier);
112
+
113
+ return { success: true };
114
+ }),
115
+
116
+ /**
117
+ * Get Klavis plugins from database
118
+ */
119
+ getKlavisPlugins: klavisProcedure.query(async ({ ctx }) => {
120
+ const allPlugins = await ctx.pluginModel.query();
121
+ // Filter plugins that have klavis customParams
122
+ return allPlugins.filter((plugin) => plugin.customParams?.klavis);
123
+ }),
124
+
125
+ /**
126
+ * Get server instance status from Klavis API
127
+ */
128
+ getServerInstance: klavisProcedure
129
+ .input(
130
+ z.object({
131
+ instanceId: z.string(),
132
+ }),
133
+ )
134
+ .query(async ({ input, ctx }) => {
135
+ const response = await ctx.klavisClient.mcpServer.getServerInstance(input.instanceId);
136
+ return {
137
+ authNeeded: response.authNeeded,
138
+ externalUserId: response.externalUserId,
139
+ instanceId: response.instanceId,
140
+ isAuthenticated: response.isAuthenticated,
141
+ oauthUrl: response.oauthUrl,
142
+ platform: response.platform,
143
+ serverName: response.serverName,
144
+ };
145
+ }),
146
+
147
+ getUserIntergrations: klavisProcedure
148
+ .input(
149
+ z.object({
150
+ userId: z.string(),
151
+ }),
152
+ )
153
+ .query(async ({ input, ctx }) => {
154
+ const response = await ctx.klavisClient.user.getUserIntegrations(input.userId);
155
+
156
+ return {
157
+ integrations: response.integrations,
158
+ };
159
+ }),
160
+
161
+ /**
162
+ * Remove Klavis plugin from database by identifier
163
+ */
164
+ removeKlavisPlugin: klavisProcedure
165
+ .input(
166
+ z.object({
167
+ /** Identifier for storage (e.g., 'google-calendar') */
168
+ identifier: z.string(),
169
+ }),
170
+ )
171
+ .mutation(async ({ input, ctx }) => {
172
+ await ctx.pluginModel.delete(input.identifier);
173
+ return { success: true };
174
+ }),
175
+
176
+ /**
177
+ * Update Klavis plugin with tools and auth status in database
178
+ */
179
+ updateKlavisPlugin: klavisProcedure
180
+ .input(
181
+ z.object({
182
+ /** Identifier for storage (e.g., 'google-calendar') */
183
+ identifier: z.string(),
184
+ instanceId: z.string(),
185
+ isAuthenticated: z.boolean(),
186
+ oauthUrl: z.string().optional(),
187
+ /** Server name for Klavis API (e.g., 'Google Calendar') */
188
+ serverName: z.string(),
189
+ serverUrl: z.string(),
190
+ tools: z.array(
191
+ z.object({
192
+ description: z.string().optional(),
193
+ inputSchema: z.any().optional(),
194
+ name: z.string(),
195
+ }),
196
+ ),
197
+ }),
198
+ )
199
+ .mutation(async ({ input, ctx }) => {
200
+ const { identifier, serverName, serverUrl, instanceId, tools, isAuthenticated, oauthUrl } =
201
+ input;
202
+
203
+ // 获取现有插件(使用 identifier)
204
+ const existingPlugin = await ctx.pluginModel.findById(identifier);
205
+
206
+ // 构建包含所有工具的 manifest
207
+ const manifest: LobeChatPluginManifest = {
208
+ api: tools.map((tool) => ({
209
+ description: tool.description || '',
210
+ name: tool.name,
211
+ parameters: tool.inputSchema || { properties: {}, type: 'object' },
212
+ })),
213
+ identifier,
214
+ meta: existingPlugin?.manifest?.meta || {
215
+ avatar: '🔌',
216
+ description: `Klavis MCP Server: ${serverName}`,
217
+ title: serverName,
218
+ },
219
+ type: 'default',
220
+ };
221
+
222
+ const customParams = {
223
+ klavis: {
224
+ instanceId,
225
+ isAuthenticated,
226
+ oauthUrl,
227
+ serverName,
228
+ serverUrl,
229
+ },
230
+ };
231
+
232
+ // 更新或创建插件
233
+ if (existingPlugin) {
234
+ await ctx.pluginModel.update(identifier, { customParams, manifest });
235
+ } else {
236
+ await ctx.pluginModel.create({
237
+ customParams,
238
+ identifier,
239
+ manifest,
240
+ source: 'klavis',
241
+ type: 'plugin',
242
+ });
243
+ }
244
+
245
+ return { savedCount: tools.length };
246
+ }),
247
+ });
248
+
249
+ export type KlavisRouter = typeof klavisRouter;
@@ -1,10 +1,12 @@
1
1
  import { publicProcedure, router } from '@/libs/trpc/lambda';
2
2
 
3
+ import { klavisRouter } from './klavis';
3
4
  import { mcpRouter } from './mcp';
4
5
  import { searchRouter } from './search';
5
6
 
6
7
  export const toolsRouter = router({
7
8
  healthcheck: publicProcedure.query(() => "i'm live!"),
9
+ klavis: klavisRouter,
8
10
  mcp: mcpRouter,
9
11
  search: searchRouter,
10
12
  });
@@ -0,0 +1,80 @@
1
+ import { z } from 'zod';
2
+
3
+ import { getKlavisClient } from '@/libs/klavis';
4
+ import { authedProcedure, router } from '@/libs/trpc/lambda';
5
+ import { MCPService } from '@/server/services/mcp';
6
+
7
+ /**
8
+ * Klavis procedure with client initialized in context
9
+ */
10
+ const klavisProcedure = authedProcedure.use(async (opts) => {
11
+ const klavisClient = getKlavisClient();
12
+
13
+ return opts.next({
14
+ ctx: { ...opts.ctx, klavisClient },
15
+ });
16
+ });
17
+
18
+ /**
19
+ * Klavis router for tools
20
+ * Contains callTool and listTools which call external Klavis API
21
+ */
22
+ export const klavisRouter = router({
23
+ /**
24
+ * Call a tool on a Klavis Strata server
25
+ */
26
+ callTool: klavisProcedure
27
+ .input(
28
+ z.object({
29
+ serverUrl: z.string(),
30
+ toolArgs: z.record(z.unknown()).optional(),
31
+ toolName: z.string(),
32
+ }),
33
+ )
34
+ .mutation(async ({ ctx, input }) => {
35
+ const response = await ctx.klavisClient.mcpServer.callTools({
36
+ serverUrl: input.serverUrl,
37
+ toolArgs: input.toolArgs,
38
+ toolName: input.toolName,
39
+ });
40
+
41
+ // Handle error case
42
+ if (!response.success || !response.result) {
43
+ return {
44
+ content: response.error || 'Unknown error',
45
+ state: {
46
+ content: [{ text: response.error || 'Unknown error', type: 'text' }],
47
+ isError: true,
48
+ },
49
+ success: false,
50
+ };
51
+ }
52
+
53
+ // Process the response using the common MCP tool call result processor
54
+ const processedResult = await MCPService.processToolCallResult({
55
+ content: (response.result.content || []) as any[],
56
+ isError: response.result.isError,
57
+ });
58
+
59
+ return processedResult;
60
+ }),
61
+
62
+ /**
63
+ * List tools available on a Klavis Strata server
64
+ */
65
+ listTools: klavisProcedure
66
+ .input(
67
+ z.object({
68
+ serverUrl: z.string(),
69
+ }),
70
+ )
71
+ .query(async ({ ctx, input }) => {
72
+ const response = await ctx.klavisClient.mcpServer.listTools({
73
+ serverUrl: input.serverUrl,
74
+ });
75
+
76
+ return {
77
+ tools: response.tools,
78
+ };
79
+ }),
80
+ });
@@ -21,12 +21,60 @@ import { mcpSystemDepsCheckService } from './deps';
21
21
 
22
22
  const log = debug('lobe-mcp:service');
23
23
 
24
+ /**
25
+ * MCP Tool call raw result type
26
+ */
27
+ export interface MCPToolCallRawResult {
28
+ content: any[];
29
+ isError?: boolean;
30
+ }
31
+
32
+ /**
33
+ * MCP Tool call processed result type
34
+ */
35
+ export interface MCPToolCallProcessedResult {
36
+ content: string;
37
+ error?: Error;
38
+ state: {
39
+ content: any[];
40
+ isError?: boolean;
41
+ };
42
+ success: boolean;
43
+ }
44
+
24
45
  // Removed MCPConnection interface as it's no longer needed
25
46
 
26
47
  export class MCPService {
27
48
  // Store instances of the custom MCPClient, keyed by serialized MCPClientParams
28
49
  private clients: Map<string, MCPClient> = new Map();
29
50
 
51
+ /**
52
+ * Process MCP tool call result with content blocks processing
53
+ * This is a common utility method that can be used by both internal MCP calls and external services (e.g., Klavis)
54
+ */
55
+ static async processToolCallResult(
56
+ result: MCPToolCallRawResult,
57
+ processContentBlocksFn?: ProcessContentBlocksFn,
58
+ ): Promise<MCPToolCallProcessedResult> {
59
+ // Process content blocks (upload images, etc.)
60
+
61
+ const newContent =
62
+ result.isError || !processContentBlocksFn
63
+ ? result.content
64
+ : await processContentBlocksFn(result.content);
65
+
66
+ // Convert content blocks to string
67
+ const content = contentBlocksToString(newContent);
68
+
69
+ const state = { ...result, content: newContent };
70
+
71
+ if (result.isError) {
72
+ return { content, state, success: true };
73
+ }
74
+
75
+ return { content, state, success: true };
76
+ }
77
+
30
78
  private sanitizeForLogging = <T extends Record<string, any>>(obj: T): Omit<T, 'env'> => {
31
79
  if (!obj) return obj;
32
80
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -162,7 +210,12 @@ export class MCPService {
162
210
  processContentBlocks?: ProcessContentBlocksFn;
163
211
  toolName: string;
164
212
  }): Promise<any> {
165
- const { clientParams, toolName, argsStr, processContentBlocks } = options;
213
+ const {
214
+ clientParams,
215
+ toolName,
216
+ argsStr,
217
+ processContentBlocks: processContentBlocksFn,
218
+ } = options;
166
219
 
167
220
  const client = await this.getClient(clientParams); // Get client using params
168
221
 
@@ -179,26 +232,19 @@ export class MCPService {
179
232
  // Delegate the call to the MCPClient instance
180
233
  const result = await client.callTool(toolName, args); // Pass args directly
181
234
 
182
- // Process content blocks (upload images, etc.)
183
- const newContent =
184
- result.isError || !processContentBlocks
185
- ? result.content
186
- : await processContentBlocks(result.content);
187
-
188
- // Convert content blocks to string
189
- const content = contentBlocksToString(newContent);
190
-
191
- const state = { ...result, content: newContent };
235
+ // Use the common processing method
236
+ const processedResult = await MCPService.processToolCallResult(
237
+ result,
238
+ processContentBlocksFn,
239
+ );
192
240
 
193
241
  log(
194
242
  `Tool "${toolName}" called successfully for params: %O, result: %O`,
195
243
  loggableParams,
196
- state,
244
+ processedResult.state,
197
245
  );
198
246
 
199
- if (result.isError) return { content, state, success: true };
200
-
201
- return { content, state, success: true };
247
+ return processedResult;
202
248
  } catch (error) {
203
249
  if (error instanceof McpError) {
204
250
  const mcpError = error as McpError;