@lobehub/lobehub 2.0.0-next.27 → 2.0.0-next.29

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 (34) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +1 -1
  4. package/packages/database/package.json +1 -1
  5. package/packages/database/src/models/message.ts +29 -2
  6. package/packages/types/src/discover/mcp.ts +6 -0
  7. package/packages/types/src/message/ui/params.ts +0 -49
  8. package/packages/types/src/plugins/mcp.ts +4 -1
  9. package/renovate.json +4 -30
  10. package/src/features/MCP/utils.test.ts +91 -0
  11. package/src/features/MCP/utils.ts +20 -2
  12. package/src/features/PluginStore/Content.tsx +2 -3
  13. package/src/features/PluginStore/McpList/index.tsx +6 -2
  14. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +35 -35
  15. package/src/server/routers/lambda/market/index.ts +4 -2
  16. package/src/server/routers/lambda/message.ts +104 -16
  17. package/src/services/mcp.ts +40 -6
  18. package/src/services/message/index.ts +63 -35
  19. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +17 -10
  20. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +4 -4
  21. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +2 -2
  22. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +3 -2
  23. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +2 -2
  24. package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +7 -2
  25. package/src/store/chat/slices/builtinTool/actions/search.ts +3 -3
  26. package/src/store/chat/slices/message/action.test.ts +152 -15
  27. package/src/store/chat/slices/message/action.ts +70 -82
  28. package/src/store/chat/slices/plugin/action.test.ts +84 -25
  29. package/src/store/chat/slices/plugin/action.ts +52 -24
  30. package/src/store/chat/slices/thread/action.test.ts +13 -4
  31. package/src/store/chat/slices/thread/action.ts +3 -1
  32. package/src/store/tool/slices/mcpStore/action.test.ts +95 -3
  33. package/src/store/tool/slices/mcpStore/action.ts +177 -53
  34. package/src/store/tool/slices/oldStore/initialState.ts +1 -2
