@juspay/neurolink 1.6.0 → 1.9.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.
- package/CHANGELOG.md +193 -7
- package/README.md +100 -17
- package/dist/agent/direct-tools.d.ts +1203 -0
- package/dist/agent/direct-tools.js +387 -0
- package/dist/cli/commands/agent-generate.d.ts +2 -0
- package/dist/cli/commands/agent-generate.js +70 -0
- package/dist/cli/commands/config.d.ts +6 -6
- package/dist/cli/commands/config.js +326 -273
- package/dist/cli/commands/mcp.d.ts +2 -1
- package/dist/cli/commands/mcp.js +874 -146
- package/dist/cli/commands/ollama.d.ts +1 -1
- package/dist/cli/commands/ollama.js +153 -143
- package/dist/cli/index.js +589 -323
- package/dist/cli/utils/complete-setup.d.ts +19 -0
- package/dist/cli/utils/complete-setup.js +81 -0
- package/dist/cli/utils/env-manager.d.ts +44 -0
- package/dist/cli/utils/env-manager.js +226 -0
- package/dist/cli/utils/interactive-setup.d.ts +48 -0
- package/dist/cli/utils/interactive-setup.js +302 -0
- package/dist/core/dynamic-models.d.ts +208 -0
- package/dist/core/dynamic-models.js +250 -0
- package/dist/core/factory.d.ts +13 -6
- package/dist/core/factory.js +176 -61
- package/dist/core/types.d.ts +4 -2
- package/dist/core/types.js +4 -4
- package/dist/index.d.ts +16 -16
- package/dist/index.js +16 -16
- package/dist/lib/agent/direct-tools.d.ts +1203 -0
- package/dist/lib/agent/direct-tools.js +387 -0
- package/dist/lib/core/dynamic-models.d.ts +208 -0
- package/dist/lib/core/dynamic-models.js +250 -0
- package/dist/lib/core/factory.d.ts +13 -6
- package/dist/lib/core/factory.js +176 -61
- package/dist/lib/core/types.d.ts +4 -2
- package/dist/lib/core/types.js +4 -4
- package/dist/lib/index.d.ts +16 -16
- package/dist/lib/index.js +16 -16
- package/dist/lib/mcp/auto-discovery.d.ts +120 -0
- package/dist/lib/mcp/auto-discovery.js +793 -0
- package/dist/lib/mcp/client.d.ts +66 -0
- package/dist/lib/mcp/client.js +245 -0
- package/dist/lib/mcp/config.d.ts +31 -0
- package/dist/lib/mcp/config.js +74 -0
- package/dist/lib/mcp/context-manager.d.ts +4 -4
- package/dist/lib/mcp/context-manager.js +24 -18
- package/dist/lib/mcp/factory.d.ts +28 -11
- package/dist/lib/mcp/factory.js +36 -29
- package/dist/lib/mcp/function-calling.d.ts +51 -0
- package/dist/lib/mcp/function-calling.js +510 -0
- package/dist/lib/mcp/index.d.ts +190 -0
- package/dist/lib/mcp/index.js +156 -0
- package/dist/lib/mcp/initialize-tools.d.ts +28 -0
- package/dist/lib/mcp/initialize-tools.js +209 -0
- package/dist/lib/mcp/initialize.d.ts +17 -0
- package/dist/lib/mcp/initialize.js +51 -0
- package/dist/lib/mcp/logging.d.ts +71 -0
- package/dist/lib/mcp/logging.js +183 -0
- package/dist/lib/mcp/manager.d.ts +67 -0
- package/dist/lib/mcp/manager.js +176 -0
- package/dist/lib/mcp/neurolink-mcp-client.d.ts +96 -0
- package/dist/lib/mcp/neurolink-mcp-client.js +417 -0
- package/dist/lib/mcp/orchestrator.d.ts +3 -3
- package/dist/lib/mcp/orchestrator.js +46 -43
- package/dist/lib/mcp/registry.d.ts +2 -2
- package/dist/lib/mcp/registry.js +42 -33
- package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.d.ts +1 -1
- package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.js +204 -65
- package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +142 -102
- package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +6 -6
- package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.js +197 -142
- package/dist/lib/mcp/servers/utilities/utility-server.d.ts +8 -0
- package/dist/lib/mcp/servers/utilities/utility-server.js +326 -0
- package/dist/lib/mcp/tool-integration.d.ts +67 -0
- package/dist/lib/mcp/tool-integration.js +179 -0
- package/dist/lib/mcp/unified-registry.d.ts +269 -0
- package/dist/lib/mcp/unified-registry.js +1411 -0
- package/dist/lib/neurolink.d.ts +68 -6
- package/dist/lib/neurolink.js +304 -42
- package/dist/lib/providers/agent-enhanced-provider.d.ts +59 -0
- package/dist/lib/providers/agent-enhanced-provider.js +242 -0
- package/dist/lib/providers/amazonBedrock.d.ts +3 -3
- package/dist/lib/providers/amazonBedrock.js +54 -50
- package/dist/lib/providers/anthropic.d.ts +2 -2
- package/dist/lib/providers/anthropic.js +92 -84
- package/dist/lib/providers/azureOpenAI.d.ts +2 -2
- package/dist/lib/providers/azureOpenAI.js +97 -86
- package/dist/lib/providers/function-calling-provider.d.ts +70 -0
- package/dist/lib/providers/function-calling-provider.js +359 -0
- package/dist/lib/providers/googleAIStudio.d.ts +10 -5
- package/dist/lib/providers/googleAIStudio.js +60 -38
- package/dist/lib/providers/googleVertexAI.d.ts +3 -3
- package/dist/lib/providers/googleVertexAI.js +96 -86
- package/dist/lib/providers/huggingFace.d.ts +3 -3
- package/dist/lib/providers/huggingFace.js +70 -63
- package/dist/lib/providers/index.d.ts +11 -11
- package/dist/lib/providers/index.js +18 -18
- package/dist/lib/providers/mcp-provider.d.ts +62 -0
- package/dist/lib/providers/mcp-provider.js +183 -0
- package/dist/lib/providers/mistralAI.d.ts +3 -3
- package/dist/lib/providers/mistralAI.js +42 -36
- package/dist/lib/providers/ollama.d.ts +4 -4
- package/dist/lib/providers/ollama.js +113 -98
- package/dist/lib/providers/openAI.d.ts +7 -3
- package/dist/lib/providers/openAI.js +45 -33
- package/dist/lib/utils/logger.js +2 -2
- package/dist/lib/utils/providerUtils.js +53 -31
- package/dist/mcp/auto-discovery.d.ts +120 -0
- package/dist/mcp/auto-discovery.js +794 -0
- package/dist/mcp/client.d.ts +66 -0
- package/dist/mcp/client.js +245 -0
- package/dist/mcp/config.d.ts +31 -0
- package/dist/mcp/config.js +74 -0
- package/dist/mcp/context-manager.d.ts +4 -4
- package/dist/mcp/context-manager.js +24 -18
- package/dist/mcp/factory.d.ts +28 -11
- package/dist/mcp/factory.js +36 -29
- package/dist/mcp/function-calling.d.ts +51 -0
- package/dist/mcp/function-calling.js +510 -0
- package/dist/mcp/index.d.ts +190 -0
- package/dist/mcp/index.js +156 -0
- package/dist/mcp/initialize-tools.d.ts +28 -0
- package/dist/mcp/initialize-tools.js +210 -0
- package/dist/mcp/initialize.d.ts +17 -0
- package/dist/mcp/initialize.js +51 -0
- package/dist/mcp/logging.d.ts +71 -0
- package/dist/mcp/logging.js +183 -0
- package/dist/mcp/manager.d.ts +67 -0
- package/dist/mcp/manager.js +176 -0
- package/dist/mcp/neurolink-mcp-client.d.ts +96 -0
- package/dist/mcp/neurolink-mcp-client.js +417 -0
- package/dist/mcp/orchestrator.d.ts +3 -3
- package/dist/mcp/orchestrator.js +46 -43
- package/dist/mcp/registry.d.ts +2 -2
- package/dist/mcp/registry.js +42 -33
- package/dist/mcp/servers/ai-providers/ai-analysis-tools.d.ts +1 -1
- package/dist/mcp/servers/ai-providers/ai-analysis-tools.js +204 -65
- package/dist/mcp/servers/ai-providers/ai-core-server.js +142 -102
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +6 -6
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +197 -142
- package/dist/mcp/servers/utilities/utility-server.d.ts +8 -0
- package/dist/mcp/servers/utilities/utility-server.js +326 -0
- package/dist/mcp/tool-integration.d.ts +67 -0
- package/dist/mcp/tool-integration.js +179 -0
- package/dist/mcp/unified-registry.d.ts +269 -0
- package/dist/mcp/unified-registry.js +1411 -0
- package/dist/neurolink.d.ts +68 -6
- package/dist/neurolink.js +304 -42
- package/dist/providers/agent-enhanced-provider.d.ts +59 -0
- package/dist/providers/agent-enhanced-provider.js +242 -0
- package/dist/providers/amazonBedrock.d.ts +3 -3
- package/dist/providers/amazonBedrock.js +54 -50
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +92 -84
- package/dist/providers/azureOpenAI.d.ts +2 -2
- package/dist/providers/azureOpenAI.js +97 -86
- package/dist/providers/function-calling-provider.d.ts +70 -0
- package/dist/providers/function-calling-provider.js +359 -0
- package/dist/providers/googleAIStudio.d.ts +10 -5
- package/dist/providers/googleAIStudio.js +60 -38
- package/dist/providers/googleVertexAI.d.ts +3 -3
- package/dist/providers/googleVertexAI.js +96 -86
- package/dist/providers/huggingFace.d.ts +3 -3
- package/dist/providers/huggingFace.js +70 -63
- package/dist/providers/index.d.ts +11 -11
- package/dist/providers/index.js +18 -18
- package/dist/providers/mcp-provider.d.ts +62 -0
- package/dist/providers/mcp-provider.js +183 -0
- package/dist/providers/mistralAI.d.ts +3 -3
- package/dist/providers/mistralAI.js +42 -36
- package/dist/providers/ollama.d.ts +4 -4
- package/dist/providers/ollama.js +113 -98
- package/dist/providers/openAI.d.ts +7 -3
- package/dist/providers/openAI.js +45 -33
- package/dist/utils/logger.js +2 -2
- package/dist/utils/providerUtils.js +53 -31
- package/package.json +175 -161
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NeuroLink MCP Auto-Discovery System
|
|
3
|
+
* Automatically discovers MCP servers from common configuration locations across different tools
|
|
4
|
+
* Supports VS Code, Cursor, Claude Desktop, Windsurf, Roo Code, and generic configurations
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from "fs/promises";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import * as os from "os";
|
|
9
|
+
import { autoDiscoveryLogger } from "./logging.js";
|
|
10
|
+
/**
|
|
11
|
+
* Claude Desktop configuration parser
|
|
12
|
+
*/
|
|
13
|
+
class ClaudeDesktopParser {
|
|
14
|
+
canParse(filePath, content) {
|
|
15
|
+
return (filePath.includes("claude_desktop_config.json") &&
|
|
16
|
+
content &&
|
|
17
|
+
typeof content === "object" &&
|
|
18
|
+
(content.mcpServers || content.mcp_servers));
|
|
19
|
+
}
|
|
20
|
+
parse(filePath, content, source) {
|
|
21
|
+
const servers = [];
|
|
22
|
+
const mcpServers = content.mcpServers || content.mcp_servers || {};
|
|
23
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
24
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
25
|
+
const config = serverConfig;
|
|
26
|
+
servers.push({
|
|
27
|
+
id: serverId,
|
|
28
|
+
title: config.title || serverId,
|
|
29
|
+
command: config.command || "node",
|
|
30
|
+
args: config.args || [],
|
|
31
|
+
env: config.env || {},
|
|
32
|
+
cwd: config.cwd,
|
|
33
|
+
source,
|
|
34
|
+
configPath: filePath,
|
|
35
|
+
rawConfig: config,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return servers;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* VS Code configuration parser
|
|
44
|
+
*/
|
|
45
|
+
class VSCodeParser {
|
|
46
|
+
canParse(filePath, content) {
|
|
47
|
+
return ((filePath.includes(".vscode/mcp.json") ||
|
|
48
|
+
filePath.includes("settings.json")) &&
|
|
49
|
+
content &&
|
|
50
|
+
typeof content === "object");
|
|
51
|
+
}
|
|
52
|
+
parse(filePath, content, source) {
|
|
53
|
+
const servers = [];
|
|
54
|
+
// Handle .vscode/mcp.json format
|
|
55
|
+
if (filePath.includes("mcp.json")) {
|
|
56
|
+
const mcpServers = content.mcpServers || content.servers || {};
|
|
57
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
58
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
59
|
+
const config = serverConfig;
|
|
60
|
+
servers.push({
|
|
61
|
+
id: serverId,
|
|
62
|
+
title: config.title || serverId,
|
|
63
|
+
command: config.command || "node",
|
|
64
|
+
args: config.args || [],
|
|
65
|
+
env: config.env || {},
|
|
66
|
+
cwd: config.cwd,
|
|
67
|
+
source,
|
|
68
|
+
configPath: filePath,
|
|
69
|
+
rawConfig: config,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Handle settings.json with MCP configuration
|
|
75
|
+
if (filePath.includes("settings.json") && content.mcp) {
|
|
76
|
+
const mcpConfig = content.mcp;
|
|
77
|
+
if (mcpConfig.servers) {
|
|
78
|
+
for (const [serverId, serverConfig] of Object.entries(mcpConfig.servers)) {
|
|
79
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
80
|
+
const config = serverConfig;
|
|
81
|
+
servers.push({
|
|
82
|
+
id: serverId,
|
|
83
|
+
title: config.title || serverId,
|
|
84
|
+
command: config.command || "node",
|
|
85
|
+
args: config.args || [],
|
|
86
|
+
env: config.env || {},
|
|
87
|
+
cwd: config.cwd,
|
|
88
|
+
source,
|
|
89
|
+
configPath: filePath,
|
|
90
|
+
rawConfig: config,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return servers;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Cursor configuration parser
|
|
101
|
+
*/
|
|
102
|
+
class CursorParser {
|
|
103
|
+
canParse(filePath, content) {
|
|
104
|
+
return (filePath.includes(".cursor/mcp.json") ||
|
|
105
|
+
(filePath.includes("mcp.json") && content && typeof content === "object"));
|
|
106
|
+
}
|
|
107
|
+
parse(filePath, content, source) {
|
|
108
|
+
const servers = [];
|
|
109
|
+
const mcpServers = content.mcpServers || content.servers || {};
|
|
110
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
111
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
112
|
+
const config = serverConfig;
|
|
113
|
+
servers.push({
|
|
114
|
+
id: serverId,
|
|
115
|
+
title: config.title || serverId,
|
|
116
|
+
command: config.command || "node",
|
|
117
|
+
args: config.args || [],
|
|
118
|
+
env: config.env || {},
|
|
119
|
+
cwd: config.cwd,
|
|
120
|
+
source,
|
|
121
|
+
configPath: filePath,
|
|
122
|
+
rawConfig: config,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return servers;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Windsurf configuration parser
|
|
131
|
+
*/
|
|
132
|
+
class WindsurfParser {
|
|
133
|
+
canParse(filePath, content) {
|
|
134
|
+
return (filePath.includes("windsurf/mcp_config.json") &&
|
|
135
|
+
content &&
|
|
136
|
+
typeof content === "object");
|
|
137
|
+
}
|
|
138
|
+
parse(filePath, content, source) {
|
|
139
|
+
const servers = [];
|
|
140
|
+
const mcpServers = content.mcpServers || content.servers || {};
|
|
141
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
142
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
143
|
+
const config = serverConfig;
|
|
144
|
+
servers.push({
|
|
145
|
+
id: serverId,
|
|
146
|
+
title: config.title || serverId,
|
|
147
|
+
command: config.command || "node",
|
|
148
|
+
args: config.args || [],
|
|
149
|
+
env: config.env || {},
|
|
150
|
+
cwd: config.cwd,
|
|
151
|
+
source,
|
|
152
|
+
configPath: filePath,
|
|
153
|
+
rawConfig: config,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return servers;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Cline AI Coder configuration parser
|
|
162
|
+
*/
|
|
163
|
+
class ClineParser {
|
|
164
|
+
canParse(filePath, content) {
|
|
165
|
+
return (filePath.includes("cline_mcp_settings.json") &&
|
|
166
|
+
content &&
|
|
167
|
+
typeof content === "object" &&
|
|
168
|
+
(content.mcpServers || content.servers));
|
|
169
|
+
}
|
|
170
|
+
parse(filePath, content, source) {
|
|
171
|
+
const servers = [];
|
|
172
|
+
const mcpServers = content.mcpServers || content.servers || {};
|
|
173
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
174
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
175
|
+
const config = serverConfig;
|
|
176
|
+
servers.push({
|
|
177
|
+
id: serverId,
|
|
178
|
+
title: config.title || serverId,
|
|
179
|
+
command: config.command || "node",
|
|
180
|
+
args: config.args || [],
|
|
181
|
+
env: config.env || {},
|
|
182
|
+
cwd: config.cwd,
|
|
183
|
+
source,
|
|
184
|
+
configPath: filePath,
|
|
185
|
+
rawConfig: config,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return servers;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Continue Dev configuration parser
|
|
194
|
+
*/
|
|
195
|
+
class ContinueParser {
|
|
196
|
+
canParse(filePath, content) {
|
|
197
|
+
return ((filePath.includes("continue/config.json") ||
|
|
198
|
+
filePath.includes(".continue/config.json")) &&
|
|
199
|
+
content &&
|
|
200
|
+
typeof content === "object" &&
|
|
201
|
+
(content.mcpServers || content.contextProviders?.mcp));
|
|
202
|
+
}
|
|
203
|
+
parse(filePath, content, source) {
|
|
204
|
+
const servers = [];
|
|
205
|
+
// Continue may have MCP servers in contextProviders.mcp or directly in mcpServers
|
|
206
|
+
const mcpServers = content.mcpServers || content.contextProviders?.mcp || {};
|
|
207
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
208
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
209
|
+
const config = serverConfig;
|
|
210
|
+
servers.push({
|
|
211
|
+
id: serverId,
|
|
212
|
+
title: config.title || serverId,
|
|
213
|
+
command: config.command || "node",
|
|
214
|
+
args: config.args || [],
|
|
215
|
+
env: config.env || {},
|
|
216
|
+
cwd: config.cwd,
|
|
217
|
+
source,
|
|
218
|
+
configPath: filePath,
|
|
219
|
+
rawConfig: config,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return servers;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Aider configuration parser
|
|
228
|
+
*/
|
|
229
|
+
class AiderParser {
|
|
230
|
+
canParse(filePath, content) {
|
|
231
|
+
return ((filePath.includes(".aider") || filePath.includes("aider.conf")) &&
|
|
232
|
+
content &&
|
|
233
|
+
typeof content === "object" &&
|
|
234
|
+
content.mcp_servers);
|
|
235
|
+
}
|
|
236
|
+
parse(filePath, content, source) {
|
|
237
|
+
const servers = [];
|
|
238
|
+
const mcpServers = content.mcp_servers || {};
|
|
239
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
240
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
241
|
+
const config = serverConfig;
|
|
242
|
+
servers.push({
|
|
243
|
+
id: serverId,
|
|
244
|
+
title: config.title || serverId,
|
|
245
|
+
command: config.command || "node",
|
|
246
|
+
args: config.args || [],
|
|
247
|
+
env: config.env || {},
|
|
248
|
+
cwd: config.cwd,
|
|
249
|
+
source,
|
|
250
|
+
configPath: filePath,
|
|
251
|
+
rawConfig: config,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return servers;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Generic MCP configuration parser
|
|
260
|
+
*/
|
|
261
|
+
class GenericParser {
|
|
262
|
+
canParse(filePath, content) {
|
|
263
|
+
return ((filePath.includes("mcp.json") ||
|
|
264
|
+
filePath.includes("mcp-config.json") ||
|
|
265
|
+
filePath.includes("mcp_config.json")) &&
|
|
266
|
+
content &&
|
|
267
|
+
typeof content === "object");
|
|
268
|
+
}
|
|
269
|
+
parse(filePath, content, source) {
|
|
270
|
+
const servers = [];
|
|
271
|
+
const mcpServers = content.mcpServers || content.servers || content;
|
|
272
|
+
for (const [serverId, serverConfig] of Object.entries(mcpServers)) {
|
|
273
|
+
if (typeof serverConfig === "object" && serverConfig !== null) {
|
|
274
|
+
const config = serverConfig;
|
|
275
|
+
servers.push({
|
|
276
|
+
id: serverId,
|
|
277
|
+
title: config.title || serverId,
|
|
278
|
+
command: config.command || "node",
|
|
279
|
+
args: config.args || [],
|
|
280
|
+
env: config.env || {},
|
|
281
|
+
cwd: config.cwd,
|
|
282
|
+
source,
|
|
283
|
+
configPath: filePath,
|
|
284
|
+
rawConfig: config,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return servers;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* MCP Auto-Discovery Engine
|
|
293
|
+
*/
|
|
294
|
+
export class MCPAutoDiscovery {
|
|
295
|
+
parsers = [
|
|
296
|
+
new ClaudeDesktopParser(),
|
|
297
|
+
new ClineParser(), // Move Cline parser before VSCode parser
|
|
298
|
+
new VSCodeParser(),
|
|
299
|
+
new CursorParser(),
|
|
300
|
+
new WindsurfParser(),
|
|
301
|
+
new ContinueParser(),
|
|
302
|
+
new AiderParser(),
|
|
303
|
+
new GenericParser(),
|
|
304
|
+
];
|
|
305
|
+
/**
|
|
306
|
+
* Discover MCP servers from all common locations
|
|
307
|
+
*
|
|
308
|
+
* @param options Discovery configuration options
|
|
309
|
+
* @returns Discovery result with found servers
|
|
310
|
+
*/
|
|
311
|
+
async discoverServers(options = {}) {
|
|
312
|
+
const startTime = Date.now();
|
|
313
|
+
const discovered = [];
|
|
314
|
+
const sources = [];
|
|
315
|
+
const errors = [];
|
|
316
|
+
let configFilesFound = 0;
|
|
317
|
+
const { searchWorkspace = true, searchGlobal = true, searchCommonPaths = true, followSymlinks = false, maxDepth = 3, includeInactive = true, preferredTools = [], } = options;
|
|
318
|
+
// Define search paths based on options
|
|
319
|
+
const searchPaths = this.getSearchPaths({
|
|
320
|
+
searchWorkspace,
|
|
321
|
+
searchGlobal,
|
|
322
|
+
searchCommonPaths,
|
|
323
|
+
});
|
|
324
|
+
autoDiscoveryLogger.debug(`Starting discovery with ${searchPaths.length} search paths`);
|
|
325
|
+
// Search each path
|
|
326
|
+
for (const searchPath of searchPaths) {
|
|
327
|
+
try {
|
|
328
|
+
const pathResults = await this.searchPath(searchPath, maxDepth, followSymlinks, includeInactive);
|
|
329
|
+
discovered.push(...pathResults.servers);
|
|
330
|
+
sources.push(...pathResults.sources);
|
|
331
|
+
configFilesFound += pathResults.configFilesFound;
|
|
332
|
+
if (pathResults.errors.length > 0) {
|
|
333
|
+
errors.push(...pathResults.errors);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
338
|
+
errors.push(`Error searching path ${searchPath.path}: ${errorMessage}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Remove duplicates and apply prioritization
|
|
342
|
+
const uniqueServers = this.deduplicateServers(discovered, preferredTools);
|
|
343
|
+
const duplicatesRemoved = discovered.length - uniqueServers.length;
|
|
344
|
+
const executionTime = Date.now() - startTime;
|
|
345
|
+
autoDiscoveryLogger.debug(`Discovery completed in ${executionTime}ms: ` +
|
|
346
|
+
`${uniqueServers.length} servers found, ${duplicatesRemoved} duplicates removed`);
|
|
347
|
+
return {
|
|
348
|
+
discovered: uniqueServers,
|
|
349
|
+
sources: [...new Set(sources)], // Remove duplicate sources
|
|
350
|
+
errors,
|
|
351
|
+
stats: {
|
|
352
|
+
configFilesFound,
|
|
353
|
+
serversDiscovered: uniqueServers.length,
|
|
354
|
+
duplicatesRemoved,
|
|
355
|
+
executionTime,
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Auto-register discovered servers with a registry
|
|
361
|
+
*
|
|
362
|
+
* @param registry Target MCP registry
|
|
363
|
+
* @param options Discovery options
|
|
364
|
+
* @returns Registration results
|
|
365
|
+
*/
|
|
366
|
+
async autoRegisterServers(registry, options = {}) {
|
|
367
|
+
const discoveryResult = await this.discoverServers(options);
|
|
368
|
+
const registered = [];
|
|
369
|
+
const failed = [];
|
|
370
|
+
const errors = [...discoveryResult.errors];
|
|
371
|
+
for (const server of discoveryResult.discovered) {
|
|
372
|
+
try {
|
|
373
|
+
// Convert discovered server to NeuroLink MCP server format
|
|
374
|
+
const mcpServer = await this.convertToMCPServer(server);
|
|
375
|
+
// Attempt to register
|
|
376
|
+
await registry.registerServer(mcpServer);
|
|
377
|
+
registered.push(server.id);
|
|
378
|
+
autoDiscoveryLogger.info(`Successfully registered server '${server.id}' from ${server.source.tool}`);
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
382
|
+
failed.push(server.id);
|
|
383
|
+
errors.push(`Failed to register server '${server.id}': ${errorMessage}`);
|
|
384
|
+
autoDiscoveryLogger.error(`Failed to register server '${server.id}': ${errorMessage}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return { registered, failed, errors };
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get standard search paths for MCP configurations
|
|
391
|
+
*/
|
|
392
|
+
getSearchPaths(options) {
|
|
393
|
+
const paths = [];
|
|
394
|
+
const homeDir = os.homedir();
|
|
395
|
+
const currentDir = process.cwd();
|
|
396
|
+
// Global configurations
|
|
397
|
+
if (options.searchGlobal) {
|
|
398
|
+
// Claude Desktop
|
|
399
|
+
paths.push({
|
|
400
|
+
path: path.join(homeDir, "Library", "Application Support", "Claude"),
|
|
401
|
+
source: {
|
|
402
|
+
tool: "Claude Desktop",
|
|
403
|
+
type: "global",
|
|
404
|
+
priority: 9,
|
|
405
|
+
description: "Claude Desktop global configuration",
|
|
406
|
+
},
|
|
407
|
+
patterns: ["claude_desktop_config.json"],
|
|
408
|
+
});
|
|
409
|
+
// Cursor global
|
|
410
|
+
paths.push({
|
|
411
|
+
path: path.join(homeDir, ".cursor"),
|
|
412
|
+
source: {
|
|
413
|
+
tool: "Cursor",
|
|
414
|
+
type: "global",
|
|
415
|
+
priority: 8,
|
|
416
|
+
description: "Cursor global configuration",
|
|
417
|
+
},
|
|
418
|
+
patterns: ["mcp.json"],
|
|
419
|
+
});
|
|
420
|
+
// Windsurf global
|
|
421
|
+
paths.push({
|
|
422
|
+
path: path.join(homeDir, ".codeium", "windsurf"),
|
|
423
|
+
source: {
|
|
424
|
+
tool: "Windsurf",
|
|
425
|
+
type: "global",
|
|
426
|
+
priority: 8,
|
|
427
|
+
description: "Windsurf global configuration",
|
|
428
|
+
},
|
|
429
|
+
patterns: ["mcp_config.json"],
|
|
430
|
+
});
|
|
431
|
+
// VS Code global
|
|
432
|
+
const vscodeGlobalPaths = [
|
|
433
|
+
path.join(homeDir, "Library", "Application Support", "Code", "User"), // macOS
|
|
434
|
+
path.join(homeDir, ".config", "Code", "User"), // Linux
|
|
435
|
+
path.join(homeDir, "AppData", "Roaming", "Code", "User"), // Windows
|
|
436
|
+
];
|
|
437
|
+
for (const vscodeGlobalPath of vscodeGlobalPaths) {
|
|
438
|
+
paths.push({
|
|
439
|
+
path: vscodeGlobalPath,
|
|
440
|
+
source: {
|
|
441
|
+
tool: "VS Code",
|
|
442
|
+
type: "global",
|
|
443
|
+
priority: 7,
|
|
444
|
+
description: "VS Code global configuration",
|
|
445
|
+
},
|
|
446
|
+
patterns: ["settings.json"],
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
// Cline AI Coder - stored in VS Code extension globalStorage
|
|
450
|
+
const clineGlobalPaths = [
|
|
451
|
+
path.join(homeDir, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // macOS
|
|
452
|
+
path.join(homeDir, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Linux
|
|
453
|
+
path.join(homeDir, "AppData", "Roaming", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Windows
|
|
454
|
+
// VS Code Insiders
|
|
455
|
+
path.join(homeDir, "Library", "Application Support", "Code - Insiders", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // macOS
|
|
456
|
+
path.join(homeDir, ".config", "Code - Insiders", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Linux
|
|
457
|
+
path.join(homeDir, "AppData", "Roaming", "Code - Insiders", "User", "globalStorage", "saoudrizwan.claude-dev", "settings"), // Windows
|
|
458
|
+
];
|
|
459
|
+
for (const clineGlobalPath of clineGlobalPaths) {
|
|
460
|
+
paths.push({
|
|
461
|
+
path: clineGlobalPath,
|
|
462
|
+
source: {
|
|
463
|
+
tool: "Cline AI Coder",
|
|
464
|
+
type: "global",
|
|
465
|
+
priority: 8,
|
|
466
|
+
description: "Cline AI Coder extension configuration",
|
|
467
|
+
},
|
|
468
|
+
patterns: ["cline_mcp_settings.json"],
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
// Continue Dev global configuration
|
|
472
|
+
paths.push({
|
|
473
|
+
path: path.join(homeDir, ".continue"),
|
|
474
|
+
source: {
|
|
475
|
+
tool: "Continue Dev",
|
|
476
|
+
type: "global",
|
|
477
|
+
priority: 7,
|
|
478
|
+
description: "Continue Dev global configuration",
|
|
479
|
+
},
|
|
480
|
+
patterns: ["config.json"],
|
|
481
|
+
});
|
|
482
|
+
// Aider global configuration
|
|
483
|
+
paths.push({
|
|
484
|
+
path: path.join(homeDir, ".aider"),
|
|
485
|
+
source: {
|
|
486
|
+
tool: "Aider",
|
|
487
|
+
type: "global",
|
|
488
|
+
priority: 7,
|
|
489
|
+
description: "Aider global configuration",
|
|
490
|
+
},
|
|
491
|
+
patterns: ["config.json", "aider.conf"],
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
// Workspace/project configurations
|
|
495
|
+
if (options.searchWorkspace) {
|
|
496
|
+
// VS Code workspace
|
|
497
|
+
paths.push({
|
|
498
|
+
path: path.join(currentDir, ".vscode"),
|
|
499
|
+
source: {
|
|
500
|
+
tool: "VS Code",
|
|
501
|
+
type: "workspace",
|
|
502
|
+
priority: 9,
|
|
503
|
+
description: "VS Code workspace configuration",
|
|
504
|
+
},
|
|
505
|
+
patterns: ["mcp.json", "settings.json"],
|
|
506
|
+
});
|
|
507
|
+
// Cursor project
|
|
508
|
+
paths.push({
|
|
509
|
+
path: path.join(currentDir, ".cursor"),
|
|
510
|
+
source: {
|
|
511
|
+
tool: "Cursor",
|
|
512
|
+
type: "project",
|
|
513
|
+
priority: 9,
|
|
514
|
+
description: "Cursor project configuration",
|
|
515
|
+
},
|
|
516
|
+
patterns: ["mcp.json"],
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
// Common file locations
|
|
520
|
+
if (options.searchCommonPaths) {
|
|
521
|
+
paths.push({
|
|
522
|
+
path: currentDir,
|
|
523
|
+
source: {
|
|
524
|
+
tool: "Generic",
|
|
525
|
+
type: "project",
|
|
526
|
+
priority: 6,
|
|
527
|
+
description: "Generic project configuration",
|
|
528
|
+
},
|
|
529
|
+
patterns: [
|
|
530
|
+
"mcp.json",
|
|
531
|
+
".mcp-config.json",
|
|
532
|
+
"mcp_config.json",
|
|
533
|
+
".mcp-servers.json",
|
|
534
|
+
],
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
return paths;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Search a specific path for MCP configurations
|
|
541
|
+
*/
|
|
542
|
+
async searchPath(searchConfig, maxDepth, followSymlinks, includeInactive) {
|
|
543
|
+
const servers = [];
|
|
544
|
+
const sources = [];
|
|
545
|
+
const errors = [];
|
|
546
|
+
let configFilesFound = 0;
|
|
547
|
+
try {
|
|
548
|
+
// Check if path exists
|
|
549
|
+
await fs.access(searchConfig.path);
|
|
550
|
+
autoDiscoveryLogger.debug(`Searching path: ${searchConfig.path}`);
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
// Path doesn't exist, skip silently
|
|
554
|
+
autoDiscoveryLogger.debug(`Path does not exist: ${searchConfig.path}`);
|
|
555
|
+
return { servers, sources, configFilesFound, errors };
|
|
556
|
+
}
|
|
557
|
+
// Search for each pattern
|
|
558
|
+
for (const pattern of searchConfig.patterns) {
|
|
559
|
+
const filePath = path.join(searchConfig.path, pattern);
|
|
560
|
+
try {
|
|
561
|
+
await fs.access(filePath);
|
|
562
|
+
autoDiscoveryLogger.debug(`Found config file: ${filePath}`);
|
|
563
|
+
// Read and parse the file with resilient JSON parsing
|
|
564
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
565
|
+
const parsedContent = this.parseJsonResilient(content, filePath);
|
|
566
|
+
configFilesFound++;
|
|
567
|
+
// Try to parse with available parsers
|
|
568
|
+
let parsed = false;
|
|
569
|
+
for (const parser of this.parsers) {
|
|
570
|
+
if (parser.canParse(filePath, parsedContent)) {
|
|
571
|
+
autoDiscoveryLogger.debug(`Using parser for ${searchConfig.source.tool}: ${parser.constructor.name}`);
|
|
572
|
+
const parsedServers = parser.parse(filePath, parsedContent, searchConfig.source);
|
|
573
|
+
autoDiscoveryLogger.debug(`Parsed ${parsedServers.length} servers from ${filePath}`);
|
|
574
|
+
servers.push(...parsedServers);
|
|
575
|
+
sources.push(searchConfig.source);
|
|
576
|
+
parsed = true;
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (!parsed) {
|
|
581
|
+
autoDiscoveryLogger.debug(`No parser could handle ${filePath}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
catch (error) {
|
|
585
|
+
if (error.code !== "ENOENT") {
|
|
586
|
+
// File exists but couldn't be parsed
|
|
587
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
588
|
+
errors.push(`Error parsing ${filePath}: ${errorMessage}`);
|
|
589
|
+
autoDiscoveryLogger.debug(`Error parsing ${filePath}: ${errorMessage}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return { servers, sources, configFilesFound, errors };
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Parse JSON with resilience to common syntax issues
|
|
597
|
+
*/
|
|
598
|
+
parseJsonResilient(content, filePath) {
|
|
599
|
+
try {
|
|
600
|
+
// First, try standard JSON parsing
|
|
601
|
+
return JSON.parse(content);
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
const originalError = error instanceof Error ? error.message : String(error);
|
|
605
|
+
try {
|
|
606
|
+
// Attempt to fix common JSON issues
|
|
607
|
+
let fixedContent = content;
|
|
608
|
+
// Remove trailing commas before closing braces and brackets
|
|
609
|
+
fixedContent = fixedContent.replace(/,(\s*[}\]])/g, "$1");
|
|
610
|
+
// Remove single-line comments (// comments)
|
|
611
|
+
fixedContent = fixedContent.replace(/\/\/.*$/gm, "");
|
|
612
|
+
// Remove multi-line comments (/* comments */)
|
|
613
|
+
fixedContent = fixedContent.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
614
|
+
// Remove trailing commas after the last property in objects
|
|
615
|
+
fixedContent = fixedContent.replace(/,(\s*})/g, "$1");
|
|
616
|
+
// Remove trailing commas after the last element in arrays
|
|
617
|
+
fixedContent = fixedContent.replace(/,(\s*])/g, "$1");
|
|
618
|
+
// Fix unescaped control characters in strings
|
|
619
|
+
fixedContent = fixedContent.replace(/("(?:[^"\\]|\\.)+")/g, (match) => {
|
|
620
|
+
try {
|
|
621
|
+
// Try to parse the string to see if it's valid
|
|
622
|
+
JSON.parse(`{${match}}`);
|
|
623
|
+
return match;
|
|
624
|
+
}
|
|
625
|
+
catch {
|
|
626
|
+
// If invalid, escape common control characters
|
|
627
|
+
return match
|
|
628
|
+
.replace(/\n/g, "\\n")
|
|
629
|
+
.replace(/\r/g, "\\r")
|
|
630
|
+
.replace(/\t/g, "\\t")
|
|
631
|
+
.replace(/\f/g, "\\f")
|
|
632
|
+
.replace(/\b/g, "\\b");
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
// Fix unquoted object keys
|
|
636
|
+
fixedContent = fixedContent.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":');
|
|
637
|
+
// Try parsing the fixed content
|
|
638
|
+
const result = JSON.parse(fixedContent);
|
|
639
|
+
autoDiscoveryLogger.debug(`Successfully repaired JSON syntax issues in ${filePath}`);
|
|
640
|
+
return result;
|
|
641
|
+
}
|
|
642
|
+
catch (secondError) {
|
|
643
|
+
// If we still can't parse it, try one more time with aggressive fixes
|
|
644
|
+
try {
|
|
645
|
+
let aggressiveFixedContent = content;
|
|
646
|
+
// Remove any non-printable characters except standard whitespace
|
|
647
|
+
aggressiveFixedContent = aggressiveFixedContent.replace(
|
|
648
|
+
// eslint-disable-next-line no-control-regex
|
|
649
|
+
/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "");
|
|
650
|
+
// Apply all previous fixes
|
|
651
|
+
aggressiveFixedContent = aggressiveFixedContent.replace(/,(\s*[}\]])/g, "$1");
|
|
652
|
+
aggressiveFixedContent = aggressiveFixedContent.replace(/\/\/.*$/gm, "");
|
|
653
|
+
aggressiveFixedContent = aggressiveFixedContent.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
654
|
+
aggressiveFixedContent = aggressiveFixedContent.replace(/,(\s*})/g, "$1");
|
|
655
|
+
aggressiveFixedContent = aggressiveFixedContent.replace(/,(\s*])/g, "$1");
|
|
656
|
+
const result = JSON.parse(aggressiveFixedContent);
|
|
657
|
+
autoDiscoveryLogger.debug(`Successfully repaired JSON with aggressive fixes in ${filePath}`);
|
|
658
|
+
return result;
|
|
659
|
+
}
|
|
660
|
+
catch (thirdError) {
|
|
661
|
+
// If all attempts fail, throw a comprehensive error but don't crash the discovery
|
|
662
|
+
const secondErrorMessage = secondError instanceof Error
|
|
663
|
+
? secondError.message
|
|
664
|
+
: String(secondError);
|
|
665
|
+
const thirdErrorMessage = thirdError instanceof Error
|
|
666
|
+
? thirdError.message
|
|
667
|
+
: String(thirdError);
|
|
668
|
+
autoDiscoveryLogger.warn(`Unable to repair JSON in ${filePath}. ` +
|
|
669
|
+
`Original: ${originalError}. After basic repair: ${secondErrorMessage}. ` +
|
|
670
|
+
`After aggressive repair: ${thirdErrorMessage}`);
|
|
671
|
+
// Return empty object so discovery can continue
|
|
672
|
+
return {};
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Remove duplicate servers and apply prioritization
|
|
679
|
+
*/
|
|
680
|
+
deduplicateServers(servers, preferredTools) {
|
|
681
|
+
const serverMap = new Map();
|
|
682
|
+
// Sort by priority (higher priority first)
|
|
683
|
+
const sortedServers = servers.sort((a, b) => {
|
|
684
|
+
// Preferred tools get highest priority
|
|
685
|
+
const aPreferred = preferredTools.includes(a.source.tool);
|
|
686
|
+
const bPreferred = preferredTools.includes(b.source.tool);
|
|
687
|
+
if (aPreferred && !bPreferred) {
|
|
688
|
+
return -1;
|
|
689
|
+
}
|
|
690
|
+
if (!aPreferred && bPreferred) {
|
|
691
|
+
return 1;
|
|
692
|
+
}
|
|
693
|
+
// Then by source priority
|
|
694
|
+
return b.source.priority - a.source.priority;
|
|
695
|
+
});
|
|
696
|
+
// Keep the highest priority version of each server
|
|
697
|
+
for (const server of sortedServers) {
|
|
698
|
+
const key = server.id;
|
|
699
|
+
if (!serverMap.has(key)) {
|
|
700
|
+
serverMap.set(key, server);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return Array.from(serverMap.values());
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Map tool names to valid MCP server categories
|
|
707
|
+
*/
|
|
708
|
+
mapToolToCategory(toolName) {
|
|
709
|
+
const normalizedTool = toolName.toLowerCase().replace(/\s+/g, "-");
|
|
710
|
+
// Map known tools to appropriate categories
|
|
711
|
+
const categoryMap = {
|
|
712
|
+
"claude-desktop": "ai-providers",
|
|
713
|
+
"vs-code": "development",
|
|
714
|
+
cursor: "development",
|
|
715
|
+
windsurf: "development",
|
|
716
|
+
"roo-code": "development",
|
|
717
|
+
generic: "integrations",
|
|
718
|
+
};
|
|
719
|
+
return categoryMap[normalizedTool] || "custom";
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Convert discovered server to NeuroLink MCP server format
|
|
723
|
+
*/
|
|
724
|
+
async convertToMCPServer(server) {
|
|
725
|
+
// This is a simplified conversion
|
|
726
|
+
// In a real implementation, you might want to actually spawn the server
|
|
727
|
+
// and discover its tools via MCP protocol
|
|
728
|
+
const mcpServer = {
|
|
729
|
+
id: server.id,
|
|
730
|
+
title: server.title,
|
|
731
|
+
category: this.mapToolToCategory(server.source.tool),
|
|
732
|
+
tools: {
|
|
733
|
+
// Placeholder - would be populated by actual MCP discovery
|
|
734
|
+
[`${server.id}-placeholder`]: {
|
|
735
|
+
name: `${server.id}-placeholder`,
|
|
736
|
+
description: `Placeholder tool for discovered server ${server.id}`,
|
|
737
|
+
category: "discovered",
|
|
738
|
+
isImplemented: false,
|
|
739
|
+
execute: async (params, context) => {
|
|
740
|
+
return {
|
|
741
|
+
success: false,
|
|
742
|
+
error: "This is a placeholder tool. Actual server discovery not yet implemented.",
|
|
743
|
+
};
|
|
744
|
+
},
|
|
745
|
+
},
|
|
746
|
+
},
|
|
747
|
+
registerTool(tool) {
|
|
748
|
+
// Check for duplicate tool names
|
|
749
|
+
if (this.tools[tool.name]) {
|
|
750
|
+
throw new Error(`Tool '${tool.name}' already exists in discovered server '${this.id}'`);
|
|
751
|
+
}
|
|
752
|
+
// Register the tool
|
|
753
|
+
this.tools[tool.name] = {
|
|
754
|
+
...tool,
|
|
755
|
+
// Add server metadata to tool
|
|
756
|
+
metadata: {
|
|
757
|
+
...tool.metadata,
|
|
758
|
+
serverId: this.id,
|
|
759
|
+
serverCategory: this.category,
|
|
760
|
+
registeredAt: Date.now(),
|
|
761
|
+
},
|
|
762
|
+
};
|
|
763
|
+
return this;
|
|
764
|
+
},
|
|
765
|
+
metadata: {
|
|
766
|
+
source: server.source.tool,
|
|
767
|
+
configPath: server.configPath,
|
|
768
|
+
command: server.command,
|
|
769
|
+
args: server.args,
|
|
770
|
+
env: server.env,
|
|
771
|
+
cwd: server.cwd,
|
|
772
|
+
discoveredAt: Date.now(),
|
|
773
|
+
},
|
|
774
|
+
};
|
|
775
|
+
return mcpServer;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Default auto-discovery instance
|
|
780
|
+
*/
|
|
781
|
+
export const defaultAutoDiscovery = new MCPAutoDiscovery();
|
|
782
|
+
/**
|
|
783
|
+
* Utility function to discover servers with default instance
|
|
784
|
+
*/
|
|
785
|
+
export async function discoverMCPServers(options) {
|
|
786
|
+
return defaultAutoDiscovery.discoverServers(options);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Utility function to auto-register discovered servers
|
|
790
|
+
*/
|
|
791
|
+
export async function autoRegisterMCPServers(registry, options) {
|
|
792
|
+
return defaultAutoDiscovery.autoRegisterServers(registry, options);
|
|
793
|
+
}
|