@axiom-lattice/gateway 2.1.34 → 2.1.36
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/.turbo/turbo-build.log +8 -12
- package/CHANGELOG.md +16 -0
- package/dist/index.js +603 -252
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +510 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/controllers/mcp-configs.ts +691 -0
- package/src/routes/index.ts +3 -0
- package/src/services/agent_service.ts +0 -53
- package/dist/chunk-FSASG3SB.mjs +0 -94
- package/dist/chunk-FSASG3SB.mjs.map +0 -1
- package/dist/config-F3FCBSPH.mjs +0 -9
- package/dist/config-F3FCBSPH.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiom-lattice/gateway",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.36",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/index.mjs",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"pino-roll": "^3.1.0",
|
|
37
37
|
"redis": "^5.0.1",
|
|
38
38
|
"uuid": "^9.0.1",
|
|
39
|
-
"@axiom-lattice/core": "2.1.
|
|
40
|
-
"@axiom-lattice/protocols": "2.1.
|
|
41
|
-
"@axiom-lattice/queue-redis": "1.0.
|
|
39
|
+
"@axiom-lattice/core": "2.1.30",
|
|
40
|
+
"@axiom-lattice/protocols": "2.1.17",
|
|
41
|
+
"@axiom-lattice/queue-redis": "1.0.16"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/jest": "^29.5.14",
|
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Config Controller
|
|
3
|
+
* Handles MCP server configuration CRUD operations with automatic tool registration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FastifyRequest, FastifyReply } from "fastify";
|
|
7
|
+
import {
|
|
8
|
+
getStoreLattice,
|
|
9
|
+
mcpManager,
|
|
10
|
+
toolLatticeManager,
|
|
11
|
+
} from "@axiom-lattice/core";
|
|
12
|
+
import type {
|
|
13
|
+
McpServerConfigStore,
|
|
14
|
+
McpServerConfigEntry,
|
|
15
|
+
CreateMcpServerConfigRequest,
|
|
16
|
+
UpdateMcpServerConfigRequest,
|
|
17
|
+
McpServerConfig,
|
|
18
|
+
McpTool,
|
|
19
|
+
} from "@axiom-lattice/protocols";
|
|
20
|
+
import { randomUUID } from "crypto";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get tenant ID from request headers
|
|
24
|
+
*/
|
|
25
|
+
function getTenantId(request: FastifyRequest): string {
|
|
26
|
+
return (request.headers["x-tenant-id"] as string) || "default";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* MCP server config list response
|
|
31
|
+
*/
|
|
32
|
+
interface McpServerConfigListResponse {
|
|
33
|
+
success: boolean;
|
|
34
|
+
message: string;
|
|
35
|
+
data: {
|
|
36
|
+
records: McpServerConfigEntry[];
|
|
37
|
+
total: number;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* MCP server config response
|
|
43
|
+
*/
|
|
44
|
+
interface McpServerConfigResponse {
|
|
45
|
+
success: boolean;
|
|
46
|
+
message: string;
|
|
47
|
+
data?: McpServerConfigEntry;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Test connection response
|
|
52
|
+
*/
|
|
53
|
+
interface TestConnectionResponse {
|
|
54
|
+
success: boolean;
|
|
55
|
+
message: string;
|
|
56
|
+
data?: {
|
|
57
|
+
connected: boolean;
|
|
58
|
+
latency?: number;
|
|
59
|
+
error?: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* List tools response
|
|
65
|
+
*/
|
|
66
|
+
interface ListToolsResponse {
|
|
67
|
+
success: boolean;
|
|
68
|
+
message: string;
|
|
69
|
+
data?: {
|
|
70
|
+
tools: McpTool[];
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get all MCP server configs for a tenant
|
|
76
|
+
*/
|
|
77
|
+
export async function getMcpServerConfigList(
|
|
78
|
+
request: FastifyRequest,
|
|
79
|
+
reply: FastifyReply
|
|
80
|
+
): Promise<McpServerConfigListResponse> {
|
|
81
|
+
const tenantId = getTenantId(request);
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
85
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
86
|
+
|
|
87
|
+
const configs = await store.getAllConfigs(tenantId);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
message: "MCP server configurations retrieved successfully",
|
|
92
|
+
data: {
|
|
93
|
+
records: configs,
|
|
94
|
+
total: configs.length,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("Failed to get MCP server configs:", error);
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
message: "Failed to retrieve MCP server configurations",
|
|
102
|
+
data: {
|
|
103
|
+
records: [],
|
|
104
|
+
total: 0,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get MCP server config by key
|
|
112
|
+
*/
|
|
113
|
+
export async function getMcpServerConfig(
|
|
114
|
+
request: FastifyRequest,
|
|
115
|
+
reply: FastifyReply
|
|
116
|
+
): Promise<McpServerConfigResponse> {
|
|
117
|
+
const tenantId = getTenantId(request);
|
|
118
|
+
const { key } = request.params as { key: string };
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
122
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
123
|
+
|
|
124
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
125
|
+
|
|
126
|
+
if (!config) {
|
|
127
|
+
return {
|
|
128
|
+
success: false,
|
|
129
|
+
message: "MCP server configuration not found",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
message: "MCP server configuration retrieved successfully",
|
|
136
|
+
data: config,
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("Failed to get MCP server config:", error);
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
message: "Failed to retrieve MCP server configuration",
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create new MCP server config
|
|
149
|
+
*/
|
|
150
|
+
export async function createMcpServerConfig(
|
|
151
|
+
request: FastifyRequest,
|
|
152
|
+
reply: FastifyReply
|
|
153
|
+
): Promise<McpServerConfigResponse> {
|
|
154
|
+
const tenantId = getTenantId(request);
|
|
155
|
+
const body = request.body as CreateMcpServerConfigRequest & {
|
|
156
|
+
id?: string;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
161
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
162
|
+
|
|
163
|
+
// Check if key already exists
|
|
164
|
+
const existing = await store.getConfigByKey(tenantId, body.key);
|
|
165
|
+
if (existing) {
|
|
166
|
+
reply.code(409);
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
message: "MCP server configuration with this key already exists",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const id = body.id || randomUUID();
|
|
174
|
+
const config = await store.createConfig(tenantId, id, body);
|
|
175
|
+
|
|
176
|
+
// Auto-connect and register tools
|
|
177
|
+
try {
|
|
178
|
+
await connectAndRegisterTools(config);
|
|
179
|
+
// Update status to connected
|
|
180
|
+
await store.updateConfig(tenantId, id, { status: "connected" });
|
|
181
|
+
config.status = "connected";
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.warn("Failed to auto-connect MCP server:", error);
|
|
184
|
+
// Update status to error but don't fail the creation
|
|
185
|
+
await store.updateConfig(tenantId, id, { status: "error" });
|
|
186
|
+
config.status = "error";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
reply.code(201);
|
|
190
|
+
return {
|
|
191
|
+
success: true,
|
|
192
|
+
message: "MCP server configuration created successfully",
|
|
193
|
+
data: config,
|
|
194
|
+
};
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error("Failed to create MCP server config:", error);
|
|
197
|
+
return {
|
|
198
|
+
success: false,
|
|
199
|
+
message: "Failed to create MCP server configuration",
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Update MCP server config
|
|
206
|
+
*/
|
|
207
|
+
export async function updateMcpServerConfig(
|
|
208
|
+
request: FastifyRequest,
|
|
209
|
+
reply: FastifyReply
|
|
210
|
+
): Promise<McpServerConfigResponse> {
|
|
211
|
+
const tenantId = getTenantId(request);
|
|
212
|
+
const { key } = request.params as { key: string };
|
|
213
|
+
const updates = request.body as Partial<UpdateMcpServerConfigRequest>;
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
217
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
218
|
+
|
|
219
|
+
const existing = await store.getConfigByKey(tenantId, key);
|
|
220
|
+
if (!existing) {
|
|
221
|
+
reply.code(404);
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
message: "MCP server configuration not found",
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// If config is being updated, we need to reconnect
|
|
229
|
+
const shouldReconnect = updates.config !== undefined;
|
|
230
|
+
|
|
231
|
+
const updated = await store.updateConfig(tenantId, existing.id, updates);
|
|
232
|
+
|
|
233
|
+
if (!updated) {
|
|
234
|
+
return {
|
|
235
|
+
success: false,
|
|
236
|
+
message: "Failed to update MCP server configuration",
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Reconnect and re-register tools if config changed
|
|
241
|
+
if (shouldReconnect) {
|
|
242
|
+
try {
|
|
243
|
+
// Disconnect existing connection if any
|
|
244
|
+
if (mcpManager.hasServer(key)) {
|
|
245
|
+
await mcpManager.removeServer(key);
|
|
246
|
+
}
|
|
247
|
+
// Reconnect with new config
|
|
248
|
+
await connectAndRegisterTools(updated);
|
|
249
|
+
await store.updateConfig(tenantId, existing.id, { status: "connected" });
|
|
250
|
+
updated.status = "connected";
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.warn("Failed to reconnect MCP server:", error);
|
|
253
|
+
await store.updateConfig(tenantId, existing.id, { status: "error" });
|
|
254
|
+
updated.status = "error";
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
success: true,
|
|
260
|
+
message: "MCP server configuration updated successfully",
|
|
261
|
+
data: updated,
|
|
262
|
+
};
|
|
263
|
+
} catch (error) {
|
|
264
|
+
console.error("Failed to update MCP server config:", error);
|
|
265
|
+
return {
|
|
266
|
+
success: false,
|
|
267
|
+
message: "Failed to update MCP server configuration",
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Delete MCP server config by key or id
|
|
274
|
+
*/
|
|
275
|
+
export async function deleteMcpServerConfig(
|
|
276
|
+
request: FastifyRequest,
|
|
277
|
+
reply: FastifyReply
|
|
278
|
+
): Promise<McpServerConfigResponse> {
|
|
279
|
+
const tenantId = getTenantId(request);
|
|
280
|
+
const { keyOrId } = request.params as { keyOrId: string };
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
284
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
285
|
+
|
|
286
|
+
// Try to find by key first, then by id
|
|
287
|
+
let config = await store.getConfigByKey(tenantId, keyOrId);
|
|
288
|
+
let configKey = keyOrId;
|
|
289
|
+
|
|
290
|
+
if (!config) {
|
|
291
|
+
// Try to find by id
|
|
292
|
+
config = await store.getConfigById(tenantId, keyOrId);
|
|
293
|
+
if (config) {
|
|
294
|
+
configKey = config.key;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!config) {
|
|
299
|
+
reply.code(404);
|
|
300
|
+
return {
|
|
301
|
+
success: false,
|
|
302
|
+
message: "MCP server configuration not found",
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Disconnect and remove from MCP manager
|
|
307
|
+
try {
|
|
308
|
+
if (mcpManager.hasServer(configKey)) {
|
|
309
|
+
await mcpManager.removeServer(configKey);
|
|
310
|
+
}
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.warn("Failed to remove from MCP manager:", error);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const deleted = await store.deleteConfig(tenantId, config.id);
|
|
316
|
+
|
|
317
|
+
if (!deleted) {
|
|
318
|
+
return {
|
|
319
|
+
success: false,
|
|
320
|
+
message: "Failed to delete MCP server configuration",
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
success: true,
|
|
326
|
+
message: "MCP server configuration deleted successfully",
|
|
327
|
+
};
|
|
328
|
+
} catch (error) {
|
|
329
|
+
console.error("Failed to delete MCP server config:", error);
|
|
330
|
+
return {
|
|
331
|
+
success: false,
|
|
332
|
+
message: "Failed to delete MCP server configuration",
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Test MCP server connection
|
|
339
|
+
*/
|
|
340
|
+
export async function testMcpServerConnection(
|
|
341
|
+
request: FastifyRequest,
|
|
342
|
+
reply: FastifyReply
|
|
343
|
+
): Promise<TestConnectionResponse> {
|
|
344
|
+
const tenantId = getTenantId(request);
|
|
345
|
+
const { key } = request.params as { key: string };
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
349
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
350
|
+
|
|
351
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
352
|
+
if (!config) {
|
|
353
|
+
reply.code(404);
|
|
354
|
+
return {
|
|
355
|
+
success: false,
|
|
356
|
+
message: "MCP server configuration not found",
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const startTime = Date.now();
|
|
361
|
+
|
|
362
|
+
// Test connection by trying to list tools
|
|
363
|
+
try {
|
|
364
|
+
// Create temporary connection
|
|
365
|
+
const testKey = `__test_${key}_${Date.now()}`;
|
|
366
|
+
const connection = convertToConnection(config.config);
|
|
367
|
+
mcpManager.addServer(testKey, connection);
|
|
368
|
+
|
|
369
|
+
await mcpManager.connect();
|
|
370
|
+
const tools = await mcpManager.getAllTools();
|
|
371
|
+
const latency = Date.now() - startTime;
|
|
372
|
+
|
|
373
|
+
// Cleanup
|
|
374
|
+
await mcpManager.removeServer(testKey);
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
success: true,
|
|
378
|
+
message: "Connection test successful",
|
|
379
|
+
data: {
|
|
380
|
+
connected: true,
|
|
381
|
+
latency,
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
} catch (error) {
|
|
385
|
+
return {
|
|
386
|
+
success: true,
|
|
387
|
+
message: "Connection test failed",
|
|
388
|
+
data: {
|
|
389
|
+
connected: false,
|
|
390
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.error("Failed to test MCP server connection:", error);
|
|
396
|
+
return {
|
|
397
|
+
success: false,
|
|
398
|
+
message: "Failed to test MCP server connection",
|
|
399
|
+
data: {
|
|
400
|
+
connected: false,
|
|
401
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
402
|
+
},
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* List available tools from an MCP server
|
|
409
|
+
*/
|
|
410
|
+
export async function listMcpServerTools(
|
|
411
|
+
request: FastifyRequest,
|
|
412
|
+
reply: FastifyReply
|
|
413
|
+
): Promise<ListToolsResponse> {
|
|
414
|
+
const tenantId = getTenantId(request);
|
|
415
|
+
const { key } = request.params as { key: string };
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
419
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
420
|
+
|
|
421
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
422
|
+
if (!config) {
|
|
423
|
+
reply.code(404);
|
|
424
|
+
return {
|
|
425
|
+
success: false,
|
|
426
|
+
message: "MCP server configuration not found",
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Ensure server is connected
|
|
431
|
+
if (!mcpManager.hasServer(key)) {
|
|
432
|
+
await connectAndRegisterTools(config);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const tools = await mcpManager.getAllTools();
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
success: true,
|
|
439
|
+
message: "Tools retrieved successfully",
|
|
440
|
+
data: {
|
|
441
|
+
tools,
|
|
442
|
+
},
|
|
443
|
+
};
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error("Failed to list MCP tools:", error);
|
|
446
|
+
return {
|
|
447
|
+
success: false,
|
|
448
|
+
message: "Failed to retrieve tools",
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Connect to MCP server and register tools
|
|
455
|
+
*/
|
|
456
|
+
export async function connectMcpServer(
|
|
457
|
+
request: FastifyRequest,
|
|
458
|
+
reply: FastifyReply
|
|
459
|
+
): Promise<McpServerConfigResponse> {
|
|
460
|
+
const tenantId = getTenantId(request);
|
|
461
|
+
const { key } = request.params as { key: string };
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
465
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
466
|
+
|
|
467
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
468
|
+
if (!config) {
|
|
469
|
+
reply.code(404);
|
|
470
|
+
return {
|
|
471
|
+
success: false,
|
|
472
|
+
message: "MCP server configuration not found",
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
await connectAndRegisterTools(config);
|
|
477
|
+
|
|
478
|
+
// Update status
|
|
479
|
+
const updated = await store.updateConfig(tenantId, config.id, {
|
|
480
|
+
status: "connected",
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
return {
|
|
484
|
+
success: true,
|
|
485
|
+
message: "MCP server connected successfully",
|
|
486
|
+
data: updated || config,
|
|
487
|
+
};
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error("Failed to connect MCP server:", error);
|
|
490
|
+
|
|
491
|
+
// Update status to error
|
|
492
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
493
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
494
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
495
|
+
if (config) {
|
|
496
|
+
await store.updateConfig(tenantId, config.id, { status: "error" });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return {
|
|
500
|
+
success: false,
|
|
501
|
+
message: `Failed to connect MCP server: ${
|
|
502
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
503
|
+
}`,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Disconnect from MCP server
|
|
510
|
+
*/
|
|
511
|
+
export async function disconnectMcpServer(
|
|
512
|
+
request: FastifyRequest,
|
|
513
|
+
reply: FastifyReply
|
|
514
|
+
): Promise<McpServerConfigResponse> {
|
|
515
|
+
const tenantId = getTenantId(request);
|
|
516
|
+
const { key } = request.params as { key: string };
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
const storeLattice = getStoreLattice("default", "mcp");
|
|
520
|
+
const store: McpServerConfigStore = storeLattice.store;
|
|
521
|
+
|
|
522
|
+
const config = await store.getConfigByKey(tenantId, key);
|
|
523
|
+
if (!config) {
|
|
524
|
+
reply.code(404);
|
|
525
|
+
return {
|
|
526
|
+
success: false,
|
|
527
|
+
message: "MCP server configuration not found",
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Disconnect from MCP manager
|
|
532
|
+
if (mcpManager.hasServer(key)) {
|
|
533
|
+
await mcpManager.removeServer(key);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Update status
|
|
537
|
+
const updated = await store.updateConfig(tenantId, config.id, {
|
|
538
|
+
status: "disconnected",
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
return {
|
|
542
|
+
success: true,
|
|
543
|
+
message: "MCP server disconnected successfully",
|
|
544
|
+
data: updated || config,
|
|
545
|
+
};
|
|
546
|
+
} catch (error) {
|
|
547
|
+
console.error("Failed to disconnect MCP server:", error);
|
|
548
|
+
return {
|
|
549
|
+
success: false,
|
|
550
|
+
message: "Failed to disconnect MCP server",
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Test MCP server connection and list tools without saving config
|
|
557
|
+
* Used for multi-step configuration flow
|
|
558
|
+
*/
|
|
559
|
+
export async function testMcpServerTools(
|
|
560
|
+
request: FastifyRequest,
|
|
561
|
+
reply: FastifyReply
|
|
562
|
+
): Promise<ListToolsResponse> {
|
|
563
|
+
const body = request.body as {
|
|
564
|
+
config: McpServerConfig;
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
if (!body.config) {
|
|
569
|
+
reply.code(400);
|
|
570
|
+
return {
|
|
571
|
+
success: false,
|
|
572
|
+
message: "config is required",
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const testKey = `__test_${Date.now()}`;
|
|
577
|
+
|
|
578
|
+
// Create temporary connection
|
|
579
|
+
const connection = convertToConnection(body.config);
|
|
580
|
+
mcpManager.addServer(testKey, connection);
|
|
581
|
+
|
|
582
|
+
await mcpManager.connect();
|
|
583
|
+
const tools = await mcpManager.getAllTools();
|
|
584
|
+
|
|
585
|
+
// Cleanup
|
|
586
|
+
await mcpManager.removeServer(testKey);
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
success: true,
|
|
590
|
+
message: "Tools retrieved successfully",
|
|
591
|
+
data: {
|
|
592
|
+
tools,
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
} catch (error) {
|
|
596
|
+
console.error("Failed to test MCP server tools:", error);
|
|
597
|
+
return {
|
|
598
|
+
success: false,
|
|
599
|
+
message: `Failed to retrieve tools: ${
|
|
600
|
+
error instanceof Error ? error.message : String(error)
|
|
601
|
+
}`,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Convert McpServerConfig to Connection format compatible with @langchain/mcp-adapters
|
|
608
|
+
*/
|
|
609
|
+
function convertToConnection(config: McpServerConfig): any {
|
|
610
|
+
const baseConfig = {
|
|
611
|
+
env: config.env,
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
if (config.transport === "stdio") {
|
|
615
|
+
return {
|
|
616
|
+
...baseConfig,
|
|
617
|
+
transport: "stdio" as const,
|
|
618
|
+
command: config.command!,
|
|
619
|
+
args: config.args || [],
|
|
620
|
+
};
|
|
621
|
+
} else {
|
|
622
|
+
// For HTTP/SSE transports
|
|
623
|
+
return {
|
|
624
|
+
...baseConfig,
|
|
625
|
+
transport: config.transport === "streamable_http" ? "http" : config.transport,
|
|
626
|
+
url: config.url!,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Helper function to connect to MCP server and register selected tools
|
|
633
|
+
*/
|
|
634
|
+
async function connectAndRegisterTools(
|
|
635
|
+
config: McpServerConfigEntry
|
|
636
|
+
): Promise<void> {
|
|
637
|
+
// Add server to MCP manager
|
|
638
|
+
const connection = convertToConnection(config.config);
|
|
639
|
+
mcpManager.addServer(config.key, connection);
|
|
640
|
+
|
|
641
|
+
// Connect to server
|
|
642
|
+
await mcpManager.connect();
|
|
643
|
+
|
|
644
|
+
// Get all available tools
|
|
645
|
+
const allTools = await mcpManager.getAllTools();
|
|
646
|
+
|
|
647
|
+
// Filter to only selected tools
|
|
648
|
+
const selectedTools = allTools.filter((tool) =>
|
|
649
|
+
config.selectedTools.includes(tool.name)
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
// Register selected tools to ToolLattice
|
|
653
|
+
for (const tool of selectedTools) {
|
|
654
|
+
toolLatticeManager.registerExistingTool(tool.name, tool);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Register MCP server config routes
|
|
660
|
+
*/
|
|
661
|
+
export function registerMcpServerConfigRoutes(app: any): void {
|
|
662
|
+
// Get all configs
|
|
663
|
+
app.get("/api/mcp-servers", getMcpServerConfigList);
|
|
664
|
+
|
|
665
|
+
// Get config by key
|
|
666
|
+
app.get("/api/mcp-servers/:key", getMcpServerConfig);
|
|
667
|
+
|
|
668
|
+
// Create config
|
|
669
|
+
app.post("/api/mcp-servers", createMcpServerConfig);
|
|
670
|
+
|
|
671
|
+
// Update config
|
|
672
|
+
app.put("/api/mcp-servers/:key", updateMcpServerConfig);
|
|
673
|
+
|
|
674
|
+
// Delete config by key or id
|
|
675
|
+
app.delete("/api/mcp-servers/:keyOrId", deleteMcpServerConfig);
|
|
676
|
+
|
|
677
|
+
// Test connection
|
|
678
|
+
app.post("/api/mcp-servers/:key/test", testMcpServerConnection);
|
|
679
|
+
|
|
680
|
+
// List available tools
|
|
681
|
+
app.get("/api/mcp-servers/:key/tools", listMcpServerTools);
|
|
682
|
+
|
|
683
|
+
// Connect to server
|
|
684
|
+
app.post("/api/mcp-servers/:key/connect", connectMcpServer);
|
|
685
|
+
|
|
686
|
+
// Disconnect from server
|
|
687
|
+
app.post("/api/mcp-servers/:key/disconnect", disconnectMcpServer);
|
|
688
|
+
|
|
689
|
+
// Test tools without saving config (for multi-step config flow)
|
|
690
|
+
app.post("/api/mcp-servers/test-tools", testMcpServerTools);
|
|
691
|
+
}
|