@juspay/neurolink 1.5.3 ā 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 +241 -1
- package/README.md +113 -20
- 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 +76 -9
- package/dist/cli/commands/config.js +358 -233
- package/dist/cli/commands/mcp.d.ts +2 -1
- package/dist/cli/commands/mcp.js +874 -146
- package/dist/cli/commands/ollama.d.ts +8 -0
- package/dist/cli/commands/ollama.js +333 -0
- package/dist/cli/index.js +591 -327
- 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 +180 -50
- package/dist/core/types.d.ts +8 -3
- package/dist/core/types.js +7 -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 +180 -50
- package/dist/lib/core/types.d.ts +8 -3
- package/dist/lib/core/types.js +7 -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 +205 -66
- package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +143 -99
- 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 +404 -251
- 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 +314 -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 +31 -0
- package/dist/lib/providers/huggingFace.js +362 -0
- package/dist/lib/providers/index.d.ts +14 -8
- package/dist/lib/providers/index.js +18 -12
- 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 +32 -0
- package/dist/lib/providers/mistralAI.js +223 -0
- package/dist/lib/providers/ollama.d.ts +51 -0
- package/dist/lib/providers/ollama.js +508 -0
- 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 +59 -22
- 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 +205 -66
- package/dist/mcp/servers/ai-providers/ai-core-server.js +143 -99
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +6 -6
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +404 -253
- 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 +314 -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 +31 -0
- package/dist/providers/huggingFace.js +362 -0
- package/dist/providers/index.d.ts +14 -8
- package/dist/providers/index.js +18 -12
- package/dist/providers/mcp-provider.d.ts +62 -0
- package/dist/providers/mcp-provider.js +183 -0
- package/dist/providers/mistralAI.d.ts +32 -0
- package/dist/providers/mistralAI.js +223 -0
- package/dist/providers/ollama.d.ts +51 -0
- package/dist/providers/ollama.js +508 -0
- 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 +59 -22
- package/package.json +28 -4
package/dist/cli/commands/mcp.js
CHANGED
|
@@ -3,20 +3,25 @@
|
|
|
3
3
|
* MCP Server Management Commands
|
|
4
4
|
* Real MCP server connectivity and management
|
|
5
5
|
*/
|
|
6
|
-
import ora from
|
|
7
|
-
import chalk from
|
|
8
|
-
import fs from
|
|
9
|
-
import { spawn } from
|
|
10
|
-
import path from
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { discoverMCPServers } from "../../lib/mcp/auto-discovery.js";
|
|
12
|
+
import { defaultUnifiedRegistry, } from "../../lib/mcp/unified-registry.js";
|
|
13
|
+
import { ContextManager } from "../../lib/mcp/context-manager.js";
|
|
14
|
+
import { initializeNeuroLinkMCP } from "../../lib/mcp/initialize.js";
|
|
15
|
+
import { mcpLogger, setGlobalMCPLogLevel, LogLevel, } from "../../lib/mcp/logging.js";
|
|
11
16
|
// Default MCP config file location
|
|
12
|
-
const MCP_CONFIG_FILE = path.join(process.cwd(),
|
|
17
|
+
const MCP_CONFIG_FILE = path.join(process.cwd(), ".mcp-config.json");
|
|
13
18
|
// Load MCP configuration
|
|
14
19
|
function loadMCPConfig() {
|
|
15
20
|
if (!fs.existsSync(MCP_CONFIG_FILE)) {
|
|
16
21
|
return { mcpServers: {} };
|
|
17
22
|
}
|
|
18
23
|
try {
|
|
19
|
-
const content = fs.readFileSync(MCP_CONFIG_FILE,
|
|
24
|
+
const content = fs.readFileSync(MCP_CONFIG_FILE, "utf-8");
|
|
20
25
|
return JSON.parse(content);
|
|
21
26
|
}
|
|
22
27
|
catch (error) {
|
|
@@ -30,33 +35,33 @@ function saveMCPConfig(config) {
|
|
|
30
35
|
// Check if MCP server process is running
|
|
31
36
|
async function checkMCPServerStatus(serverConfig) {
|
|
32
37
|
try {
|
|
33
|
-
if (serverConfig.transport ===
|
|
38
|
+
if (serverConfig.transport === "stdio") {
|
|
34
39
|
// For stdio servers, we need to actually try connecting
|
|
35
40
|
const child = spawn(serverConfig.command, serverConfig.args || [], {
|
|
36
|
-
stdio: [
|
|
41
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
37
42
|
env: { ...process.env, ...serverConfig.env },
|
|
38
|
-
cwd: serverConfig.cwd
|
|
43
|
+
cwd: serverConfig.cwd,
|
|
39
44
|
});
|
|
40
45
|
return new Promise((resolve) => {
|
|
41
46
|
const timeout = setTimeout(() => {
|
|
42
47
|
child.kill();
|
|
43
48
|
resolve(false);
|
|
44
49
|
}, 3000);
|
|
45
|
-
child.on(
|
|
50
|
+
child.on("spawn", () => {
|
|
46
51
|
clearTimeout(timeout);
|
|
47
52
|
child.kill();
|
|
48
53
|
resolve(true);
|
|
49
54
|
});
|
|
50
|
-
child.on(
|
|
55
|
+
child.on("error", () => {
|
|
51
56
|
clearTimeout(timeout);
|
|
52
57
|
resolve(false);
|
|
53
58
|
});
|
|
54
59
|
});
|
|
55
60
|
}
|
|
56
|
-
else if (serverConfig.transport ===
|
|
61
|
+
else if (serverConfig.transport === "sse" && serverConfig.url) {
|
|
57
62
|
// For SSE servers, check if URL is accessible
|
|
58
63
|
try {
|
|
59
|
-
const response = await fetch(serverConfig.url, { method:
|
|
64
|
+
const response = await fetch(serverConfig.url, { method: "HEAD" });
|
|
60
65
|
return response.ok;
|
|
61
66
|
}
|
|
62
67
|
catch {
|
|
@@ -71,24 +76,24 @@ async function checkMCPServerStatus(serverConfig) {
|
|
|
71
76
|
}
|
|
72
77
|
// Connect to MCP server and get capabilities
|
|
73
78
|
async function getMCPServerCapabilities(serverConfig) {
|
|
74
|
-
if (serverConfig.transport ===
|
|
79
|
+
if (serverConfig.transport === "stdio") {
|
|
75
80
|
// Spawn MCP server and send initialize request
|
|
76
81
|
const child = spawn(serverConfig.command, serverConfig.args || [], {
|
|
77
|
-
stdio: [
|
|
82
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
78
83
|
env: { ...process.env, ...serverConfig.env },
|
|
79
|
-
cwd: serverConfig.cwd
|
|
84
|
+
cwd: serverConfig.cwd,
|
|
80
85
|
});
|
|
81
86
|
return new Promise((resolve, reject) => {
|
|
82
87
|
const timeout = setTimeout(() => {
|
|
83
88
|
child.kill();
|
|
84
|
-
reject(new Error(
|
|
89
|
+
reject(new Error("Timeout connecting to MCP server"));
|
|
85
90
|
}, 5000);
|
|
86
|
-
let responseData =
|
|
87
|
-
child.stdout?.on(
|
|
91
|
+
let responseData = "";
|
|
92
|
+
child.stdout?.on("data", (data) => {
|
|
88
93
|
responseData += data.toString();
|
|
89
94
|
// Look for JSON-RPC response
|
|
90
95
|
try {
|
|
91
|
-
const lines = responseData.split(
|
|
96
|
+
const lines = responseData.split("\n");
|
|
92
97
|
for (const line of lines) {
|
|
93
98
|
if (line.trim() && line.includes('"result"')) {
|
|
94
99
|
const response = JSON.parse(line.trim());
|
|
@@ -105,50 +110,50 @@ async function getMCPServerCapabilities(serverConfig) {
|
|
|
105
110
|
// Continue parsing
|
|
106
111
|
}
|
|
107
112
|
});
|
|
108
|
-
child.on(
|
|
113
|
+
child.on("spawn", () => {
|
|
109
114
|
// Send initialize request
|
|
110
115
|
const initRequest = {
|
|
111
|
-
jsonrpc:
|
|
116
|
+
jsonrpc: "2.0",
|
|
112
117
|
id: 1,
|
|
113
|
-
method:
|
|
118
|
+
method: "initialize",
|
|
114
119
|
params: {
|
|
115
|
-
protocolVersion:
|
|
120
|
+
protocolVersion: "2024-11-05",
|
|
116
121
|
capabilities: {},
|
|
117
122
|
clientInfo: {
|
|
118
|
-
name:
|
|
119
|
-
version:
|
|
120
|
-
}
|
|
121
|
-
}
|
|
123
|
+
name: "neurolink-cli",
|
|
124
|
+
version: "1.0.0",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
122
127
|
};
|
|
123
|
-
child.stdin?.write(JSON.stringify(initRequest) +
|
|
128
|
+
child.stdin?.write(JSON.stringify(initRequest) + "\n");
|
|
124
129
|
});
|
|
125
|
-
child.on(
|
|
130
|
+
child.on("error", (error) => {
|
|
126
131
|
clearTimeout(timeout);
|
|
127
132
|
reject(error);
|
|
128
133
|
});
|
|
129
134
|
});
|
|
130
135
|
}
|
|
131
|
-
throw new Error(
|
|
136
|
+
throw new Error("SSE transport not yet implemented for capabilities");
|
|
132
137
|
}
|
|
133
138
|
// List available tools from MCP server
|
|
134
139
|
async function listMCPServerTools(serverConfig) {
|
|
135
|
-
if (serverConfig.transport ===
|
|
140
|
+
if (serverConfig.transport === "stdio") {
|
|
136
141
|
const child = spawn(serverConfig.command, serverConfig.args || [], {
|
|
137
|
-
stdio: [
|
|
142
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
138
143
|
env: { ...process.env, ...serverConfig.env },
|
|
139
|
-
cwd: serverConfig.cwd
|
|
144
|
+
cwd: serverConfig.cwd,
|
|
140
145
|
});
|
|
141
146
|
return new Promise((resolve, reject) => {
|
|
142
147
|
const timeout = setTimeout(() => {
|
|
143
148
|
child.kill();
|
|
144
|
-
reject(new Error(
|
|
149
|
+
reject(new Error("Timeout listing MCP server tools"));
|
|
145
150
|
}, 5000);
|
|
146
|
-
let responseData =
|
|
151
|
+
let responseData = "";
|
|
147
152
|
let initialized = false;
|
|
148
|
-
child.stdout?.on(
|
|
153
|
+
child.stdout?.on("data", (data) => {
|
|
149
154
|
responseData += data.toString();
|
|
150
155
|
try {
|
|
151
|
-
const lines = responseData.split(
|
|
156
|
+
const lines = responseData.split("\n");
|
|
152
157
|
for (const line of lines) {
|
|
153
158
|
if (line.trim() && line.includes('"result"')) {
|
|
154
159
|
const response = JSON.parse(line.trim());
|
|
@@ -156,12 +161,12 @@ async function listMCPServerTools(serverConfig) {
|
|
|
156
161
|
// Initialize successful, now list tools
|
|
157
162
|
initialized = true;
|
|
158
163
|
const listToolsRequest = {
|
|
159
|
-
jsonrpc:
|
|
164
|
+
jsonrpc: "2.0",
|
|
160
165
|
id: 2,
|
|
161
|
-
method:
|
|
162
|
-
params: {}
|
|
166
|
+
method: "tools/list",
|
|
167
|
+
params: {},
|
|
163
168
|
};
|
|
164
|
-
child.stdin?.write(JSON.stringify(listToolsRequest) +
|
|
169
|
+
child.stdin?.write(JSON.stringify(listToolsRequest) + "\n");
|
|
165
170
|
}
|
|
166
171
|
else if (response.id === 2 && response.result.tools) {
|
|
167
172
|
clearTimeout(timeout);
|
|
@@ -176,84 +181,377 @@ async function listMCPServerTools(serverConfig) {
|
|
|
176
181
|
// Continue parsing
|
|
177
182
|
}
|
|
178
183
|
});
|
|
179
|
-
child.on(
|
|
184
|
+
child.on("spawn", () => {
|
|
180
185
|
// Send initialize request first
|
|
181
186
|
const initRequest = {
|
|
182
|
-
jsonrpc:
|
|
187
|
+
jsonrpc: "2.0",
|
|
183
188
|
id: 1,
|
|
184
|
-
method:
|
|
189
|
+
method: "initialize",
|
|
185
190
|
params: {
|
|
186
|
-
protocolVersion:
|
|
191
|
+
protocolVersion: "2024-11-05",
|
|
187
192
|
capabilities: {},
|
|
188
193
|
clientInfo: {
|
|
189
|
-
name:
|
|
190
|
-
version:
|
|
194
|
+
name: "neurolink-cli",
|
|
195
|
+
version: "1.0.0",
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
child.stdin?.write(JSON.stringify(initRequest) + "\n");
|
|
200
|
+
});
|
|
201
|
+
child.on("error", (error) => {
|
|
202
|
+
clearTimeout(timeout);
|
|
203
|
+
reject(error);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
throw new Error("SSE transport not yet implemented for tool listing");
|
|
208
|
+
}
|
|
209
|
+
// Execute tool on MCP server
|
|
210
|
+
async function executeMCPTool(serverConfig, toolName, toolParams) {
|
|
211
|
+
if (serverConfig.transport === "stdio") {
|
|
212
|
+
const child = spawn(serverConfig.command, serverConfig.args || [], {
|
|
213
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
214
|
+
env: { ...process.env, ...serverConfig.env },
|
|
215
|
+
cwd: serverConfig.cwd,
|
|
216
|
+
});
|
|
217
|
+
return new Promise((resolve, reject) => {
|
|
218
|
+
const timeout = setTimeout(() => {
|
|
219
|
+
child.kill();
|
|
220
|
+
reject(new Error("Timeout executing MCP tool"));
|
|
221
|
+
}, 10000); // Longer timeout for tool execution
|
|
222
|
+
let responseData = "";
|
|
223
|
+
let initialized = false;
|
|
224
|
+
child.stdout?.on("data", (data) => {
|
|
225
|
+
responseData += data.toString();
|
|
226
|
+
try {
|
|
227
|
+
const lines = responseData.split("\n");
|
|
228
|
+
for (const line of lines) {
|
|
229
|
+
if (line.trim() && line.includes('"result"')) {
|
|
230
|
+
const response = JSON.parse(line.trim());
|
|
231
|
+
if (response.id === 1 && response.result.capabilities) {
|
|
232
|
+
// Initialize successful, now execute tool
|
|
233
|
+
initialized = true;
|
|
234
|
+
const toolCallRequest = {
|
|
235
|
+
jsonrpc: "2.0",
|
|
236
|
+
id: 2,
|
|
237
|
+
method: "tools/call",
|
|
238
|
+
params: {
|
|
239
|
+
name: toolName,
|
|
240
|
+
arguments: toolParams,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
child.stdin?.write(JSON.stringify(toolCallRequest) + "\n");
|
|
244
|
+
}
|
|
245
|
+
else if (response.id === 2) {
|
|
246
|
+
clearTimeout(timeout);
|
|
247
|
+
child.kill();
|
|
248
|
+
if (response.result) {
|
|
249
|
+
resolve(response.result);
|
|
250
|
+
}
|
|
251
|
+
else if (response.error) {
|
|
252
|
+
reject(new Error(`MCP Error: ${response.error.message || "Unknown error"}`));
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
reject(new Error("Unknown MCP response format"));
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (line.trim() && line.includes('"error"')) {
|
|
261
|
+
const response = JSON.parse(line.trim());
|
|
262
|
+
if (response.error) {
|
|
263
|
+
clearTimeout(timeout);
|
|
264
|
+
child.kill();
|
|
265
|
+
reject(new Error(`MCP Error: ${response.error.message || "Unknown error"}`));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
191
268
|
}
|
|
192
269
|
}
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
// Continue parsing
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
child.stderr?.on("data", (data) => {
|
|
276
|
+
console.error(chalk.red(`MCP Server Error: ${data.toString()}`));
|
|
277
|
+
});
|
|
278
|
+
child.on("spawn", () => {
|
|
279
|
+
// Send initialize request first
|
|
280
|
+
const initRequest = {
|
|
281
|
+
jsonrpc: "2.0",
|
|
282
|
+
id: 1,
|
|
283
|
+
method: "initialize",
|
|
284
|
+
params: {
|
|
285
|
+
protocolVersion: "2024-11-05",
|
|
286
|
+
capabilities: {},
|
|
287
|
+
clientInfo: {
|
|
288
|
+
name: "neurolink-cli",
|
|
289
|
+
version: "1.0.0",
|
|
290
|
+
},
|
|
291
|
+
},
|
|
193
292
|
};
|
|
194
|
-
child.stdin?.write(JSON.stringify(initRequest) +
|
|
293
|
+
child.stdin?.write(JSON.stringify(initRequest) + "\n");
|
|
195
294
|
});
|
|
196
|
-
child.on(
|
|
295
|
+
child.on("error", (error) => {
|
|
197
296
|
clearTimeout(timeout);
|
|
198
297
|
reject(error);
|
|
199
298
|
});
|
|
200
299
|
});
|
|
201
300
|
}
|
|
202
|
-
throw new Error(
|
|
301
|
+
throw new Error("SSE transport not yet implemented for tool execution");
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Display discovery results in table format
|
|
305
|
+
*/
|
|
306
|
+
function displayTable(discoveryResult) {
|
|
307
|
+
console.log(chalk.green(`\nš Found ${discoveryResult.discovered.length} MCP servers:`));
|
|
308
|
+
console.log(chalk.gray("ā".repeat(80)));
|
|
309
|
+
discoveryResult.discovered.forEach((server, index) => {
|
|
310
|
+
const sourceIcon = getSourceIcon(server.source.tool);
|
|
311
|
+
const typeColor = server.source.type === "global"
|
|
312
|
+
? chalk.blue
|
|
313
|
+
: server.source.type === "workspace"
|
|
314
|
+
? chalk.green
|
|
315
|
+
: chalk.gray;
|
|
316
|
+
console.log(`${chalk.white(`${index + 1}.`)} ${sourceIcon} ${chalk.cyan(server.id)}`);
|
|
317
|
+
console.log(` ${chalk.gray("Title:")} ${server.title}`);
|
|
318
|
+
console.log(` ${chalk.gray("Source:")} ${server.source.tool} ${typeColor(`(${server.source.type})`)}`);
|
|
319
|
+
console.log(` ${chalk.gray("Command:")} ${server.command} ${server.args?.join(" ") || ""}`);
|
|
320
|
+
console.log(` ${chalk.gray("Config:")} ${server.configPath}`);
|
|
321
|
+
if (server.env && Object.keys(server.env).length > 0) {
|
|
322
|
+
console.log(` ${chalk.gray("Environment:")} ${Object.keys(server.env).length} variable(s)`);
|
|
323
|
+
}
|
|
324
|
+
console.log();
|
|
325
|
+
});
|
|
326
|
+
// Display statistics
|
|
327
|
+
console.log(chalk.blue("š Discovery Statistics:"));
|
|
328
|
+
console.log(` ${chalk.gray("Execution time:")} ${discoveryResult.stats.executionTime}ms`);
|
|
329
|
+
console.log(` ${chalk.gray("Config files found:")} ${discoveryResult.stats.configFilesFound}`);
|
|
330
|
+
console.log(` ${chalk.gray("Servers discovered:")} ${discoveryResult.stats.serversDiscovered}`);
|
|
331
|
+
console.log(` ${chalk.gray("Duplicates removed:")} ${discoveryResult.stats.duplicatesRemoved}`);
|
|
332
|
+
// Display sources
|
|
333
|
+
if (discoveryResult.sources.length > 0) {
|
|
334
|
+
console.log(chalk.blue("\nšÆ Search Sources:"));
|
|
335
|
+
const sourcesByTool = discoveryResult.sources.reduce((acc, source) => {
|
|
336
|
+
acc[source.tool] = (acc[source.tool] || 0) + 1;
|
|
337
|
+
return acc;
|
|
338
|
+
}, {});
|
|
339
|
+
Object.entries(sourcesByTool).forEach(([tool, count]) => {
|
|
340
|
+
const icon = getSourceIcon(tool);
|
|
341
|
+
console.log(` ${icon} ${tool}: ${count} location(s)`);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Display discovery results in summary format
|
|
347
|
+
*/
|
|
348
|
+
function displaySummary(discoveryResult) {
|
|
349
|
+
console.log(chalk.green(`\nš Discovery Summary`));
|
|
350
|
+
console.log(chalk.gray("==================="));
|
|
351
|
+
console.log(`${chalk.cyan("Total servers found:")} ${discoveryResult.discovered.length}`);
|
|
352
|
+
console.log(`${chalk.cyan("Execution time:")} ${discoveryResult.stats.executionTime}ms`);
|
|
353
|
+
console.log(`${chalk.cyan("Config files found:")} ${discoveryResult.stats.configFilesFound}`);
|
|
354
|
+
console.log(`${chalk.cyan("Duplicates removed:")} ${discoveryResult.stats.duplicatesRemoved}`);
|
|
355
|
+
// Group by source tool
|
|
356
|
+
const serversByTool = discoveryResult.discovered.reduce((acc, server) => {
|
|
357
|
+
const tool = server.source.tool;
|
|
358
|
+
acc[tool] = (acc[tool] || 0) + 1;
|
|
359
|
+
return acc;
|
|
360
|
+
}, {});
|
|
361
|
+
if (Object.keys(serversByTool).length > 0) {
|
|
362
|
+
console.log(chalk.blue("\nš§ Servers by Tool:"));
|
|
363
|
+
Object.entries(serversByTool).forEach(([tool, count]) => {
|
|
364
|
+
const icon = getSourceIcon(tool);
|
|
365
|
+
console.log(` ${icon} ${tool}: ${count} server(s)`);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
// Group by type
|
|
369
|
+
const serversByType = discoveryResult.discovered.reduce((acc, server) => {
|
|
370
|
+
const type = server.source.type;
|
|
371
|
+
acc[type] = (acc[type] || 0) + 1;
|
|
372
|
+
return acc;
|
|
373
|
+
}, {});
|
|
374
|
+
if (Object.keys(serversByType).length > 0) {
|
|
375
|
+
console.log(chalk.blue("\nš Servers by Type:"));
|
|
376
|
+
Object.entries(serversByType).forEach(([type, count]) => {
|
|
377
|
+
const typeColor = type === "global"
|
|
378
|
+
? chalk.blue
|
|
379
|
+
: type === "workspace"
|
|
380
|
+
? chalk.green
|
|
381
|
+
: chalk.gray;
|
|
382
|
+
console.log(` ${typeColor(`${type}:`)} ${count} server(s)`);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Get icon for source tool
|
|
388
|
+
*/
|
|
389
|
+
function getSourceIcon(tool) {
|
|
390
|
+
const icons = {
|
|
391
|
+
"Claude Desktop": "š¤",
|
|
392
|
+
"VS Code": "š",
|
|
393
|
+
Cursor: "š±ļø",
|
|
394
|
+
Windsurf: "š",
|
|
395
|
+
"Roo Code": "š¦",
|
|
396
|
+
Generic: "āļø",
|
|
397
|
+
"Cline AI Coder": "š§",
|
|
398
|
+
"Continue Dev": "š",
|
|
399
|
+
Aider: "š ļø",
|
|
400
|
+
};
|
|
401
|
+
return icons[tool] || "š§";
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Get icon for source type
|
|
405
|
+
*/
|
|
406
|
+
function getSourceTypeIcon(sourceType) {
|
|
407
|
+
const icons = {
|
|
408
|
+
manual: "š",
|
|
409
|
+
auto: "š",
|
|
410
|
+
default: "āļø",
|
|
411
|
+
};
|
|
412
|
+
return icons[sourceType] || "ā";
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Get icon for server status
|
|
416
|
+
*/
|
|
417
|
+
function getStatusIcon(status) {
|
|
418
|
+
const icons = {
|
|
419
|
+
available: "ā
",
|
|
420
|
+
unavailable: "ā",
|
|
421
|
+
unknown: "ā",
|
|
422
|
+
};
|
|
423
|
+
return icons[status] || "ā";
|
|
424
|
+
}
|
|
425
|
+
// Export the tool execution function for use in other parts of the CLI
|
|
426
|
+
export async function mcpExecuteTool(serverName, toolName, toolParams) {
|
|
427
|
+
// First try unified registry (includes built-in NeuroLink servers)
|
|
428
|
+
try {
|
|
429
|
+
await defaultUnifiedRegistry.initialize();
|
|
430
|
+
const contextManager = new ContextManager();
|
|
431
|
+
const context = contextManager.createContext({
|
|
432
|
+
sessionId: `cli-${Date.now()}`,
|
|
433
|
+
userId: "cli-user",
|
|
434
|
+
aiProvider: "unified-mcp",
|
|
435
|
+
});
|
|
436
|
+
const result = await defaultUnifiedRegistry.executeTool(toolName, toolParams, context, {
|
|
437
|
+
preferredSource: "default", // Try built-in servers first
|
|
438
|
+
fallbackEnabled: true,
|
|
439
|
+
validateBeforeExecution: true,
|
|
440
|
+
timeoutMs: 30000,
|
|
441
|
+
});
|
|
442
|
+
if (result.success) {
|
|
443
|
+
return result.data;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
mcpLogger.debug("[mcpExecuteTool] Unified registry failed, trying manual config:", {
|
|
448
|
+
error: error instanceof Error ? error.message : String(error),
|
|
449
|
+
serverName,
|
|
450
|
+
toolName: toolName,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
// Fallback to manual configuration (legacy behavior)
|
|
454
|
+
const config = loadMCPConfig();
|
|
455
|
+
const serverConfig = config.mcpServers[serverName];
|
|
456
|
+
if (!serverConfig) {
|
|
457
|
+
throw new Error(`MCP server '${serverName}' not found`);
|
|
458
|
+
}
|
|
459
|
+
const result = await executeMCPTool(serverConfig, toolName, toolParams);
|
|
460
|
+
mcpLogger.debug("[mcpExecuteTool] Tool executed via manual config:", {
|
|
461
|
+
serverName,
|
|
462
|
+
toolName,
|
|
463
|
+
hasResult: !!result,
|
|
464
|
+
resultType: typeof result,
|
|
465
|
+
});
|
|
466
|
+
// Extract the text content from MCP result format
|
|
467
|
+
if (result.content && Array.isArray(result.content)) {
|
|
468
|
+
const textContent = result.content.find((item) => item.type === "text");
|
|
469
|
+
if (textContent) {
|
|
470
|
+
return JSON.parse(textContent.text);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
203
474
|
}
|
|
204
475
|
// MCP Commands for yargs
|
|
205
476
|
export function addMCPCommands(yargs) {
|
|
206
|
-
return yargs.command(
|
|
477
|
+
return yargs.command("mcp <subcommand>", "Manage MCP (Model Context Protocol) servers", (yargsBuilder) => {
|
|
207
478
|
yargsBuilder
|
|
208
|
-
.usage(
|
|
479
|
+
.usage("Usage: $0 mcp <subcommand> [options]")
|
|
209
480
|
// List MCP servers
|
|
210
|
-
.command(
|
|
211
|
-
.usage(
|
|
212
|
-
.option(
|
|
213
|
-
|
|
214
|
-
|
|
481
|
+
.command("list", "List configured MCP servers", (y) => y
|
|
482
|
+
.usage("Usage: $0 mcp list [options]")
|
|
483
|
+
.option("status", {
|
|
484
|
+
type: "boolean",
|
|
485
|
+
description: "Check server status",
|
|
486
|
+
})
|
|
487
|
+
.example("$0 mcp list", "List all MCP servers")
|
|
488
|
+
.example("$0 mcp list --status", "List servers with status check"), async (argv) => {
|
|
215
489
|
const config = loadMCPConfig();
|
|
216
490
|
const servers = Object.entries(config.mcpServers);
|
|
217
491
|
if (servers.length === 0) {
|
|
218
|
-
console.log(chalk.yellow(
|
|
219
|
-
console.log(chalk.blue(
|
|
492
|
+
console.log(chalk.yellow("š No MCP servers configured"));
|
|
493
|
+
console.log(chalk.blue("š” Add a server with: neurolink mcp add <name> <command>"));
|
|
220
494
|
return;
|
|
221
495
|
}
|
|
222
496
|
console.log(chalk.blue(`š Configured MCP servers (${servers.length}):\n`));
|
|
223
497
|
for (const [name, serverConfig] of servers) {
|
|
224
498
|
console.log(chalk.bold(`š§ ${name}`));
|
|
225
|
-
console.log(` Command: ${serverConfig.command} ${(serverConfig.args || []).join(
|
|
499
|
+
console.log(` Command: ${serverConfig.command} ${(serverConfig.args || []).join(" ")}`);
|
|
226
500
|
console.log(` Transport: ${serverConfig.transport}`);
|
|
227
501
|
if (argv.status) {
|
|
228
502
|
const spinner = ora(`Checking ${name}...`).start();
|
|
229
503
|
try {
|
|
230
504
|
const isRunning = await checkMCPServerStatus(serverConfig);
|
|
231
505
|
if (isRunning) {
|
|
232
|
-
spinner.succeed(`${name}: ${chalk.green(
|
|
506
|
+
spinner.succeed(`${name}: ${chalk.green("ā
Available")}`);
|
|
233
507
|
}
|
|
234
508
|
else {
|
|
235
|
-
spinner.fail(`${name}: ${chalk.red(
|
|
509
|
+
spinner.fail(`${name}: ${chalk.red("ā Not available")}`);
|
|
236
510
|
}
|
|
237
511
|
}
|
|
238
512
|
catch (error) {
|
|
239
|
-
spinner.fail(`${name}: ${chalk.red(
|
|
513
|
+
spinner.fail(`${name}: ${chalk.red("ā Error")} - ${error.message}`);
|
|
240
514
|
}
|
|
241
515
|
}
|
|
242
516
|
console.log(); // Empty line
|
|
243
517
|
}
|
|
244
518
|
})
|
|
245
519
|
// Add MCP server
|
|
246
|
-
.command(
|
|
247
|
-
.usage(
|
|
248
|
-
.positional(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
520
|
+
.command("add <name> <command>", "Add a new MCP server", (y) => y
|
|
521
|
+
.usage("Usage: $0 mcp add <name> <command> [options]")
|
|
522
|
+
.positional("name", {
|
|
523
|
+
type: "string",
|
|
524
|
+
description: "Server name",
|
|
525
|
+
demandOption: true,
|
|
526
|
+
})
|
|
527
|
+
.positional("command", {
|
|
528
|
+
type: "string",
|
|
529
|
+
description: "Command to run server",
|
|
530
|
+
demandOption: true,
|
|
531
|
+
})
|
|
532
|
+
.option("args", {
|
|
533
|
+
type: "array",
|
|
534
|
+
description: "Command arguments",
|
|
535
|
+
})
|
|
536
|
+
.option("transport", {
|
|
537
|
+
choices: ["stdio", "sse"],
|
|
538
|
+
default: "stdio",
|
|
539
|
+
description: "Transport type",
|
|
540
|
+
})
|
|
541
|
+
.option("url", {
|
|
542
|
+
type: "string",
|
|
543
|
+
description: "URL for SSE transport",
|
|
544
|
+
})
|
|
545
|
+
.option("env", {
|
|
546
|
+
type: "string",
|
|
547
|
+
description: "Environment variables (JSON)",
|
|
548
|
+
})
|
|
549
|
+
.option("cwd", {
|
|
550
|
+
type: "string",
|
|
551
|
+
description: "Working directory",
|
|
552
|
+
})
|
|
553
|
+
.example('$0 mcp add filesystem "npx @modelcontextprotocol/server-filesystem"', "Add filesystem server")
|
|
554
|
+
.example('$0 mcp add github "npx @modelcontextprotocol/server-github"', "Add GitHub server"), async (argv) => {
|
|
257
555
|
const config = loadMCPConfig();
|
|
258
556
|
const serverConfig = {
|
|
259
557
|
name: argv.name,
|
|
@@ -261,14 +559,14 @@ export function addMCPCommands(yargs) {
|
|
|
261
559
|
args: argv.args || [],
|
|
262
560
|
transport: argv.transport,
|
|
263
561
|
url: argv.url,
|
|
264
|
-
cwd: argv.cwd
|
|
562
|
+
cwd: argv.cwd,
|
|
265
563
|
};
|
|
266
564
|
if (argv.env) {
|
|
267
565
|
try {
|
|
268
566
|
serverConfig.env = JSON.parse(argv.env);
|
|
269
567
|
}
|
|
270
568
|
catch (error) {
|
|
271
|
-
console.error(chalk.red(
|
|
569
|
+
console.error(chalk.red("ā Invalid JSON for environment variables"));
|
|
272
570
|
process.exit(1);
|
|
273
571
|
}
|
|
274
572
|
}
|
|
@@ -278,10 +576,14 @@ export function addMCPCommands(yargs) {
|
|
|
278
576
|
console.log(chalk.blue(`š” Test it with: neurolink mcp test ${argv.name}`));
|
|
279
577
|
})
|
|
280
578
|
// Remove MCP server
|
|
281
|
-
.command(
|
|
282
|
-
.usage(
|
|
283
|
-
.positional(
|
|
284
|
-
|
|
579
|
+
.command("remove <name>", "Remove an MCP server", (y) => y
|
|
580
|
+
.usage("Usage: $0 mcp remove <name>")
|
|
581
|
+
.positional("name", {
|
|
582
|
+
type: "string",
|
|
583
|
+
description: "Server name to remove",
|
|
584
|
+
demandOption: true,
|
|
585
|
+
})
|
|
586
|
+
.example("$0 mcp remove filesystem", "Remove filesystem server"), async (argv) => {
|
|
285
587
|
const config = loadMCPConfig();
|
|
286
588
|
if (!config.mcpServers[argv.name]) {
|
|
287
589
|
console.error(chalk.red(`ā MCP server '${argv.name}' not found`));
|
|
@@ -292,10 +594,14 @@ export function addMCPCommands(yargs) {
|
|
|
292
594
|
console.log(chalk.green(`ā
Removed MCP server: ${argv.name}`));
|
|
293
595
|
})
|
|
294
596
|
// Test MCP server
|
|
295
|
-
.command(
|
|
296
|
-
.usage(
|
|
297
|
-
.positional(
|
|
298
|
-
|
|
597
|
+
.command("test <name>", "Test connection to an MCP server", (y) => y
|
|
598
|
+
.usage("Usage: $0 mcp test <name>")
|
|
599
|
+
.positional("name", {
|
|
600
|
+
type: "string",
|
|
601
|
+
description: "Server name to test",
|
|
602
|
+
demandOption: true,
|
|
603
|
+
})
|
|
604
|
+
.example("$0 mcp test filesystem", "Test filesystem server"), async (argv) => {
|
|
299
605
|
const config = loadMCPConfig();
|
|
300
606
|
const serverConfig = config.mcpServers[argv.name];
|
|
301
607
|
if (!serverConfig) {
|
|
@@ -303,87 +609,93 @@ export function addMCPCommands(yargs) {
|
|
|
303
609
|
process.exit(1);
|
|
304
610
|
}
|
|
305
611
|
console.log(chalk.blue(`š Testing MCP server: ${argv.name}\n`));
|
|
306
|
-
const spinner = ora(
|
|
612
|
+
const spinner = ora("Connecting...").start();
|
|
307
613
|
try {
|
|
308
614
|
// Test basic connectivity
|
|
309
615
|
const isRunning = await checkMCPServerStatus(serverConfig);
|
|
310
616
|
if (!isRunning) {
|
|
311
|
-
spinner.fail(chalk.red(
|
|
617
|
+
spinner.fail(chalk.red("ā Server not available"));
|
|
312
618
|
return;
|
|
313
619
|
}
|
|
314
|
-
spinner.text =
|
|
620
|
+
spinner.text = "Getting capabilities...";
|
|
315
621
|
const capabilities = await getMCPServerCapabilities(serverConfig);
|
|
316
|
-
spinner.text =
|
|
622
|
+
spinner.text = "Listing tools...";
|
|
317
623
|
const tools = await listMCPServerTools(serverConfig);
|
|
318
|
-
spinner.succeed(chalk.green(
|
|
319
|
-
console.log(chalk.blue(
|
|
320
|
-
console.log(` Protocol Version: ${capabilities.protocolVersion ||
|
|
624
|
+
spinner.succeed(chalk.green("ā
Connection successful!"));
|
|
625
|
+
console.log(chalk.blue("\nš Server Capabilities:"));
|
|
626
|
+
console.log(` Protocol Version: ${capabilities.protocolVersion || "Unknown"}`);
|
|
321
627
|
if (capabilities.capabilities.tools) {
|
|
322
628
|
console.log(` Tools: ā
Supported`);
|
|
323
629
|
}
|
|
324
630
|
if (capabilities.capabilities.resources) {
|
|
325
631
|
console.log(` Resources: ā
Supported`);
|
|
326
632
|
}
|
|
327
|
-
console.log(chalk.blue(
|
|
633
|
+
console.log(chalk.blue("\nš ļø Available Tools:"));
|
|
328
634
|
if (tools.length === 0) {
|
|
329
|
-
console.log(
|
|
635
|
+
console.log(" No tools available");
|
|
330
636
|
}
|
|
331
637
|
else {
|
|
332
638
|
tools.forEach((tool) => {
|
|
333
|
-
console.log(` ⢠${tool.name}: ${tool.description ||
|
|
639
|
+
console.log(` ⢠${tool.name}: ${tool.description || "No description"}`);
|
|
334
640
|
});
|
|
335
641
|
}
|
|
336
642
|
}
|
|
337
643
|
catch (error) {
|
|
338
|
-
spinner.fail(chalk.red(
|
|
644
|
+
spinner.fail(chalk.red("ā Connection failed"));
|
|
339
645
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
340
646
|
}
|
|
341
647
|
})
|
|
342
648
|
// Install popular MCP servers
|
|
343
|
-
.command(
|
|
344
|
-
.usage(
|
|
345
|
-
.positional(
|
|
346
|
-
type:
|
|
347
|
-
choices: [
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
649
|
+
.command("install <server>", "Install popular MCP servers", (y) => y
|
|
650
|
+
.usage("Usage: $0 mcp install <server>")
|
|
651
|
+
.positional("server", {
|
|
652
|
+
type: "string",
|
|
653
|
+
choices: [
|
|
654
|
+
"filesystem",
|
|
655
|
+
"github",
|
|
656
|
+
"postgres",
|
|
657
|
+
"brave-search",
|
|
658
|
+
"puppeteer",
|
|
659
|
+
],
|
|
660
|
+
description: "Server to install",
|
|
661
|
+
demandOption: true,
|
|
662
|
+
})
|
|
663
|
+
.example("$0 mcp install filesystem", "Install filesystem server")
|
|
664
|
+
.example("$0 mcp install github", "Install GitHub server"), async (argv) => {
|
|
353
665
|
const serverName = argv.server;
|
|
354
666
|
const config = loadMCPConfig();
|
|
355
667
|
// Pre-configured popular MCP servers
|
|
356
668
|
const serverConfigs = {
|
|
357
669
|
filesystem: {
|
|
358
|
-
name:
|
|
359
|
-
command:
|
|
360
|
-
args: [
|
|
361
|
-
transport:
|
|
670
|
+
name: "filesystem",
|
|
671
|
+
command: "npx",
|
|
672
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "/"],
|
|
673
|
+
transport: "stdio",
|
|
362
674
|
},
|
|
363
675
|
github: {
|
|
364
|
-
name:
|
|
365
|
-
command:
|
|
366
|
-
args: [
|
|
367
|
-
transport:
|
|
676
|
+
name: "github",
|
|
677
|
+
command: "npx",
|
|
678
|
+
args: ["-y", "@modelcontextprotocol/server-github"],
|
|
679
|
+
transport: "stdio",
|
|
368
680
|
},
|
|
369
681
|
postgres: {
|
|
370
|
-
name:
|
|
371
|
-
command:
|
|
372
|
-
args: [
|
|
373
|
-
transport:
|
|
682
|
+
name: "postgres",
|
|
683
|
+
command: "npx",
|
|
684
|
+
args: ["-y", "@modelcontextprotocol/server-postgres"],
|
|
685
|
+
transport: "stdio",
|
|
374
686
|
},
|
|
375
|
-
|
|
376
|
-
name:
|
|
377
|
-
command:
|
|
378
|
-
args: [
|
|
379
|
-
transport:
|
|
687
|
+
"brave-search": {
|
|
688
|
+
name: "brave-search",
|
|
689
|
+
command: "npx",
|
|
690
|
+
args: ["-y", "@modelcontextprotocol/server-brave-search"],
|
|
691
|
+
transport: "stdio",
|
|
380
692
|
},
|
|
381
693
|
puppeteer: {
|
|
382
|
-
name:
|
|
383
|
-
command:
|
|
384
|
-
args: [
|
|
385
|
-
transport:
|
|
386
|
-
}
|
|
694
|
+
name: "puppeteer",
|
|
695
|
+
command: "npx",
|
|
696
|
+
args: ["-y", "@modelcontextprotocol/server-puppeteer"],
|
|
697
|
+
transport: "stdio",
|
|
698
|
+
},
|
|
387
699
|
};
|
|
388
700
|
const serverConfig = serverConfigs[serverName];
|
|
389
701
|
if (!serverConfig) {
|
|
@@ -397,12 +709,23 @@ export function addMCPCommands(yargs) {
|
|
|
397
709
|
console.log(chalk.blue(`š” Test it with: neurolink mcp test ${serverName}`));
|
|
398
710
|
})
|
|
399
711
|
// Execute tool from MCP server
|
|
400
|
-
.command(
|
|
401
|
-
.usage(
|
|
402
|
-
.positional(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
712
|
+
.command("exec <server> <tool>", "Execute a tool from an MCP server", (y) => y
|
|
713
|
+
.usage("Usage: $0 mcp exec <server> <tool> [options]")
|
|
714
|
+
.positional("server", {
|
|
715
|
+
type: "string",
|
|
716
|
+
description: "Server name",
|
|
717
|
+
demandOption: true,
|
|
718
|
+
})
|
|
719
|
+
.positional("tool", {
|
|
720
|
+
type: "string",
|
|
721
|
+
description: "Tool name",
|
|
722
|
+
demandOption: true,
|
|
723
|
+
})
|
|
724
|
+
.option("params", {
|
|
725
|
+
type: "string",
|
|
726
|
+
description: "Tool parameters (JSON)",
|
|
727
|
+
})
|
|
728
|
+
.example('$0 mcp exec filesystem read_file --params \'{"path": "README.md"}\'', "Read file using filesystem server"), async (argv) => {
|
|
406
729
|
const config = loadMCPConfig();
|
|
407
730
|
const serverConfig = config.mcpServers[argv.server];
|
|
408
731
|
if (!serverConfig) {
|
|
@@ -415,20 +738,425 @@ export function addMCPCommands(yargs) {
|
|
|
415
738
|
params = JSON.parse(argv.params);
|
|
416
739
|
}
|
|
417
740
|
catch (error) {
|
|
418
|
-
console.error(chalk.red(
|
|
741
|
+
console.error(chalk.red("ā Invalid JSON for parameters"));
|
|
419
742
|
process.exit(1);
|
|
420
743
|
}
|
|
421
744
|
}
|
|
422
745
|
console.log(chalk.blue(`š§ Executing tool: ${argv.tool} on server: ${argv.server}`));
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
746
|
+
const spinner = ora("Executing tool...").start();
|
|
747
|
+
try {
|
|
748
|
+
const result = await executeMCPTool(serverConfig, argv.tool, params);
|
|
749
|
+
spinner.succeed(chalk.green("ā
Tool executed successfully!"));
|
|
750
|
+
console.log(chalk.blue("\nš Result:"));
|
|
751
|
+
if (result.content) {
|
|
752
|
+
// Handle different content types
|
|
753
|
+
if (Array.isArray(result.content)) {
|
|
754
|
+
result.content.forEach((item) => {
|
|
755
|
+
if (item.type === "text") {
|
|
756
|
+
console.log(item.text);
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
console.log(JSON.stringify(item, null, 2));
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
console.log(JSON.stringify(result.content, null, 2));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
console.log(JSON.stringify(result, null, 2));
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
spinner.fail(chalk.red("ā Tool execution failed"));
|
|
773
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
774
|
+
process.exit(1);
|
|
775
|
+
}
|
|
776
|
+
})
|
|
777
|
+
// Enhanced unified list command
|
|
778
|
+
.command("list-all", "List servers from all sources (manual + auto + default)", (y) => y
|
|
779
|
+
.usage("Usage: $0 mcp list-all [options]")
|
|
780
|
+
.option("source", {
|
|
781
|
+
type: "string",
|
|
782
|
+
choices: ["manual", "auto", "default", "all"],
|
|
783
|
+
default: "all",
|
|
784
|
+
description: "Filter by source type",
|
|
785
|
+
})
|
|
786
|
+
.option("format", {
|
|
787
|
+
type: "string",
|
|
788
|
+
choices: ["table", "json"],
|
|
789
|
+
default: "table",
|
|
790
|
+
description: "Output format",
|
|
791
|
+
})
|
|
792
|
+
.option("refresh", {
|
|
793
|
+
type: "boolean",
|
|
794
|
+
description: "Force refresh auto-discovery cache",
|
|
795
|
+
})
|
|
796
|
+
.example("$0 mcp list-all", "List all servers from all sources")
|
|
797
|
+
.example("$0 mcp list-all --source auto", "List only auto-discovered servers")
|
|
798
|
+
.example("$0 mcp list-all --refresh", "Refresh auto-discovery and list all"), async (argv) => {
|
|
799
|
+
console.log(chalk.blue("š NeuroLink Unified MCP Registry"));
|
|
800
|
+
console.log(chalk.gray("==================================="));
|
|
801
|
+
const spinner = ora("Initializing NeuroLink MCP...").start();
|
|
802
|
+
try {
|
|
803
|
+
// Initialize built-in NeuroLink servers first
|
|
804
|
+
await initializeNeuroLinkMCP();
|
|
805
|
+
// Initialize unified registry
|
|
806
|
+
spinner.text = "Initializing unified registry...";
|
|
807
|
+
await defaultUnifiedRegistry.initialize();
|
|
808
|
+
// Force refresh if requested
|
|
809
|
+
if (argv.refresh) {
|
|
810
|
+
spinner.text = "Refreshing auto-discovery...";
|
|
811
|
+
await defaultUnifiedRegistry.refreshAutoDiscovery();
|
|
812
|
+
}
|
|
813
|
+
spinner.succeed(chalk.green("Registry initialized!"));
|
|
814
|
+
// Get servers based on source filter
|
|
815
|
+
const servers = defaultUnifiedRegistry.listAllServers();
|
|
816
|
+
const filteredServers = argv.source === "all"
|
|
817
|
+
? servers
|
|
818
|
+
: servers.filter((s) => s.source.type === argv.source);
|
|
819
|
+
if (filteredServers.length === 0) {
|
|
820
|
+
console.log(chalk.yellow(`\nš No servers found from source: ${argv.source}`));
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (argv.format === "json") {
|
|
824
|
+
console.log(JSON.stringify(filteredServers, null, 2));
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
// Table format
|
|
828
|
+
console.log(chalk.green(`\nš Found ${filteredServers.length} servers:`));
|
|
829
|
+
console.log(chalk.gray("ā".repeat(80)));
|
|
830
|
+
filteredServers.forEach((server, index) => {
|
|
831
|
+
const sourceIcon = getSourceTypeIcon(server.source.type);
|
|
832
|
+
const priorityColor = server.source.priority >= 9
|
|
833
|
+
? chalk.green
|
|
834
|
+
: server.source.priority >= 7
|
|
835
|
+
? chalk.yellow
|
|
836
|
+
: chalk.gray;
|
|
837
|
+
console.log(`${chalk.white(`${index + 1}.`)} ${sourceIcon} ${chalk.cyan(server.id)}`);
|
|
838
|
+
console.log(` ${chalk.gray("Source:")} ${server.source.type} ${priorityColor(`(priority: ${server.source.priority})`)}`);
|
|
839
|
+
console.log(` ${chalk.gray("Status:")} ${getStatusIcon(server.status)} ${server.status}`);
|
|
840
|
+
if (server.config) {
|
|
841
|
+
console.log(` ${chalk.gray("Command:")} ${server.config.command} ${server.config.args?.join(" ") || ""}`);
|
|
842
|
+
}
|
|
843
|
+
if (server.source.metadata) {
|
|
844
|
+
const metadata = server.source.metadata;
|
|
845
|
+
if (metadata.configPath) {
|
|
846
|
+
console.log(` ${chalk.gray("Config:")} ${metadata.configPath}`);
|
|
847
|
+
}
|
|
848
|
+
if (metadata.toolCount) {
|
|
849
|
+
console.log(` ${chalk.gray("Tools:")} ${metadata.toolCount}`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
console.log();
|
|
853
|
+
});
|
|
854
|
+
// Display statistics
|
|
855
|
+
const stats = defaultUnifiedRegistry.getStats();
|
|
856
|
+
console.log(chalk.blue("š Registry Statistics:"));
|
|
857
|
+
console.log(` ${chalk.gray("Manual servers:")} ${stats.manual.servers}`);
|
|
858
|
+
console.log(` ${chalk.gray("Auto-discovered:")} ${stats.auto.servers}`);
|
|
859
|
+
console.log(` ${chalk.gray("Default registry:")} ${stats.default.servers}`);
|
|
860
|
+
console.log(` ${chalk.gray("Total tools:")} ${stats.total.tools}`);
|
|
861
|
+
}
|
|
862
|
+
catch (error) {
|
|
863
|
+
spinner.fail(chalk.red("Registry initialization failed"));
|
|
864
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
865
|
+
process.exit(1);
|
|
866
|
+
}
|
|
867
|
+
})
|
|
868
|
+
// Enhanced tool execution with unified registry
|
|
869
|
+
.command("run <tool>", "Execute a tool using unified registry (auto-fallback)", (y) => y
|
|
870
|
+
.usage("Usage: $0 mcp run <tool> [options]")
|
|
871
|
+
.positional("tool", {
|
|
872
|
+
type: "string",
|
|
873
|
+
description: "Tool name to execute",
|
|
874
|
+
demandOption: true,
|
|
875
|
+
})
|
|
876
|
+
.option("params", {
|
|
877
|
+
type: "string",
|
|
878
|
+
description: "Tool parameters (JSON)",
|
|
879
|
+
})
|
|
880
|
+
.option("source", {
|
|
881
|
+
type: "string",
|
|
882
|
+
choices: ["manual", "auto", "default"],
|
|
883
|
+
description: "Preferred source (with fallback)",
|
|
884
|
+
})
|
|
885
|
+
.option("no-fallback", {
|
|
886
|
+
type: "boolean",
|
|
887
|
+
description: "Disable fallback to other sources",
|
|
888
|
+
})
|
|
889
|
+
.example('$0 mcp run generate-text --params \'{"prompt": "Hello world"}\'', "Run tool with fallback")
|
|
890
|
+
.example('$0 mcp run read_file --params \'{"path": "README.md"}\' --source manual', "Prefer manual config"), async (argv) => {
|
|
891
|
+
console.log(chalk.blue(`š Executing tool: ${argv.tool}`));
|
|
892
|
+
const spinner = ora("Initializing NeuroLink MCP...").start();
|
|
893
|
+
try {
|
|
894
|
+
// Initialize built-in NeuroLink servers first
|
|
895
|
+
await initializeNeuroLinkMCP();
|
|
896
|
+
// Initialize unified registry
|
|
897
|
+
spinner.text = "Initializing unified registry...";
|
|
898
|
+
await defaultUnifiedRegistry.initialize();
|
|
899
|
+
let params = {};
|
|
900
|
+
if (argv.params) {
|
|
901
|
+
try {
|
|
902
|
+
params = JSON.parse(argv.params);
|
|
903
|
+
}
|
|
904
|
+
catch (error) {
|
|
905
|
+
spinner.fail(chalk.red("ā Invalid JSON for parameters"));
|
|
906
|
+
process.exit(1);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
// Create execution context
|
|
910
|
+
const contextManager = new ContextManager();
|
|
911
|
+
const context = contextManager.createContext({
|
|
912
|
+
sessionId: `cli-${Date.now()}`,
|
|
913
|
+
userId: "cli-user",
|
|
914
|
+
aiProvider: "unified-mcp",
|
|
915
|
+
});
|
|
916
|
+
const executionOptions = {
|
|
917
|
+
preferredSource: argv.source,
|
|
918
|
+
fallbackEnabled: !argv["no-fallback"],
|
|
919
|
+
validateBeforeExecution: true,
|
|
920
|
+
timeoutMs: 30000,
|
|
921
|
+
};
|
|
922
|
+
spinner.text = "Executing tool...";
|
|
923
|
+
const result = await defaultUnifiedRegistry.executeTool(argv.tool, params, context, executionOptions);
|
|
924
|
+
if (result.success) {
|
|
925
|
+
spinner.succeed(chalk.green("ā
Tool executed successfully!"));
|
|
926
|
+
console.log(chalk.blue("\nš Result:"));
|
|
927
|
+
if (result.data) {
|
|
928
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
929
|
+
}
|
|
930
|
+
else {
|
|
931
|
+
console.log("No data returned");
|
|
932
|
+
}
|
|
933
|
+
if (result.metadata) {
|
|
934
|
+
console.log(chalk.gray("\nš§ Execution Details:"));
|
|
935
|
+
console.log(` Tool: ${result.metadata.toolName}`);
|
|
936
|
+
console.log(` Server: ${result.metadata.serverId || "unknown"}`);
|
|
937
|
+
console.log(` Execution time: ${result.metadata.executionTime}ms`);
|
|
938
|
+
console.log(` Session: ${result.metadata.sessionId}`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
else {
|
|
942
|
+
spinner.fail(chalk.red("ā Tool execution failed"));
|
|
943
|
+
console.error(chalk.red(`Error: ${result.error}`));
|
|
944
|
+
process.exit(1);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
catch (error) {
|
|
948
|
+
spinner.fail(chalk.red("ā Execution failed"));
|
|
949
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
950
|
+
process.exit(1);
|
|
951
|
+
}
|
|
952
|
+
})
|
|
953
|
+
// Configuration management commands
|
|
954
|
+
.command("config <action>", "Manage unified registry configuration", (y) => y
|
|
955
|
+
.usage("Usage: $0 mcp config <action> [options]")
|
|
956
|
+
.command("show", "Show current configuration", {}, async () => {
|
|
957
|
+
try {
|
|
958
|
+
await defaultUnifiedRegistry.initialize();
|
|
959
|
+
const config = defaultUnifiedRegistry.getConfig();
|
|
960
|
+
console.log(chalk.blue("š§ Unified Registry Configuration"));
|
|
961
|
+
console.log(chalk.gray("================================"));
|
|
962
|
+
console.log(JSON.stringify(config, null, 2));
|
|
963
|
+
}
|
|
964
|
+
catch (error) {
|
|
965
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
966
|
+
process.exit(1);
|
|
967
|
+
}
|
|
968
|
+
})
|
|
969
|
+
.command("enable-auto-discovery", "Enable auto-discovery", {}, async () => {
|
|
970
|
+
try {
|
|
971
|
+
await defaultUnifiedRegistry.initialize();
|
|
972
|
+
defaultUnifiedRegistry.updateAutoDiscoveryConfig({
|
|
973
|
+
enabled: true,
|
|
974
|
+
});
|
|
975
|
+
console.log(chalk.green("ā
Auto-discovery enabled"));
|
|
976
|
+
}
|
|
977
|
+
catch (error) {
|
|
978
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
979
|
+
process.exit(1);
|
|
980
|
+
}
|
|
981
|
+
})
|
|
982
|
+
.command("disable-auto-discovery", "Disable auto-discovery", {}, async () => {
|
|
983
|
+
try {
|
|
984
|
+
await defaultUnifiedRegistry.initialize();
|
|
985
|
+
defaultUnifiedRegistry.updateAutoDiscoveryConfig({
|
|
986
|
+
enabled: false,
|
|
987
|
+
});
|
|
988
|
+
console.log(chalk.green("ā
Auto-discovery disabled"));
|
|
989
|
+
}
|
|
990
|
+
catch (error) {
|
|
991
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
992
|
+
process.exit(1);
|
|
993
|
+
}
|
|
994
|
+
})
|
|
995
|
+
.command("set-sources <sources>", "Set preferred auto-discovery sources", (y) => y.positional("sources", {
|
|
996
|
+
type: "string",
|
|
997
|
+
description: "Comma-separated source list",
|
|
998
|
+
demandOption: true,
|
|
999
|
+
}), async (argv) => {
|
|
1000
|
+
try {
|
|
1001
|
+
await defaultUnifiedRegistry.initialize();
|
|
1002
|
+
const sources = argv.sources
|
|
1003
|
+
.split(",")
|
|
1004
|
+
.map((s) => s.trim());
|
|
1005
|
+
defaultUnifiedRegistry.updateAutoDiscoveryConfig({
|
|
1006
|
+
sources,
|
|
1007
|
+
});
|
|
1008
|
+
console.log(chalk.green(`ā
Updated preferred sources: ${sources.join(", ")}`));
|
|
1009
|
+
}
|
|
1010
|
+
catch (error) {
|
|
1011
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
1012
|
+
process.exit(1);
|
|
1013
|
+
}
|
|
1014
|
+
})
|
|
1015
|
+
.command("set-log-level <level>", "Set MCP logging verbosity", (y) => y.positional("level", {
|
|
1016
|
+
type: "string",
|
|
1017
|
+
choices: ["silent", "error", "warn", "info", "debug"],
|
|
1018
|
+
description: "Log level to set",
|
|
1019
|
+
demandOption: true,
|
|
1020
|
+
}), async (argv) => {
|
|
1021
|
+
try {
|
|
1022
|
+
const level = argv.level;
|
|
1023
|
+
const logLevel = level === "silent"
|
|
1024
|
+
? LogLevel.SILENT
|
|
1025
|
+
: level === "error"
|
|
1026
|
+
? LogLevel.ERROR
|
|
1027
|
+
: level === "warn"
|
|
1028
|
+
? LogLevel.WARN
|
|
1029
|
+
: level === "info"
|
|
1030
|
+
? LogLevel.INFO
|
|
1031
|
+
: LogLevel.DEBUG;
|
|
1032
|
+
setGlobalMCPLogLevel(logLevel);
|
|
1033
|
+
console.log(chalk.green(`ā
MCP log level set to: ${level}`));
|
|
1034
|
+
console.log(chalk.gray("This affects auto-discovery, registry operations, and tool execution logging"));
|
|
1035
|
+
}
|
|
1036
|
+
catch (error) {
|
|
1037
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
1038
|
+
process.exit(1);
|
|
1039
|
+
}
|
|
1040
|
+
})
|
|
1041
|
+
.example("$0 mcp config show", "Show current configuration")
|
|
1042
|
+
.example("$0 mcp config enable-auto-discovery", "Enable auto-discovery")
|
|
1043
|
+
.example('$0 mcp config set-sources "claude,vscode,cursor"', "Set preferred sources")
|
|
1044
|
+
.example("$0 mcp config set-log-level debug", "Enable debug logging for troubleshooting"))
|
|
1045
|
+
// Discover MCP servers from all AI tools
|
|
1046
|
+
.command(["discover [options]", "d [options]"], "Discover MCP servers from all AI development tools", (y) => y
|
|
1047
|
+
.usage("Usage: $0 mcp discover [options]")
|
|
1048
|
+
.option("format", {
|
|
1049
|
+
alias: "f",
|
|
1050
|
+
type: "string",
|
|
1051
|
+
choices: ["table", "json", "yaml", "summary"],
|
|
1052
|
+
default: "table",
|
|
1053
|
+
description: "Output format",
|
|
1054
|
+
})
|
|
1055
|
+
.option("include-inactive", {
|
|
1056
|
+
type: "boolean",
|
|
1057
|
+
default: true,
|
|
1058
|
+
description: "Include servers that may not be currently active",
|
|
1059
|
+
})
|
|
1060
|
+
.option("preferred-tools", {
|
|
1061
|
+
type: "string",
|
|
1062
|
+
description: "Prioritize specific tools (comma-separated)",
|
|
1063
|
+
})
|
|
1064
|
+
.option("workspace-only", {
|
|
1065
|
+
type: "boolean",
|
|
1066
|
+
description: "Search only workspace/project configurations",
|
|
1067
|
+
})
|
|
1068
|
+
.option("global-only", {
|
|
1069
|
+
type: "boolean",
|
|
1070
|
+
description: "Search only global configurations",
|
|
1071
|
+
})
|
|
1072
|
+
.example("$0 mcp discover", "Discover all MCP servers")
|
|
1073
|
+
.example("$0 mcp d --format json", "Export as JSON (using alias)")
|
|
1074
|
+
.example('$0 mcp discover --preferred-tools "claude,cursor"', "Prioritize specific tools"), async (argv) => {
|
|
1075
|
+
console.log(chalk.blue("š NeuroLink MCP Server Discovery"));
|
|
1076
|
+
console.log(chalk.gray("====================================="));
|
|
1077
|
+
const options = {
|
|
1078
|
+
searchGlobal: !argv["workspace-only"],
|
|
1079
|
+
searchWorkspace: !argv["global-only"],
|
|
1080
|
+
searchCommonPaths: true,
|
|
1081
|
+
includeInactive: argv["include-inactive"],
|
|
1082
|
+
preferredTools: argv["preferred-tools"]
|
|
1083
|
+
? argv["preferred-tools"]
|
|
1084
|
+
.split(",")
|
|
1085
|
+
.map((t) => t.trim())
|
|
1086
|
+
: [],
|
|
1087
|
+
};
|
|
1088
|
+
const spinner = ora("Discovering MCP servers...").start();
|
|
1089
|
+
try {
|
|
1090
|
+
mcpLogger.debug("[MCP Discovery] Starting server discovery:", {
|
|
1091
|
+
searchGlobal: options.searchGlobal,
|
|
1092
|
+
searchWorkspace: options.searchWorkspace,
|
|
1093
|
+
includeInactive: options.includeInactive,
|
|
1094
|
+
preferredTools: options.preferredTools,
|
|
1095
|
+
});
|
|
1096
|
+
const discoveryResult = await discoverMCPServers(options);
|
|
1097
|
+
mcpLogger.info("[MCP Discovery] Discovery completed:", {
|
|
1098
|
+
serversFound: discoveryResult.discovered.length,
|
|
1099
|
+
executionTime: discoveryResult.stats.executionTime,
|
|
1100
|
+
configFilesFound: discoveryResult.stats.configFilesFound,
|
|
1101
|
+
duplicatesRemoved: discoveryResult.stats.duplicatesRemoved,
|
|
1102
|
+
});
|
|
1103
|
+
spinner.succeed(chalk.green("Discovery completed!"));
|
|
1104
|
+
if (discoveryResult.discovered.length === 0) {
|
|
1105
|
+
console.log(chalk.yellow("\nš No MCP servers found"));
|
|
1106
|
+
console.log(chalk.gray("\nš” Tips for finding MCP servers:"));
|
|
1107
|
+
console.log(chalk.gray(" ⢠Make sure you have Claude Desktop, VS Code, or Cursor with MCP configurations"));
|
|
1108
|
+
console.log(chalk.gray(" ⢠Check that MCP configuration files exist in their expected locations"));
|
|
1109
|
+
console.log(chalk.gray(" ⢠Run with 'neurolink mcp discover' to search all locations"));
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
// Display results based on format
|
|
1113
|
+
if (argv.format === "json") {
|
|
1114
|
+
console.log(JSON.stringify(discoveryResult, null, 2));
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
if (argv.format === "yaml") {
|
|
1118
|
+
// Simple YAML output
|
|
1119
|
+
console.log("discovered:");
|
|
1120
|
+
discoveryResult.discovered.forEach((server) => {
|
|
1121
|
+
console.log(` - id: ${server.id}`);
|
|
1122
|
+
console.log(` title: ${server.title}`);
|
|
1123
|
+
console.log(` source: ${server.source.tool}`);
|
|
1124
|
+
console.log(` command: ${server.command}`);
|
|
1125
|
+
if (server.args && server.args.length > 0) {
|
|
1126
|
+
console.log(" args:");
|
|
1127
|
+
server.args.forEach((arg) => console.log(` - ${arg}`));
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (argv.format === "summary") {
|
|
1133
|
+
displaySummary(discoveryResult);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
// Table format (default)
|
|
1137
|
+
displayTable(discoveryResult);
|
|
1138
|
+
// Display errors if any
|
|
1139
|
+
if (discoveryResult.errors.length > 0) {
|
|
1140
|
+
console.log(chalk.yellow("\nā ļø Discovery warnings:"));
|
|
1141
|
+
discoveryResult.errors.forEach((error) => {
|
|
1142
|
+
console.log(chalk.yellow(` ⢠${error}`));
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
catch (error) {
|
|
1147
|
+
mcpLogger.error("[MCP Discovery] Discovery failed:", {
|
|
1148
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1149
|
+
options,
|
|
1150
|
+
});
|
|
1151
|
+
spinner.fail(chalk.red("Discovery failed"));
|
|
1152
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
1153
|
+
process.exit(1);
|
|
1154
|
+
}
|
|
1155
|
+
})
|
|
1156
|
+
.demandCommand(1, "Please specify an MCP subcommand")
|
|
1157
|
+
.example("$0 mcp list", "List configured MCP servers")
|
|
1158
|
+
.example("$0 mcp discover", "Discover MCP servers from all tools")
|
|
1159
|
+
.example("$0 mcp install filesystem", "Install filesystem MCP server")
|
|
1160
|
+
.example("$0 mcp test filesystem", "Test filesystem server connection");
|
|
433
1161
|
});
|
|
434
1162
|
}
|