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