@@ -72,7 +72,7 @@ describe('Message Router Integration Tests', () => {
72
72
  it('should create message with correct sessionId and topicId', async () => {
73
73
  const caller = messageRouter.createCaller(createTestContext(userId));
74
74
 
75
- const messageId = await caller.createMessage({
75
+ const result = await caller.createNewMessage({
76
76
  content: 'Test message',
77
77
  role: 'user',
78
78
  sessionId: testSessionId,
@@ -83,11 +83,11 @@ describe('Message Router Integration Tests', () => {
83
83
  const [createdMessage] = await serverDB
84
84
  .select()
85
85
  .from(messages)
86
- .where(eq(messages.id, messageId));
86
+ .where(eq(messages.id, result.id));
87
87
 
88
88
  expect(createdMessage).toBeDefined();
89
89
  expect(createdMessage).toMatchObject({
90
- id: messageId,
90
+ id: result.id,
91
91
  sessionId: testSessionId,
92
92
  topicId: testTopicId,
93
93
  userId: userId,
@@ -111,7 +111,7 @@ describe('Message Router Integration Tests', () => {
111
111
  })
112
112
  .returning();
113
113
 
114
- const messageId = await caller.createMessage({
114
+ const result = await caller.createNewMessage({
115
115
  content: 'Test message in thread',
116
116
  role: 'user',
117
117
  sessionId: testSessionId,
@@ -123,12 +123,12 @@ describe('Message Router Integration Tests', () => {
123
123
  const [createdMessage] = await serverDB
124
124
  .select()
125
125
  .from(messages)
126
- .where(eq(messages.id, messageId));
126
+ .where(eq(messages.id, result.id));
127
127
 
128
128
  expect(createdMessage).toBeDefined();
129
129
  expect(createdMessage.threadId).toBe(thread.id);
130
130
  expect(createdMessage).toMatchObject({
131
- id: messageId,
131
+ id: result.id,
132
132
  sessionId: testSessionId,
133
133
  topicId: testTopicId,
134
134
  threadId: thread.id,
@@ -140,7 +140,7 @@ describe('Message Router Integration Tests', () => {
140
140
  it('should create message without topicId', async () => {
141
141
  const caller = messageRouter.createCaller(createTestContext(userId));
142
142
 
143
- const messageId = await caller.createMessage({
143
+ const result = await caller.createNewMessage({
144
144
  content: 'Test message without topic',
145
145
  role: 'user',
146
146
  sessionId: testSessionId,
@@ -150,7 +150,7 @@ describe('Message Router Integration Tests', () => {
150
150
  const [createdMessage] = await serverDB
151
151
  .select()
152
152
  .from(messages)
153
- .where(eq(messages.id, messageId));
153
+ .where(eq(messages.id, result.id));
154
154
 
155
155
  expect(createdMessage.topicId).toBeNull();
156
156
  expect(createdMessage.sessionId).toBe(testSessionId);
@@ -160,7 +160,7 @@ describe('Message Router Integration Tests', () => {
160
160
  const caller = messageRouter.createCaller(createTestContext(userId));
161
161
 
162
162
  await expect(
163
- caller.createMessage({
163
+ caller.createNewMessage({
164
164
  content: 'Test message',
165
165
  role: 'user',
166
166
  sessionId: 'non-existent-session',
@@ -192,7 +192,7 @@ describe('Message Router Integration Tests', () => {
192
192
 
193
193
  // 尝试在 testSessionId 下创建消息,但使用 anotherTopic 的 ID
194
194
  await expect(
195
- caller.createMessage({
195
+ caller.createNewMessage({
196
196
  content: 'Test message',
197
197
  role: 'user',
198
198
  sessionId: testSessionId,
@@ -207,13 +207,13 @@ describe('Message Router Integration Tests', () => {
207
207
  const caller = messageRouter.createCaller(createTestContext(userId));
208
208
 
209
209
  // 创建多个消息
210
- const msg1Id = await caller.createMessage({
210
+ const msg1Result = await caller.createNewMessage({
211
211
  content: 'Message 1',
212
212
  role: 'user',
213
213
  sessionId: testSessionId,
214
214
  });
215
215
 
216
- const msg2Id = await caller.createMessage({
216
+ const msg2Result = await caller.createNewMessage({
217
217
  content: 'Message 2',
218
218
  role: 'assistant',
219
219
  sessionId: testSessionId,
@@ -228,7 +228,7 @@ describe('Message Router Integration Tests', () => {
228
228
  })
229
229
  .returning();
230
230
 
231
- await caller.createMessage({
231
+ await caller.createNewMessage({
232
232
  content: 'Message in another session',
233
233
  role: 'user',
234
234
  sessionId: anotherSession.id,
@@ -240,15 +240,15 @@ describe('Message Router Integration Tests', () => {
240
240
  });
241
241
 
242
242
  expect(result).toHaveLength(2);
243
- expect(result.map((m) => m.id)).toContain(msg1Id);
244
- expect(result.map((m) => m.id)).toContain(msg2Id);
243
+ expect(result.map((m) => m.id)).toContain(msg1Result.id);
244
+ expect(result.map((m) => m.id)).toContain(msg2Result.id);
245
245
  });
246
246
 
247
247
  it('should return messages filtered by topicId', async () => {
248
248
  const caller = messageRouter.createCaller(createTestContext(userId));
249
249
 
250
250
  // 在 topic 中创建消息
251
- const msgInTopicId = await caller.createMessage({
251
+ const msgInTopicResult = await caller.createNewMessage({
252
252
  content: 'Message in topic',
253
253
  role: 'user',
254
254
  sessionId: testSessionId,
@@ -256,7 +256,7 @@ describe('Message Router Integration Tests', () => {
256
256
  });
257
257
 
258
258
  // 在 session 中创建消息(不在 topic 中)
259
- await caller.createMessage({
259
+ await caller.createNewMessage({
260
260
  content: 'Message without topic',
261
261
  role: 'user',
262
262
  sessionId: testSessionId,
@@ -269,7 +269,7 @@ describe('Message Router Integration Tests', () => {
269
269
  });
270
270
 
271
271
  expect(result).toHaveLength(1);
272
- expect(result[0].id).toBe(msgInTopicId);
272
+ expect(result[0].id).toBe(msgInTopicResult.id);
273
273
  expect(result[0].topicId).toBe(testTopicId);
274
274
  });
275
275
 
@@ -278,7 +278,7 @@ describe('Message Router Integration Tests', () => {
278
278
 
279
279
  // 创建多个消息
280
280
  for (let i = 0; i < 5; i++) {
281
- await caller.createMessage({
281
+ await caller.createNewMessage({
282
282
  content: `Pagination test message ${i}`,
283
283
  role: 'user',
284
284
  sessionId: testSessionId,
@@ -323,20 +323,20 @@ describe('Message Router Integration Tests', () => {
323
323
  const caller = messageRouter.createCaller(createTestContext(userId));
324
324
 
325
325
  // 创建消息
326
- const msg1Id = await caller.createMessage({
326
+ const msg1Result = await caller.createNewMessage({
327
327
  content: 'Message 1',
328
328
  role: 'user',
329
329
  sessionId: testSessionId,
330
330
  });
331
331
 
332
- const msg2Id = await caller.createMessage({
332
+ const msg2Result = await caller.createNewMessage({
333
333
  content: 'Message 2',
334
334
  role: 'user',
335
335
  sessionId: testSessionId,
336
336
  });
337
337
 
338
338
  // 删除消息
339
- await caller.removeMessages({ ids: [msg1Id, msg2Id] });
339
+ await caller.removeMessages({ ids: [msg1Result.id, msg2Result.id] });
340
340
 
341
341
  // 验证消息已删除
342
342
  const remainingMessages = await serverDB
@@ -353,13 +353,13 @@ describe('Message Router Integration Tests', () => {
353
353
  const caller = messageRouter.createCaller(createTestContext(userId));
354
354
 
355
355
  // 创建多个消息
356
- await caller.createMessage({
356
+ await caller.createNewMessage({
357
357
  content: 'Message 1',
358
358
  role: 'user',
359
359
  sessionId: testSessionId,
360
360
  });
361
361
 
362
- await caller.createMessage({
362
+ await caller.createNewMessage({
363
363
  content: 'Message 2',
364
364
  role: 'assistant',
365
365
  sessionId: testSessionId,
@@ -383,7 +383,7 @@ describe('Message Router Integration Tests', () => {
383
383
  const caller = messageRouter.createCaller(createTestContext(userId));
384
384
 
385
385
  // 在 topic 中创建消息
386
- await caller.createMessage({
386
+ await caller.createNewMessage({
387
387
  content: 'Message in topic',
388
388
  role: 'user',
389
389
  sessionId: testSessionId,
@@ -391,7 +391,7 @@ describe('Message Router Integration Tests', () => {
391
391
  });
392
392
 
393
393
  // 在 session 中创建消息(不在 topic 中)
394
- const msgOutsideTopicId = await caller.createMessage({
394
+ const msgOutsideTopicResult = await caller.createNewMessage({
395
395
  content: 'Message outside topic',
396
396
  role: 'user',
397
397
  sessionId: testSessionId,
@@ -410,7 +410,7 @@ describe('Message Router Integration Tests', () => {
410
410
  .where(eq(messages.sessionId, testSessionId));
411
411
 
412
412
  expect(remainingMessages).toHaveLength(1);
413
- expect(remainingMessages[0].id).toBe(msgOutsideTopicId);
413
+ expect(remainingMessages[0].id).toBe(msgOutsideTopicResult.id);
414
414
  });
415
415
  });
416
416
 
@@ -418,14 +418,14 @@ describe('Message Router Integration Tests', () => {
418
418
  it('should update message content', async () => {
419
419
  const caller = messageRouter.createCaller(createTestContext(userId));
420
420
 
421
- const messageId = await caller.createMessage({
421
+ const result = await caller.createNewMessage({
422
422
  content: 'Original content',
423
423
  role: 'user',
424
424
  sessionId: testSessionId,
425
425
  });
426
426
 
427
427
  await caller.update({
428
- id: messageId,
428
+ id: result.id,
429
429
  value: {
430
430
  content: 'Updated content',
431
431
  },
@@ -434,7 +434,7 @@ describe('Message Router Integration Tests', () => {
434
434
  const [updatedMessage] = await serverDB
435
435
  .select()
436
436
  .from(messages)
437
- .where(eq(messages.id, messageId));
437
+ .where(eq(messages.id, result.id));
438
438
 
439
439
  expect(updatedMessage.content).toBe('Updated content');
440
440
  });
@@ -444,13 +444,13 @@ describe('Message Router Integration Tests', () => {
444
444
  it('should search messages by keyword', async () => {
445
445
  const caller = messageRouter.createCaller(createTestContext(userId));
446
446
 
447
- await caller.createMessage({
447
+ await caller.createNewMessage({
448
448
  content: 'This is a test message about TypeScript',
449
449
  role: 'user',
450
450
  sessionId: testSessionId,
451
451
  });
452
452
 
453
- await caller.createMessage({
453
+ await caller.createNewMessage({
454
454
  content: 'Another message about JavaScript',
455
455
  role: 'user',
456
456
  sessionId: testSessionId,
@@ -470,13 +470,13 @@ describe('Message Router Integration Tests', () => {
470
470
  const caller = messageRouter.createCaller(createTestContext(userId));
471
471
 
472
472
  // 创建消息
473
- await caller.createMessage({
473
+ await caller.createNewMessage({
474
474
  content: 'Message 1',
475
475
  role: 'user',
476
476
  sessionId: testSessionId,
477
477
  });
478
478
 
479
- await caller.createMessage({
479
+ await caller.createNewMessage({
480
480
  content: 'Message 2',
481
481
  role: 'assistant',
482
482
  sessionId: testSessionId,
@@ -490,7 +490,7 @@ describe('Message Router Integration Tests', () => {
490
490
  it('should count words', async () => {
491
491
  const caller = messageRouter.createCaller(createTestContext(userId));
492
492
 
493
- await caller.createMessage({
493
+ await caller.createNewMessage({
494
494
  content: 'Hello world',
495
495
  role: 'user',
496
496
  sessionId: testSessionId,
@@ -3,10 +3,10 @@ import { serialize } from 'cookie';
3
3
  import debug from 'debug';
4
4
  import { z } from 'zod';
5
5
 
6
- import { isDesktop } from '@/const/version';
6
+ import { isDesktop } from '@lobechat/const';
7
7
  import { publicProcedure, router } from '@/libs/trpc/lambda';
8
8
  import { DiscoverService } from '@/server/services/discover';
9
- import { AssistantSorts, McpSorts, ModelSorts, PluginSorts, ProviderSorts } from '@/types/discover';
9
+ import { AssistantSorts, McpConnectionType, McpSorts, ModelSorts, PluginSorts, ProviderSorts } from '@/types/discover';
10
10
 
11
11
  const log = debug('lambda-router:market');
12
12
 
@@ -83,6 +83,7 @@ export const marketRouter = router({
83
83
  z
84
84
  .object({
85
85
  category: z.string().optional(),
86
+ connectionType: z.nativeEnum(McpConnectionType).optional(),
86
87
  locale: z.string().optional(),
87
88
  order: z.enum(['asc', 'desc']).optional(),
88
89
  page: z.number().optional(),
@@ -178,6 +179,7 @@ export const marketRouter = router({
178
179
  z
179
180
  .object({
180
181
  category: z.string().optional(),
182
+ connectionType: z.nativeEnum(McpConnectionType).optional(),
181
183
  locale: z.string().optional(),
182
184
  order: z.enum(['asc', 'desc']).optional(),
183
185
  page: z.number().optional(),
@@ -1,5 +1,4 @@
1
1
  import {
2
- CreateMessageParamsSchema,
3
2
  CreateNewMessageParamsSchema,
4
3
  UpdateMessageParamsSchema,
5
4
  UpdateMessageRAGParamsSchema,
@@ -53,14 +52,6 @@ export const messageRouter = router({
53
52
  return ctx.messageModel.countWords(input);
54
53
  }),
55
54
 
56
- createMessage: messageProcedure
57
- .input(CreateMessageParamsSchema)
58
- .mutation(async ({ input, ctx }) => {
59
- const data = await ctx.messageModel.create(input as any);
60
-
61
- return data.id;
62
- }),
63
-
64
55
  createNewMessage: messageProcedure
65
56
  .input(CreateNewMessageParamsSchema)
66
57
  .mutation(async ({ input, ctx }) => {
@@ -109,9 +100,32 @@ export const messageRouter = router({
109
100
  }),
110
101
 
111
102
  removeMessage: messageProcedure
112
- .input(z.object({ id: z.string() }))
103
+ .input(
104
+ z.object({
105
+ id: z.string(),
106
+ sessionId: z.string().nullable().optional(),
107
+ topicId: z.string().nullable().optional(),
108
+ useGroup: z.boolean().optional(),
109
+ }),
110
+ )
113
111
  .mutation(async ({ input, ctx }) => {
114
- return ctx.messageModel.deleteMessage(input.id);
112
+ await ctx.messageModel.deleteMessage(input.id);
113
+
114
+ // If sessionId or topicId is provided, return the full message list
115
+ if (input.sessionId !== undefined || input.topicId !== undefined) {
116
+ const messageList = await ctx.messageModel.query(
117
+ {
118
+ sessionId: input.sessionId,
119
+ topicId: input.topicId,
120
+ },
121
+ {
122
+ groupAssistantMessages: input.useGroup ?? false,
123
+ postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
124
+ },
125
+ );
126
+ return { messages: messageList, success: true };
127
+ }
128
+ return { success: true };
115
129
  }),
116
130
 
117
131
  removeMessageQuery: messageProcedure
@@ -121,9 +135,32 @@ export const messageRouter = router({
121
135
  }),
122
136
 
123
137
  removeMessages: messageProcedure
124
- .input(z.object({ ids: z.array(z.string()) }))
138
+ .input(
139
+ z.object({
140
+ ids: z.array(z.string()),
141
+ sessionId: z.string().nullable().optional(),
142
+ topicId: z.string().nullable().optional(),
143
+ useGroup: z.boolean().optional(),
144
+ }),
145
+ )
125
146
  .mutation(async ({ input, ctx }) => {
126
- return ctx.messageModel.deleteMessages(input.ids);
147
+ await ctx.messageModel.deleteMessages(input.ids);
148
+
149
+ // If sessionId or topicId is provided, return the full message list
150
+ if (input.sessionId !== undefined || input.topicId !== undefined) {
151
+ const messageList = await ctx.messageModel.query(
152
+ {
153
+ sessionId: input.sessionId,
154
+ topicId: input.topicId,
155
+ },
156
+ {
157
+ groupAssistantMessages: input.useGroup ?? false,
158
+ postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
159
+ },
160
+ );
161
+ return { messages: messageList, success: true };
162
+ }
163
+ return { success: true };
127
164
  }),
128
165
 
129
166
  removeMessagesByAssistant: messageProcedure
@@ -165,11 +202,13 @@ export const messageRouter = router({
165
202
  id: z.string(),
166
203
  sessionId: z.string().nullable().optional(),
167
204
  topicId: z.string().nullable().optional(),
205
+ useGroup: z.boolean().optional(),
168
206
  value: UpdateMessageParamsSchema,
169
207
  }),
170
208
  )
171
209
  .mutation(async ({ input, ctx }) => {
172
210
  return ctx.messageModel.update(input.id, input.value as any, {
211
+ groupAssistantMessages: input.useGroup ?? false,
173
212
  postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
174
213
  sessionId: input.sessionId,
175
214
  topicId: input.topicId,
@@ -188,9 +227,31 @@ export const messageRouter = router({
188
227
  }),
189
228
 
190
229
  updateMessageRAG: messageProcedure
191
- .input(UpdateMessageRAGParamsSchema)
230
+ .input(
231
+ UpdateMessageRAGParamsSchema.extend({
232
+ sessionId: z.string().nullable().optional(),
233
+ topicId: z.string().nullable().optional(),
234
+ useGroup: z.boolean().optional(),
235
+ }),
236
+ )
192
237
  .mutation(async ({ input, ctx }) => {
193
238
  await ctx.messageModel.updateMessageRAG(input.id, input.value);
239
+
240
+ // If sessionId or topicId is provided, return the full message list
241
+ if (input.sessionId !== undefined || input.topicId !== undefined) {
242
+ const messageList = await ctx.messageModel.query(
243
+ {
244
+ sessionId: input.sessionId,
245
+ topicId: input.topicId,
246
+ },
247
+ {
248
+ groupAssistantMessages: input.useGroup ?? false,
249
+ postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
250
+ },
251
+ );
252
+ return { messages: messageList, success: true };
253
+ }
254
+ return { success: true };
194
255
  }),
195
256
 
196
257
  updateMetadata: messageProcedure
@@ -208,22 +269,49 @@ export const messageRouter = router({
208
269
  .input(
209
270
  z.object({
210
271
  id: z.string(),
272
+ sessionId: z.string().nullable().optional(),
273
+ topicId: z.string().nullable().optional(),
274
+ useGroup: z.boolean().optional(),
211
275
  value: z.object({}).passthrough().nullable(),
212
276
  }),
213
277
  )
214
278
  .mutation(async ({ input, ctx }) => {
215
- return ctx.messageModel.updateMessagePlugin(input.id, { error: input.value });
279
+ // If sessionId or topicId is provided, we need to return the full message list
280
+ if (input.sessionId !== undefined || input.topicId !== undefined) {
281
+ await ctx.messageModel.updateMessagePlugin(input.id, { error: input.value });
282
+ const messageList = await ctx.messageModel.query(
283
+ {
284
+ sessionId: input.sessionId,
285
+ topicId: input.topicId,
286
+ },
287
+ {
288
+ groupAssistantMessages: input.useGroup ?? false,
289
+ postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
290
+ },
291
+ );
292
+ return { messages: messageList, success: true };
293
+ }
294
+ await ctx.messageModel.updateMessagePlugin(input.id, { error: input.value });
295
+ return { success: true };
216
296
  }),
217
297
 
218
298
  updatePluginState: messageProcedure
219
299
  .input(
220
300
  z.object({
221
301
  id: z.string(),
302
+ sessionId: z.string().nullable().optional(),
303
+ topicId: z.string().nullable().optional(),
304
+ useGroup: z.boolean().optional(),
222
305
  value: z.object({}).passthrough(),
223
306
  }),
224
307
  )
225
308
  .mutation(async ({ input, ctx }) => {
226
- return ctx.messageModel.updatePluginState(input.id, input.value);
309
+ return ctx.messageModel.updatePluginState(input.id, input.value, {
310
+ groupAssistantMessages: input.useGroup ?? false,
311
+ postProcessUrl: (path) => ctx.fileService.getFullFileUrl(path),
312
+ sessionId: input.sessionId,
313
+ topicId: input.topicId,
314
+ });
227
315
  }),
228
316
 
229
317
  updateTTS: messageProcedure
@@ -41,10 +41,44 @@ class MCPService {
41
41
 
42
42
  if (!plugin) return;
43
43
 
44
+ const connection = plugin.customParams?.mcp;
45
+ const settingsEntries = plugin.settings
46
+ ? Object.entries(plugin.settings as Record<string, any>).filter(
47
+ ([, value]) => value !== undefined && value !== null,
48
+ )
49
+ : [];
50
+ const pluginSettings =
51
+ settingsEntries.length > 0
52
+ ? settingsEntries.reduce<Record<string, unknown>>((acc, [key, value]) => {
53
+ acc[key] = value;
54
+
55
+ return acc;
56
+ }, {})
57
+ : undefined;
58
+
59
+ const params = {
60
+ ...connection,
61
+ name: identifier,
62
+ } as any;
63
+
64
+ if (connection?.type === 'http') {
65
+ params.headers = {
66
+ ...connection.headers,
67
+ ...pluginSettings,
68
+ };
69
+ }
70
+
71
+ if (connection?.type === 'stdio') {
72
+ params.env = {
73
+ ...connection?.env,
74
+ ...pluginSettings,
75
+ };
76
+ }
77
+
44
78
  const data = {
45
79
  args,
46
- env: plugin.settings || plugin.customParams?.mcp?.env,
47
- params: { ...plugin.customParams?.mcp, name: identifier } as any,
80
+ env: connection?.type === 'stdio' ? params.env : pluginSettings ?? connection?.env,
81
+ params,
48
82
  toolName: apiName,
49
83
  };
50
84
 
@@ -93,10 +127,10 @@ class MCPService {
93
127
  callDurationMs,
94
128
  customPluginInfo: isCustomPlugin
95
129
  ? {
96
- avatar: plugin.manifest?.meta.avatar,
97
- description: plugin.manifest?.meta.description,
98
- name: plugin.manifest?.meta.title,
99
- }
130
+ avatar: plugin.manifest?.meta.avatar,
131
+ description: plugin.manifest?.meta.description,
132
+ name: plugin.manifest?.meta.title,
133
+ }
100
134
  : undefined,
101
135
  errorCode,
102
136
  errorMessage,