flex-mcp 1.0.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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -0
  3. package/dist/api/auth.d.ts +6 -0
  4. package/dist/api/auth.d.ts.map +1 -0
  5. package/dist/api/auth.js +111 -0
  6. package/dist/api/auth.js.map +1 -0
  7. package/dist/api/configs.d.ts +6 -0
  8. package/dist/api/configs.d.ts.map +1 -0
  9. package/dist/api/configs.js +693 -0
  10. package/dist/api/configs.js.map +1 -0
  11. package/dist/api/index.d.ts +6 -0
  12. package/dist/api/index.d.ts.map +1 -0
  13. package/dist/api/index.js +24 -0
  14. package/dist/api/index.js.map +1 -0
  15. package/dist/api/mcp.d.ts +7 -0
  16. package/dist/api/mcp.d.ts.map +1 -0
  17. package/dist/api/mcp.js +46 -0
  18. package/dist/api/mcp.js.map +1 -0
  19. package/dist/api/middleware.d.ts +25 -0
  20. package/dist/api/middleware.d.ts.map +1 -0
  21. package/dist/api/middleware.js +65 -0
  22. package/dist/api/middleware.js.map +1 -0
  23. package/dist/api/prompts.d.ts +6 -0
  24. package/dist/api/prompts.d.ts.map +1 -0
  25. package/dist/api/prompts.js +260 -0
  26. package/dist/api/prompts.js.map +1 -0
  27. package/dist/api/resources.d.ts +6 -0
  28. package/dist/api/resources.d.ts.map +1 -0
  29. package/dist/api/resources.js +159 -0
  30. package/dist/api/resources.js.map +1 -0
  31. package/dist/api/tables.d.ts +15 -0
  32. package/dist/api/tables.d.ts.map +1 -0
  33. package/dist/api/tables.js +702 -0
  34. package/dist/api/tables.js.map +1 -0
  35. package/dist/api/users.d.ts +6 -0
  36. package/dist/api/users.d.ts.map +1 -0
  37. package/dist/api/users.js +96 -0
  38. package/dist/api/users.js.map +1 -0
  39. package/dist/auth/index.d.ts +15 -0
  40. package/dist/auth/index.d.ts.map +1 -0
  41. package/dist/auth/index.js +126 -0
  42. package/dist/auth/index.js.map +1 -0
  43. package/dist/cli.d.ts +7 -0
  44. package/dist/cli.d.ts.map +1 -0
  45. package/dist/cli.js +288 -0
  46. package/dist/cli.js.map +1 -0
  47. package/dist/config.d.ts +21 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/config.js +54 -0
  50. package/dist/config.js.map +1 -0
  51. package/dist/db/index.d.ts +11 -0
  52. package/dist/db/index.d.ts.map +1 -0
  53. package/dist/db/index.js +164 -0
  54. package/dist/db/index.js.map +1 -0
  55. package/dist/db/schema.d.ts +646 -0
  56. package/dist/db/schema.d.ts.map +1 -0
  57. package/dist/db/schema.js +73 -0
  58. package/dist/db/schema.js.map +1 -0
  59. package/dist/index.d.ts +5 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +153 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/mcp/aggregator.d.ts +78 -0
  64. package/dist/mcp/aggregator.d.ts.map +1 -0
  65. package/dist/mcp/aggregator.js +1266 -0
  66. package/dist/mcp/aggregator.js.map +1 -0
  67. package/dist/mcp/component-loader.d.ts +17 -0
  68. package/dist/mcp/component-loader.d.ts.map +1 -0
  69. package/dist/mcp/component-loader.js +131 -0
  70. package/dist/mcp/component-loader.js.map +1 -0
  71. package/dist/mcp/index.d.ts +7 -0
  72. package/dist/mcp/index.d.ts.map +1 -0
  73. package/dist/mcp/index.js +107 -0
  74. package/dist/mcp/index.js.map +1 -0
  75. package/dist/mcp/mcp-client.d.ts +53 -0
  76. package/dist/mcp/mcp-client.d.ts.map +1 -0
  77. package/dist/mcp/mcp-client.js +418 -0
  78. package/dist/mcp/mcp-client.js.map +1 -0
  79. package/dist/mcp/post-sse-transport.d.ts +52 -0
  80. package/dist/mcp/post-sse-transport.d.ts.map +1 -0
  81. package/dist/mcp/post-sse-transport.js +375 -0
  82. package/dist/mcp/post-sse-transport.js.map +1 -0
  83. package/dist/mcp/service.d.ts +49 -0
  84. package/dist/mcp/service.d.ts.map +1 -0
  85. package/dist/mcp/service.js +358 -0
  86. package/dist/mcp/service.js.map +1 -0
  87. package/dist/mcp/tool-sync.d.ts +27 -0
  88. package/dist/mcp/tool-sync.d.ts.map +1 -0
  89. package/dist/mcp/tool-sync.js +200 -0
  90. package/dist/mcp/tool-sync.js.map +1 -0
  91. package/dist/script/compiler.d.ts +15 -0
  92. package/dist/script/compiler.d.ts.map +1 -0
  93. package/dist/script/compiler.js +163 -0
  94. package/dist/script/compiler.js.map +1 -0
  95. package/dist/script/context.d.ts +11 -0
  96. package/dist/script/context.d.ts.map +1 -0
  97. package/dist/script/context.js +786 -0
  98. package/dist/script/context.js.map +1 -0
  99. package/dist/script/executor.d.ts +43 -0
  100. package/dist/script/executor.d.ts.map +1 -0
  101. package/dist/script/executor.js +126 -0
  102. package/dist/script/executor.js.map +1 -0
  103. package/dist/static.d.ts +21 -0
  104. package/dist/static.d.ts.map +1 -0
  105. package/dist/static.js +95 -0
  106. package/dist/static.js.map +1 -0
  107. package/dist/types/index.d.ts +337 -0
  108. package/dist/types/index.d.ts.map +1 -0
  109. package/dist/types/index.js +5 -0
  110. package/dist/types/index.js.map +1 -0
  111. package/dist/utils/id.d.ts +5 -0
  112. package/dist/utils/id.d.ts.map +1 -0
  113. package/dist/utils/id.js +8 -0
  114. package/dist/utils/id.js.map +1 -0
  115. package/dist/utils/logger.d.ts +21 -0
  116. package/dist/utils/logger.d.ts.map +1 -0
  117. package/dist/utils/logger.js +31 -0
  118. package/dist/utils/logger.js.map +1 -0
  119. package/dist/utils/prompt-template.d.ts +15 -0
  120. package/dist/utils/prompt-template.d.ts.map +1 -0
  121. package/dist/utils/prompt-template.js +82 -0
  122. package/dist/utils/prompt-template.js.map +1 -0
  123. package/dist/utils/telemetry.d.ts +45 -0
  124. package/dist/utils/telemetry.d.ts.map +1 -0
  125. package/dist/utils/telemetry.js +245 -0
  126. package/dist/utils/telemetry.js.map +1 -0
  127. package/dist/utils/variables.d.ts +24 -0
  128. package/dist/utils/variables.d.ts.map +1 -0
  129. package/dist/utils/variables.js +45 -0
  130. package/dist/utils/variables.js.map +1 -0
  131. package/package.json +70 -0
@@ -0,0 +1,693 @@
1
+ /**
2
+ * MCP 配置管理 API
3
+ */
4
+ import { Hono } from 'hono';
5
+ import { authMiddleware, requireAdmin, getCurrentUserId, getCurrentUserRole } from './middleware.js';
6
+ import { db } from '../db/index.js';
7
+ import { mcpConfigs, mcpComponentTools } from '../db/schema.js';
8
+ import { eq, and } from 'drizzle-orm';
9
+ import { generateId } from '../utils/id.js';
10
+ import { mcpService } from '../mcp/service.js';
11
+ import { syncComponentTools, loadComponentToolsList } from '../mcp/tool-sync.js';
12
+ import { report } from '../utils/telemetry.js';
13
+ export const configRouter = new Hono();
14
+ /**
15
+ * 将数据库行转换为 MCPConfig 对象
16
+ */
17
+ function rowToConfig(row) {
18
+ return {
19
+ id: row.id,
20
+ userId: row.userId,
21
+ name: row.name,
22
+ components: JSON.parse(row.components),
23
+ customTools: JSON.parse(row.customTools),
24
+ variables: JSON.parse(row.variables),
25
+ initScript: row.initScript || undefined,
26
+ enableExploreTable: row.enableExploreTable !== undefined ? Boolean(row.enableExploreTable) : true, // 默认为 true
27
+ createdAt: row.createdAt,
28
+ updatedAt: row.updatedAt,
29
+ };
30
+ }
31
+ // 所有路由需要认证
32
+ configRouter.use('*', authMiddleware);
33
+ // 获取当前用户的唯一配置(如果不存在则创建)
34
+ configRouter.get('/me', async (c) => {
35
+ try {
36
+ const currentUserId = getCurrentUserId(c);
37
+ const currentRole = getCurrentUserRole(c);
38
+ // 查找当前用户的配置
39
+ let result = await db
40
+ .select()
41
+ .from(mcpConfigs)
42
+ .where(eq(mcpConfigs.userId, currentUserId))
43
+ .limit(1);
44
+ if (result.length === 0) {
45
+ // 如果不存在,创建默认配置
46
+ const configId = generateId();
47
+ const now = new Date();
48
+ await db.insert(mcpConfigs).values({
49
+ id: configId,
50
+ userId: currentUserId,
51
+ name: 'Default Config',
52
+ components: '[]',
53
+ customTools: '[]',
54
+ variables: '[]',
55
+ initScript: null,
56
+ enableExploreTable: true,
57
+ createdAt: now,
58
+ updatedAt: now,
59
+ });
60
+ result = await db
61
+ .select()
62
+ .from(mcpConfigs)
63
+ .where(eq(mcpConfigs.id, configId))
64
+ .limit(1);
65
+ // 上报配置创建统计
66
+ report('config_created', {
67
+ is_default: true,
68
+ }, currentUserId);
69
+ }
70
+ const configRow = result[0];
71
+ const config = rowToConfig(configRow);
72
+ // 超级管理员查看时,隐藏variables
73
+ if (currentRole === 'admin') {
74
+ const sanitizedConfig = {
75
+ ...config,
76
+ variables: [],
77
+ };
78
+ return c.json({ success: true, data: sanitizedConfig });
79
+ }
80
+ return c.json({ success: true, data: config });
81
+ }
82
+ catch (error) {
83
+ return c.json({ success: false, error: error.message }, 500);
84
+ }
85
+ });
86
+ // 管理员获取指定用户的配置(不包含敏感信息:variables)
87
+ configRouter.get('/user/:userId', requireAdmin(), async (c) => {
88
+ try {
89
+ const targetUserId = c.req.param('userId');
90
+ let result = await db
91
+ .select()
92
+ .from(mcpConfigs)
93
+ .where(eq(mcpConfigs.userId, targetUserId))
94
+ .limit(1);
95
+ if (result.length === 0) {
96
+ // 如果管理员查看时不存在,也创建一个空的
97
+ const configId = generateId();
98
+ const now = new Date();
99
+ await db.insert(mcpConfigs).values({
100
+ id: configId,
101
+ userId: targetUserId,
102
+ name: 'Default Config',
103
+ components: '[]',
104
+ customTools: '[]',
105
+ variables: '[]',
106
+ initScript: null,
107
+ enableExploreTable: true,
108
+ createdAt: now,
109
+ updatedAt: now,
110
+ });
111
+ result = await db
112
+ .select()
113
+ .from(mcpConfigs)
114
+ .where(eq(mcpConfigs.id, configId))
115
+ .limit(1);
116
+ }
117
+ const configRow = result[0];
118
+ const config = rowToConfig(configRow);
119
+ // 管理员查看时,隐藏敏感信息(variables)
120
+ const sanitizedConfig = {
121
+ ...config,
122
+ variables: [], // 对管理员隐藏变量
123
+ };
124
+ return c.json({ success: true, data: sanitizedConfig });
125
+ }
126
+ catch (error) {
127
+ return c.json({ success: false, error: error.message }, 500);
128
+ }
129
+ });
130
+ // 更新当前用户的配置
131
+ configRouter.put('/me', async (c) => {
132
+ try {
133
+ const currentUserId = getCurrentUserId(c);
134
+ const currentRole = getCurrentUserRole(c);
135
+ const body = await c.req.json();
136
+ // 超级管理员不允许修改variables
137
+ if (currentRole === 'admin' && body.variables !== undefined) {
138
+ return c.json({ success: false, error: '超级管理员不允许修改变量管理' }, 403);
139
+ }
140
+ // 查找配置
141
+ const result = await db
142
+ .select()
143
+ .from(mcpConfigs)
144
+ .where(eq(mcpConfigs.userId, currentUserId))
145
+ .limit(1);
146
+ if (result.length === 0) {
147
+ return c.json({ success: false, error: 'Config not found' }, 404);
148
+ }
149
+ const configId = result[0].id;
150
+ // 更新配置(如果是admin,排除variables字段)
151
+ const updateData = {
152
+ name: body.name,
153
+ components: body.components ? JSON.stringify(body.components) : undefined,
154
+ customTools: body.customTools ? JSON.stringify(body.customTools) : undefined,
155
+ initScript: body.initScript || null,
156
+ updatedAt: new Date(),
157
+ };
158
+ // 只有非admin用户才能更新variables
159
+ if (currentRole !== 'admin') {
160
+ updateData.variables = body.variables ? JSON.stringify(body.variables) : undefined;
161
+ }
162
+ // 更新 enableExploreTable 字段(如果提供)
163
+ if (body.enableExploreTable !== undefined) {
164
+ updateData.enableExploreTable = body.enableExploreTable ? 1 : 0;
165
+ }
166
+ await db
167
+ .update(mcpConfigs)
168
+ .set(updateData)
169
+ .where(eq(mcpConfigs.id, configId));
170
+ // 上报配置更新统计
171
+ report('config_updated', {
172
+ has_components: !!body.components,
173
+ has_custom_tools: !!body.customTools,
174
+ has_init_script: !!body.initScript,
175
+ components_count: body.components?.length || 0,
176
+ custom_tools_count: body.customTools?.length || 0,
177
+ enable_explore_table: body.enableExploreTable,
178
+ }, currentUserId);
179
+ // 清理用户的聚合器缓存,强制重新加载配置
180
+ // 注意:清理失败不应该影响配置保存
181
+ try {
182
+ await mcpService.clearAggregator(currentUserId);
183
+ }
184
+ catch (error) {
185
+ console.error(`[Config] Failed to clear aggregator for user ${currentUserId}:`, error.message || error);
186
+ // 继续执行,不抛出错误
187
+ }
188
+ // 返回更新后的配置
189
+ const updatedResult = await db
190
+ .select()
191
+ .from(mcpConfigs)
192
+ .where(eq(mcpConfigs.id, configId))
193
+ .limit(1);
194
+ const configRow = updatedResult[0];
195
+ const config = rowToConfig(configRow);
196
+ // 如果是admin,返回时隐藏variables
197
+ if (currentRole === 'admin') {
198
+ const sanitizedConfig = {
199
+ ...config,
200
+ variables: [],
201
+ };
202
+ return c.json({ success: true, data: sanitizedConfig });
203
+ }
204
+ return c.json({ success: true, data: config });
205
+ }
206
+ catch (error) {
207
+ return c.json({ success: false, error: error.message }, 500);
208
+ }
209
+ });
210
+ // 管理员更新指定用户的配置(不允许修改 variables)
211
+ configRouter.put('/user/:userId', requireAdmin(), async (c) => {
212
+ try {
213
+ const targetUserId = c.req.param('userId');
214
+ const body = await c.req.json();
215
+ const result = await db
216
+ .select()
217
+ .from(mcpConfigs)
218
+ .where(eq(mcpConfigs.userId, targetUserId))
219
+ .limit(1);
220
+ if (result.length === 0) {
221
+ return c.json({ success: false, error: 'Config not found' }, 404);
222
+ }
223
+ const configId = result[0].id;
224
+ // 管理员不允许修改用户的 variables,移除该字段
225
+ const { ...updateData } = body;
226
+ await db
227
+ .update(mcpConfigs)
228
+ .set({
229
+ name: updateData.name,
230
+ components: updateData.components ? JSON.stringify(updateData.components) : undefined,
231
+ customTools: updateData.customTools ? JSON.stringify(updateData.customTools) : undefined,
232
+ // variables 字段被忽略,管理员无法修改
233
+ initScript: updateData.initScript || null,
234
+ enableExploreTable: updateData.enableExploreTable !== undefined ? updateData.enableExploreTable : undefined,
235
+ updatedAt: new Date(),
236
+ })
237
+ .where(eq(mcpConfigs.id, configId));
238
+ // 清理用户的聚合器缓存,强制重新加载配置
239
+ // 注意:清理失败不应该影响配置保存
240
+ try {
241
+ await mcpService.clearAggregator(targetUserId);
242
+ }
243
+ catch (error) {
244
+ console.error(`[Config] Failed to clear aggregator for user ${targetUserId}:`, error.message || error);
245
+ // 继续执行,不抛出错误
246
+ }
247
+ // 返回更新后的配置(不包含 variables)
248
+ const updatedResult = await db
249
+ .select()
250
+ .from(mcpConfigs)
251
+ .where(eq(mcpConfigs.id, configId))
252
+ .limit(1);
253
+ const configRow = updatedResult[0];
254
+ const config = rowToConfig(configRow);
255
+ // 管理员查看时,隐藏敏感信息(variables)
256
+ const sanitizedConfig = {
257
+ ...config,
258
+ variables: [],
259
+ };
260
+ return c.json({ success: true, data: sanitizedConfig });
261
+ }
262
+ catch (error) {
263
+ return c.json({ success: false, error: error.message }, 500);
264
+ }
265
+ });
266
+ // 测试执行初始化脚本
267
+ configRouter.post('/test-init-script', async (c) => {
268
+ try {
269
+ const currentUserId = getCurrentUserId(c);
270
+ const body = await c.req.json();
271
+ if (!body.script) {
272
+ return c.json({ success: false, error: 'Script is required' }, 400);
273
+ }
274
+ // 获取用户配置
275
+ const result = await db
276
+ .select()
277
+ .from(mcpConfigs)
278
+ .where(eq(mcpConfigs.userId, currentUserId))
279
+ .limit(1);
280
+ if (result.length === 0) {
281
+ return c.json({ success: false, error: 'Config not found' }, 404);
282
+ }
283
+ const config = rowToConfig(result[0]);
284
+ // 获取 MCP 客户端配置(如果存在)
285
+ const mcpClientConfig = mcpService.getMCPClientConfig(currentUserId);
286
+ // 创建临时脚本执行器
287
+ const { ScriptExecutor } = await import('../script/executor.js');
288
+ const executor = new ScriptExecutor(config, mcpClientConfig);
289
+ // 捕获输出
290
+ const output = [];
291
+ const originalLog = console.log;
292
+ const originalError = console.error;
293
+ const originalWarn = console.warn;
294
+ const originalDebug = console.debug;
295
+ // 重定向日志输出
296
+ console.log = (...args) => {
297
+ output.push(`[INFO] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
298
+ originalLog(...args);
299
+ };
300
+ console.error = (...args) => {
301
+ output.push(`[ERROR] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
302
+ originalError(...args);
303
+ };
304
+ console.warn = (...args) => {
305
+ output.push(`[WARN] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
306
+ originalWarn(...args);
307
+ };
308
+ console.debug = (...args) => {
309
+ output.push(`[DEBUG] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
310
+ originalDebug(...args);
311
+ };
312
+ try {
313
+ // 执行脚本
314
+ await executor.executeInitScript(body.script);
315
+ return c.json({
316
+ success: true,
317
+ data: {
318
+ output: output.length > 0 ? output.join('\n') : '执行成功,无输出',
319
+ },
320
+ });
321
+ }
322
+ catch (error) {
323
+ return c.json({
324
+ success: false,
325
+ error: error.message,
326
+ data: {
327
+ output: output.length > 0 ? output.join('\n') : '',
328
+ },
329
+ });
330
+ }
331
+ finally {
332
+ // 恢复原始日志函数
333
+ console.log = originalLog;
334
+ console.error = originalError;
335
+ console.warn = originalWarn;
336
+ console.debug = originalDebug;
337
+ }
338
+ }
339
+ catch (error) {
340
+ return c.json({ success: false, error: error.message }, 500);
341
+ }
342
+ });
343
+ // 测试执行自定义工具脚本
344
+ configRouter.post('/test-custom-tool', async (c) => {
345
+ try {
346
+ const currentUserId = getCurrentUserId(c);
347
+ const body = await c.req.json();
348
+ if (!body.script) {
349
+ return c.json({ success: false, error: 'Script is required' }, 400);
350
+ }
351
+ // 获取用户配置
352
+ const result = await db
353
+ .select()
354
+ .from(mcpConfigs)
355
+ .where(eq(mcpConfigs.userId, currentUserId))
356
+ .limit(1);
357
+ if (result.length === 0) {
358
+ return c.json({ success: false, error: 'Config not found' }, 404);
359
+ }
360
+ const config = rowToConfig(result[0]);
361
+ // 获取 MCP 客户端配置(如果存在)
362
+ const mcpClientConfig = mcpService.getMCPClientConfig(currentUserId);
363
+ // 创建临时脚本执行器
364
+ const { ScriptExecutor } = await import('../script/executor.js');
365
+ const executor = new ScriptExecutor(config, mcpClientConfig);
366
+ // 创建临时自定义工具对象
367
+ const tempTool = {
368
+ id: 'test-tool',
369
+ name: body.toolName || 'test-tool',
370
+ description: 'Test tool',
371
+ serverScript: body.script,
372
+ parameters: [],
373
+ };
374
+ // 捕获输出
375
+ const output = [];
376
+ const originalLog = console.log;
377
+ const originalError = console.error;
378
+ const originalWarn = console.warn;
379
+ const originalDebug = console.debug;
380
+ // 重定向日志输出
381
+ console.log = (...args) => {
382
+ output.push(`[INFO] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
383
+ originalLog(...args);
384
+ };
385
+ console.error = (...args) => {
386
+ output.push(`[ERROR] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
387
+ originalError(...args);
388
+ };
389
+ console.warn = (...args) => {
390
+ output.push(`[WARN] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
391
+ originalWarn(...args);
392
+ };
393
+ console.debug = (...args) => {
394
+ output.push(`[DEBUG] ${args.map(a => typeof a === 'object' ? JSON.stringify(a, null, 2) : String(a)).join(' ')}`);
395
+ originalDebug(...args);
396
+ };
397
+ try {
398
+ // 执行工具脚本
399
+ const result = await executor.executeTool(tempTool, body.args || {});
400
+ return c.json({
401
+ success: true,
402
+ data: {
403
+ result,
404
+ output: output.length > 0 ? output.join('\n') : '执行成功,无输出',
405
+ },
406
+ });
407
+ }
408
+ catch (error) {
409
+ return c.json({
410
+ success: false,
411
+ error: error.message,
412
+ data: {
413
+ output: output.length > 0 ? output.join('\n') : '',
414
+ },
415
+ });
416
+ }
417
+ finally {
418
+ // 恢复原始日志函数
419
+ console.log = originalLog;
420
+ console.error = originalError;
421
+ console.warn = originalWarn;
422
+ console.debug = originalDebug;
423
+ }
424
+ }
425
+ catch (error) {
426
+ return c.json({ success: false, error: error.message }, 500);
427
+ }
428
+ });
429
+ // 同步 MCP 组件工具配置到数据库
430
+ configRouter.post('/sync-tools', async (c) => {
431
+ try {
432
+ const currentUserId = getCurrentUserId(c);
433
+ const body = await c.req.json();
434
+ const componentId = body.componentId; // 可选,如果提供则只同步该组件
435
+ // 获取用户配置
436
+ const result = await db
437
+ .select()
438
+ .from(mcpConfigs)
439
+ .where(eq(mcpConfigs.userId, currentUserId))
440
+ .limit(1);
441
+ if (result.length === 0) {
442
+ return c.json({ success: false, error: 'MCP configuration not found' }, 404);
443
+ }
444
+ const config = rowToConfig(result[0]);
445
+ // 如果指定了 componentId,验证组件存在
446
+ if (componentId) {
447
+ const component = config.components.find(c => c.id === componentId);
448
+ if (!component) {
449
+ return c.json({ success: false, error: `Component ${componentId} not found` }, 404);
450
+ }
451
+ // 注意:即使组件被禁用,也允许同步工具(用于工具管理)
452
+ }
453
+ // 同步工具(允许禁用组件同步工具,用于工具管理)
454
+ const syncResult = await syncComponentTools(config, componentId, true);
455
+ // 上报工具同步统计
456
+ report('tools_synced', {
457
+ component_id: componentId || 'all',
458
+ success_count: syncResult.success,
459
+ failed_count: syncResult.failed,
460
+ has_errors: syncResult.errors.length > 0,
461
+ }, currentUserId);
462
+ // 清理聚合器缓存,强制重新加载
463
+ await mcpService.clearAggregator(currentUserId);
464
+ return c.json({
465
+ success: true,
466
+ data: {
467
+ success: syncResult.success,
468
+ failed: syncResult.failed,
469
+ errors: syncResult.errors,
470
+ },
471
+ });
472
+ }
473
+ catch (error) {
474
+ return c.json({ success: false, error: error.message }, 500);
475
+ }
476
+ });
477
+ // 仅加载组件的工具列表(不保存到数据库,即使组件禁用也可以加载)
478
+ configRouter.post('/component-tools/load', async (c) => {
479
+ try {
480
+ const currentUserId = getCurrentUserId(c);
481
+ const body = await c.req.json();
482
+ const componentId = body.componentId;
483
+ if (!componentId) {
484
+ return c.json({ success: false, error: 'componentId is required' }, 400);
485
+ }
486
+ // 获取用户配置
487
+ const result = await db
488
+ .select()
489
+ .from(mcpConfigs)
490
+ .where(eq(mcpConfigs.userId, currentUserId))
491
+ .limit(1);
492
+ if (result.length === 0) {
493
+ return c.json({ success: false, error: 'MCP configuration not found' }, 404);
494
+ }
495
+ const config = rowToConfig(result[0]);
496
+ // 加载工具列表(即使组件禁用也可以加载)
497
+ const loadResult = await loadComponentToolsList(config, componentId);
498
+ return c.json({
499
+ success: true,
500
+ data: {
501
+ tools: loadResult.tools,
502
+ errors: loadResult.errors,
503
+ },
504
+ });
505
+ }
506
+ catch (error) {
507
+ return c.json({ success: false, error: error.message }, 500);
508
+ }
509
+ });
510
+ // 获取指定组件的工具列表(从数据库读取)
511
+ configRouter.get('/component-tools/:componentId', async (c) => {
512
+ try {
513
+ const currentUserId = getCurrentUserId(c);
514
+ const componentId = c.req.param('componentId');
515
+ // 获取用户配置
516
+ const result = await db
517
+ .select()
518
+ .from(mcpConfigs)
519
+ .where(eq(mcpConfigs.userId, currentUserId))
520
+ .limit(1);
521
+ if (result.length === 0) {
522
+ return c.json({ success: false, error: 'MCP configuration not found' }, 404);
523
+ }
524
+ const config = rowToConfig(result[0]);
525
+ // 验证组件存在
526
+ const component = config.components.find(c => c.id === componentId);
527
+ if (!component) {
528
+ return c.json({ success: false, error: `Component ${componentId} not found` }, 404);
529
+ }
530
+ // 从数据库获取该组件的工具列表(原始数据)
531
+ const dbTools = await db
532
+ .select()
533
+ .from(mcpComponentTools)
534
+ .where(and(eq(mcpComponentTools.configId, config.id), eq(mcpComponentTools.componentId, componentId)));
535
+ // 获取组件的 toolPatches 配置(配置数据,用于覆盖原始数据)
536
+ const toolPatches = component.config?.toolPatches || {};
537
+ // 转换为前端需要的格式
538
+ // 注意:这里应用 toolPatches 来覆盖原始数据,但原始数据仍然保存在数据库中
539
+ const tools = dbTools.map(dbTool => {
540
+ // 从原始数据开始
541
+ let inputSchema = dbTool.inputSchema;
542
+ let description = dbTool.description;
543
+ // 应用工具补丁(如果存在,使用原始工具名称作为键)
544
+ // toolPatches 存储在组件配置中,不在工具记录中
545
+ const toolPatch = toolPatches[dbTool.originalToolName];
546
+ if (toolPatch && typeof toolPatch === 'object') {
547
+ // 应用 description 补丁(如果存在)
548
+ if (toolPatch.description !== undefined && toolPatch.description !== null && toolPatch.description !== '') {
549
+ description = toolPatch.description;
550
+ }
551
+ // 应用 inputSchema 补丁(如果存在)
552
+ if (toolPatch.inputSchema) {
553
+ inputSchema = {
554
+ ...inputSchema,
555
+ ...toolPatch.inputSchema,
556
+ };
557
+ }
558
+ // 应用 fieldDescriptions 补丁(如果存在)
559
+ if (toolPatch.fieldDescriptions && typeof toolPatch.fieldDescriptions === 'object') {
560
+ // 更新 inputSchema 中对应字段的 description
561
+ if (inputSchema.properties) {
562
+ for (const [fieldName, fieldDescription] of Object.entries(toolPatch.fieldDescriptions)) {
563
+ if (inputSchema.properties[fieldName]) {
564
+ inputSchema.properties[fieldName] = {
565
+ ...inputSchema.properties[fieldName],
566
+ description: fieldDescription,
567
+ };
568
+ }
569
+ }
570
+ }
571
+ }
572
+ }
573
+ return {
574
+ name: dbTool.toolName,
575
+ description: description,
576
+ inputSchema: inputSchema,
577
+ disabled: dbTool.disabled, // 使用数据库中存储的 disabled 状态
578
+ originalToolName: dbTool.originalToolName,
579
+ };
580
+ });
581
+ return c.json({
582
+ success: true,
583
+ data: { tools },
584
+ });
585
+ }
586
+ catch (error) {
587
+ return c.json({ success: false, error: error.message }, 500);
588
+ }
589
+ });
590
+ // 获取所有组件的所有工具列表(包括禁用的),用于 Context 类型生成
591
+ // 返回的是原始数据 + 编辑数据的叠加结果
592
+ configRouter.get('/all-tools', async (c) => {
593
+ try {
594
+ const currentUserId = getCurrentUserId(c);
595
+ // 获取用户配置
596
+ const result = await db
597
+ .select()
598
+ .from(mcpConfigs)
599
+ .where(eq(mcpConfigs.userId, currentUserId))
600
+ .limit(1);
601
+ if (result.length === 0) {
602
+ return c.json({ success: false, error: 'MCP configuration not found' }, 404);
603
+ }
604
+ const config = rowToConfig(result[0]);
605
+ // 从数据库获取所有组件的所有工具列表(包括禁用的)
606
+ const dbTools = await db
607
+ .select()
608
+ .from(mcpComponentTools)
609
+ .where(eq(mcpComponentTools.configId, config.id));
610
+ // 创建一个组件ID到组件配置的映射
611
+ const componentMap = new Map();
612
+ for (const comp of config.components) {
613
+ componentMap.set(comp.id, comp);
614
+ }
615
+ // 转换为前端需要的格式,应用 toolPatches
616
+ const allTools = [];
617
+ for (const dbTool of dbTools) {
618
+ const component = componentMap.get(dbTool.componentId);
619
+ if (!component) {
620
+ continue; // 跳过找不到组件的工具
621
+ }
622
+ // 获取组件的 toolPatches 配置(配置数据,用于覆盖原始数据)
623
+ const toolPatches = component.config?.toolPatches || {};
624
+ // 从原始数据开始
625
+ let inputSchema = dbTool.inputSchema;
626
+ let description = dbTool.description;
627
+ // 应用工具补丁(如果存在,使用原始工具名称作为键)
628
+ const toolPatch = toolPatches[dbTool.originalToolName];
629
+ if (toolPatch && typeof toolPatch === 'object') {
630
+ // 应用 description 补丁(如果存在)
631
+ if (toolPatch.description !== undefined && toolPatch.description !== null && toolPatch.description !== '') {
632
+ description = toolPatch.description;
633
+ }
634
+ // 应用 inputSchema 补丁(如果存在)
635
+ if (toolPatch.inputSchema) {
636
+ inputSchema = {
637
+ ...inputSchema,
638
+ ...toolPatch.inputSchema,
639
+ };
640
+ }
641
+ // 应用 fieldDescriptions 补丁(如果存在)
642
+ if (toolPatch.fieldDescriptions && typeof toolPatch.fieldDescriptions === 'object') {
643
+ // 更新 inputSchema 中对应字段的 description
644
+ if (inputSchema.properties) {
645
+ for (const [fieldName, fieldDescription] of Object.entries(toolPatch.fieldDescriptions)) {
646
+ if (inputSchema.properties[fieldName]) {
647
+ inputSchema.properties[fieldName] = {
648
+ ...inputSchema.properties[fieldName],
649
+ description: fieldDescription,
650
+ };
651
+ }
652
+ }
653
+ }
654
+ }
655
+ }
656
+ allTools.push({
657
+ name: dbTool.toolName,
658
+ description: description,
659
+ inputSchema: inputSchema,
660
+ originalToolName: dbTool.originalToolName,
661
+ });
662
+ }
663
+ // 添加自定义工具
664
+ // 自定义工具没有 originalToolName,使用 name 作为工具名称
665
+ for (const customTool of config.customTools) {
666
+ allTools.push({
667
+ name: customTool.name,
668
+ description: customTool.description,
669
+ inputSchema: {
670
+ type: 'object',
671
+ properties: customTool.parameters.reduce((acc, param) => {
672
+ acc[param.name] = {
673
+ type: param.type,
674
+ description: param.description || '',
675
+ };
676
+ return acc;
677
+ }, {}),
678
+ required: customTool.parameters.filter((p) => p.required).map((p) => p.name),
679
+ },
680
+ // 自定义工具没有 originalToolName,所以不设置这个字段
681
+ // Context 类型生成时会使用 name 作为工具名称
682
+ });
683
+ }
684
+ return c.json({
685
+ success: true,
686
+ data: { tools: allTools },
687
+ });
688
+ }
689
+ catch (error) {
690
+ return c.json({ success: false, error: error.message }, 500);
691
+ }
692
+ });
693
+ //# sourceMappingURL=configs.js.map