@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
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NeuroLink External MCP Manager
|
|
3
|
+
* Manages external MCP servers and bridges them into the NeuroLink factory ecosystem
|
|
4
|
+
* Reads .mcp-config.json and spawns external clients
|
|
5
|
+
*/
|
|
6
|
+
import { readFile } from "fs/promises";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
import { createExternalMCPClient, } from "./external-client.js";
|
|
9
|
+
import { createMCPServer } from "./factory.js";
|
|
10
|
+
import { MCPToolRegistry } from "./tool-registry.js";
|
|
11
|
+
import { mcpLogger as logger } from "./logging.js";
|
|
12
|
+
/**
|
|
13
|
+
* External MCP Manager
|
|
14
|
+
* Bridges external MCP servers into NeuroLink's factory pattern ecosystem
|
|
15
|
+
*/
|
|
16
|
+
export class ExternalMCPManager {
|
|
17
|
+
servers = new Map();
|
|
18
|
+
registry;
|
|
19
|
+
configPath;
|
|
20
|
+
config = null;
|
|
21
|
+
isInitialized = false;
|
|
22
|
+
constructor(configPath, registry) {
|
|
23
|
+
this.configPath = configPath || ".mcp-config.json";
|
|
24
|
+
this.registry = registry || new MCPToolRegistry();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Initialize the external MCP manager
|
|
28
|
+
* Reads config and connects to all external servers
|
|
29
|
+
*/
|
|
30
|
+
async initialize() {
|
|
31
|
+
if (this.isInitialized) {
|
|
32
|
+
logger.debug("[External MCP Manager] Already initialized");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
logger.info("[External MCP Manager] Initializing...");
|
|
37
|
+
// Load configuration
|
|
38
|
+
await this.loadConfig();
|
|
39
|
+
if (!this.config?.mcpServers) {
|
|
40
|
+
logger.warn("[External MCP Manager] No MCP servers configured");
|
|
41
|
+
this.isInitialized = true;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Connect to all configured servers
|
|
45
|
+
const serverNames = Object.keys(this.config.mcpServers);
|
|
46
|
+
logger.info(`[External MCP Manager] Connecting to ${serverNames.length} external servers: ${serverNames.join(", ")}`);
|
|
47
|
+
const connectionPromises = serverNames.map((serverName) => this.connectToServer(serverName).catch((error) => {
|
|
48
|
+
logger.error(`[External MCP Manager] Failed to connect to ${serverName}:`, error);
|
|
49
|
+
return null;
|
|
50
|
+
}));
|
|
51
|
+
const results = await Promise.allSettled(connectionPromises);
|
|
52
|
+
const connected = results.filter((r) => r.status === "fulfilled" && r.value !== null).length;
|
|
53
|
+
logger.info(`[External MCP Manager] Connected to ${connected}/${serverNames.length} external servers`);
|
|
54
|
+
this.isInitialized = true;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logger.error("[External MCP Manager] Initialization failed:", error);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Load MCP configuration from file
|
|
63
|
+
*/
|
|
64
|
+
async loadConfig() {
|
|
65
|
+
try {
|
|
66
|
+
const configFile = resolve(this.configPath);
|
|
67
|
+
const configData = await readFile(configFile, "utf-8");
|
|
68
|
+
this.config = JSON.parse(configData);
|
|
69
|
+
logger.debug("[External MCP Manager] Config loaded:", {
|
|
70
|
+
serversCount: Object.keys(this.config?.mcpServers || {}).length,
|
|
71
|
+
autoDiscovery: this.config?.autoDiscovery?.enabled,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error.code === "ENOENT") {
|
|
76
|
+
logger.warn(`[External MCP Manager] Config file not found: ${this.configPath}`);
|
|
77
|
+
this.config = { mcpServers: {} };
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
logger.error("[External MCP Manager] Failed to load config:", error);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Connect to a specific external server
|
|
87
|
+
*/
|
|
88
|
+
async connectToServer(serverName) {
|
|
89
|
+
if (!this.config?.mcpServers[serverName]) {
|
|
90
|
+
throw new Error(`Server configuration not found: ${serverName}`);
|
|
91
|
+
}
|
|
92
|
+
const serverConfig = this.config.mcpServers[serverName];
|
|
93
|
+
try {
|
|
94
|
+
logger.info(`[External MCP Manager] Connecting to server: ${serverName}`);
|
|
95
|
+
// Create external client
|
|
96
|
+
const client = createExternalMCPClient({
|
|
97
|
+
name: serverName,
|
|
98
|
+
command: serverConfig.command,
|
|
99
|
+
args: serverConfig.args,
|
|
100
|
+
transport: serverConfig.transport,
|
|
101
|
+
env: serverConfig.env,
|
|
102
|
+
});
|
|
103
|
+
// Create server info
|
|
104
|
+
const serverInfo = {
|
|
105
|
+
name: serverName,
|
|
106
|
+
client,
|
|
107
|
+
server: createMCPServer({
|
|
108
|
+
id: `external-${serverName}`,
|
|
109
|
+
title: `External ${serverConfig.name || serverName}`,
|
|
110
|
+
description: `External MCP server: ${serverName}`,
|
|
111
|
+
category: "integrations",
|
|
112
|
+
visibility: "private",
|
|
113
|
+
metadata: {
|
|
114
|
+
external: true,
|
|
115
|
+
transport: "stdio",
|
|
116
|
+
command: serverConfig.command,
|
|
117
|
+
args: serverConfig.args,
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
status: "connecting",
|
|
121
|
+
toolCount: 0,
|
|
122
|
+
retryCount: 0,
|
|
123
|
+
};
|
|
124
|
+
// Setup event handlers
|
|
125
|
+
client.on("connected", () => {
|
|
126
|
+
logger.info(`[External MCP Manager] Connected to ${serverName}`);
|
|
127
|
+
serverInfo.status = "connected";
|
|
128
|
+
this.registerServerTools(serverInfo);
|
|
129
|
+
});
|
|
130
|
+
client.on("disconnected", () => {
|
|
131
|
+
logger.warn(`[External MCP Manager] Disconnected from ${serverName}`);
|
|
132
|
+
serverInfo.status = "disconnected";
|
|
133
|
+
// TODO: Implement reconnection logic
|
|
134
|
+
});
|
|
135
|
+
client.on("error", (error) => {
|
|
136
|
+
logger.error(`[External MCP Manager] Error from ${serverName}:`, error);
|
|
137
|
+
serverInfo.status = "error";
|
|
138
|
+
serverInfo.lastError = error.message;
|
|
139
|
+
});
|
|
140
|
+
// Store server info
|
|
141
|
+
this.servers.set(serverName, serverInfo);
|
|
142
|
+
// Connect to the server
|
|
143
|
+
await client.connect();
|
|
144
|
+
return serverInfo;
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
logger.error(`[External MCP Manager] Failed to connect to ${serverName}:`, error);
|
|
148
|
+
const serverInfo = {
|
|
149
|
+
name: serverName,
|
|
150
|
+
client: null,
|
|
151
|
+
server: null,
|
|
152
|
+
status: "error",
|
|
153
|
+
lastError: error instanceof Error ? error.message : String(error),
|
|
154
|
+
toolCount: 0,
|
|
155
|
+
retryCount: 0,
|
|
156
|
+
};
|
|
157
|
+
this.servers.set(serverName, serverInfo);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Register tools from an external server with the NeuroLink registry
|
|
163
|
+
*/
|
|
164
|
+
async registerServerTools(serverInfo) {
|
|
165
|
+
try {
|
|
166
|
+
const tools = serverInfo.client.getNeuroLinkTools();
|
|
167
|
+
// Register each tool with the server
|
|
168
|
+
for (const [toolName, tool] of Object.entries(tools)) {
|
|
169
|
+
serverInfo.server.registerTool(tool);
|
|
170
|
+
}
|
|
171
|
+
serverInfo.toolCount = Object.keys(tools).length;
|
|
172
|
+
// Register the server with the registry
|
|
173
|
+
await this.registry.registerServer(serverInfo.name, serverInfo);
|
|
174
|
+
logger.info(`[External MCP Manager] Registered ${serverInfo.toolCount} tools from ${serverInfo.name}`);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
logger.error(`[External MCP Manager] Failed to register tools from ${serverInfo.name}:`, error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Get all connected external servers
|
|
182
|
+
*/
|
|
183
|
+
getConnectedServers() {
|
|
184
|
+
return Array.from(this.servers.values()).filter((s) => s.status === "connected");
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get server status
|
|
188
|
+
*/
|
|
189
|
+
getServerStatus(serverName) {
|
|
190
|
+
return this.servers.get(serverName);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get all server statuses
|
|
194
|
+
*/
|
|
195
|
+
getAllServerStatuses() {
|
|
196
|
+
const statuses = {};
|
|
197
|
+
for (const [name, info] of this.servers) {
|
|
198
|
+
statuses[name] = {
|
|
199
|
+
...info,
|
|
200
|
+
// Don't include the actual client/server objects in status
|
|
201
|
+
client: undefined,
|
|
202
|
+
server: undefined,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return statuses;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Execute a tool on any connected server
|
|
209
|
+
*/
|
|
210
|
+
async executeTool(toolName, params, context) {
|
|
211
|
+
// Use the registry to execute the tool
|
|
212
|
+
return this.registry.executeTool(toolName, params, context);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* List all available tools from external servers
|
|
216
|
+
*/
|
|
217
|
+
listExternalTools() {
|
|
218
|
+
return this.registry.listTools();
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get registry instance
|
|
222
|
+
*/
|
|
223
|
+
getRegistry() {
|
|
224
|
+
return this.registry;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Disconnect from a specific server
|
|
228
|
+
*/
|
|
229
|
+
async disconnectServer(serverName) {
|
|
230
|
+
const serverInfo = this.servers.get(serverName);
|
|
231
|
+
if (!serverInfo || !serverInfo.client) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
await serverInfo.client.disconnect();
|
|
236
|
+
this.registry.unregisterServer(`external-${serverName}`);
|
|
237
|
+
this.servers.delete(serverName);
|
|
238
|
+
logger.info(`[External MCP Manager] Disconnected from ${serverName}`);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
logger.error(`[External MCP Manager] Failed to disconnect from ${serverName}:`, error);
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Disconnect from all servers
|
|
248
|
+
*/
|
|
249
|
+
async disconnectAll() {
|
|
250
|
+
logger.info("[External MCP Manager] Disconnecting from all external servers");
|
|
251
|
+
const disconnectPromises = Array.from(this.servers.keys()).map((serverName) => this.disconnectServer(serverName));
|
|
252
|
+
await Promise.allSettled(disconnectPromises);
|
|
253
|
+
this.servers.clear();
|
|
254
|
+
this.isInitialized = false;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Refresh connections - reload config and reconnect
|
|
258
|
+
*/
|
|
259
|
+
async refresh() {
|
|
260
|
+
logger.info("[External MCP Manager] Refreshing connections");
|
|
261
|
+
await this.disconnectAll();
|
|
262
|
+
await this.initialize();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Get comprehensive status
|
|
266
|
+
*/
|
|
267
|
+
getStatus() {
|
|
268
|
+
const servers = Array.from(this.servers.values());
|
|
269
|
+
return {
|
|
270
|
+
isInitialized: this.isInitialized,
|
|
271
|
+
configPath: this.configPath,
|
|
272
|
+
totalServers: servers.length,
|
|
273
|
+
connected: servers.filter((s) => s.status === "connected").length,
|
|
274
|
+
connecting: servers.filter((s) => s.status === "connecting").length,
|
|
275
|
+
disconnected: servers.filter((s) => s.status === "disconnected").length,
|
|
276
|
+
errors: servers.filter((s) => s.status === "error").length,
|
|
277
|
+
totalTools: servers.reduce((sum, s) => sum + s.toolCount, 0),
|
|
278
|
+
servers: this.getAllServerStatuses(),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Default external MCP manager instance
|
|
284
|
+
*/
|
|
285
|
+
export const defaultExternalMCPManager = new ExternalMCPManager();
|
|
286
|
+
/**
|
|
287
|
+
* Initialize external MCP servers with default manager
|
|
288
|
+
*/
|
|
289
|
+
export async function initializeExternalMCP(configPath) {
|
|
290
|
+
if (configPath) {
|
|
291
|
+
const manager = new ExternalMCPManager(configPath);
|
|
292
|
+
await manager.initialize();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
await defaultExternalMCPManager.initialize();
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Get the default external MCP manager
|
|
299
|
+
*/
|
|
300
|
+
export function getExternalMCPManager() {
|
|
301
|
+
return defaultExternalMCPManager;
|
|
302
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Compatible with Lighthouse MCP patterns for seamless migration
|
|
5
5
|
*/
|
|
6
6
|
import { z } from "zod";
|
|
7
|
+
import type { ExecutionContext } from "./contracts/mcp-contract.js";
|
|
7
8
|
/**
|
|
8
9
|
* MCP Server Categories for organization and discovery
|
|
9
10
|
*/
|
|
@@ -11,10 +12,9 @@ export type MCPServerCategory = "ai-providers" | "frameworks" | "development" |
|
|
|
11
12
|
/**
|
|
12
13
|
* Tool execution context - Rich context passed to every tool execution
|
|
13
14
|
* Following Lighthouse's pattern for rich tool context
|
|
15
|
+
* Extends ExecutionContext for compatibility
|
|
14
16
|
*/
|
|
15
|
-
export interface NeuroLinkExecutionContext {
|
|
16
|
-
sessionId: string;
|
|
17
|
-
userId?: string;
|
|
17
|
+
export interface NeuroLinkExecutionContext extends ExecutionContext {
|
|
18
18
|
aiProvider?: string;
|
|
19
19
|
modelId?: string;
|
|
20
20
|
temperature?: number;
|
|
@@ -51,7 +51,7 @@ export interface NeuroLinkExecutionContext {
|
|
|
51
51
|
export interface ToolResult {
|
|
52
52
|
success: boolean;
|
|
53
53
|
data?: any;
|
|
54
|
-
error?: string;
|
|
54
|
+
error?: string | Error;
|
|
55
55
|
usage?: {
|
|
56
56
|
tokens?: number;
|
|
57
57
|
cost?: number;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { tool } from "ai";
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
import {
|
|
8
|
+
import { unifiedRegistry } from "./unified-registry.js";
|
|
9
9
|
import { createExecutionContext } from "./context-manager.js";
|
|
10
10
|
import { mcpLogger } from "./logging.js";
|
|
11
11
|
/**
|
|
@@ -74,18 +74,21 @@ export async function getAvailableFunctionTools() {
|
|
|
74
74
|
const { initializeNeuroLinkMCP } = await import("./initialize.js");
|
|
75
75
|
await initializeNeuroLinkMCP();
|
|
76
76
|
// Ensure unified registry is initialized
|
|
77
|
-
await
|
|
77
|
+
await unifiedRegistry.initialize();
|
|
78
78
|
// Try to activate important servers like filesystem
|
|
79
79
|
mcpLogger.debug(`[${functionTag}] Attempting to activate important servers...`);
|
|
80
80
|
try {
|
|
81
81
|
// Get auto-discovered servers
|
|
82
|
-
const autoServers =
|
|
82
|
+
const autoServers = unifiedRegistry.getAutoDiscoveredServers();
|
|
83
83
|
// Try to activate filesystem server specifically
|
|
84
|
-
for (const
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
for (const server of autoServers) {
|
|
85
|
+
if (server.metadata.name.includes("filesystem")) {
|
|
86
|
+
mcpLogger.debug(`[${functionTag}] Activating filesystem server: ${server.metadata.name}`);
|
|
87
|
+
const serverName = server.metadata.name;
|
|
88
|
+
if (typeof serverName === "string" && serverName.length > 0) {
|
|
89
|
+
// @ts-ignore - serverName is verified as string with length > 0 above
|
|
90
|
+
await unifiedRegistry.lazyActivateServer(serverName);
|
|
91
|
+
}
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
}
|
|
@@ -93,7 +96,7 @@ export async function getAvailableFunctionTools() {
|
|
|
93
96
|
mcpLogger.debug(`[${functionTag}] Error activating servers: ${error}`);
|
|
94
97
|
}
|
|
95
98
|
// Get all tools from unified registry directly
|
|
96
|
-
const allTools = await
|
|
99
|
+
const allTools = await unifiedRegistry.listAllTools();
|
|
97
100
|
mcpLogger.debug(`[${functionTag}] Found ${allTools.length} total tools`);
|
|
98
101
|
// Try to activate servers that have "-tools" entries to get real tools
|
|
99
102
|
const serversToActivate = allTools
|
|
@@ -101,18 +104,19 @@ export async function getAvailableFunctionTools() {
|
|
|
101
104
|
.map((tool) => tool.server);
|
|
102
105
|
mcpLogger.debug(`[${functionTag}] Attempting to activate ${serversToActivate.length} servers for real tool discovery`);
|
|
103
106
|
// Activate servers to convert placeholders to real tools
|
|
104
|
-
for (const serverId of [...new Set(serversToActivate)]) {
|
|
107
|
+
for (const serverId of [...new Set(serversToActivate)].filter((id) => typeof id === "string" && id !== "unknown")) {
|
|
105
108
|
try {
|
|
106
109
|
mcpLogger.debug(`[${functionTag}] Activating server: ${serverId}`);
|
|
107
110
|
// Get the server entry from auto-discovered or manual servers
|
|
108
|
-
const autoServers =
|
|
109
|
-
const manualServers =
|
|
110
|
-
const serverEntry = autoServers.
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
const autoServers = unifiedRegistry.getAutoDiscoveredServers();
|
|
112
|
+
const manualServers = unifiedRegistry.getManualServers();
|
|
113
|
+
const serverEntry = autoServers.find((s) => s.metadata.name === serverId) ||
|
|
114
|
+
manualServers.get(serverId);
|
|
115
|
+
if (serverEntry && serverId.length > 0) {
|
|
116
|
+
await unifiedRegistry.lazyActivateServer(serverId);
|
|
113
117
|
}
|
|
114
118
|
else {
|
|
115
|
-
mcpLogger.debug(`[${functionTag}] Server entry not found for: ${serverId}`);
|
|
119
|
+
mcpLogger.debug(`[${functionTag}] Server entry not found for: ${serverId || "undefined"}`);
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
catch (error) {
|
|
@@ -120,7 +124,7 @@ export async function getAvailableFunctionTools() {
|
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
126
|
// Get tools again after activation
|
|
123
|
-
const activatedTools = await
|
|
127
|
+
const activatedTools = await unifiedRegistry.listAllTools();
|
|
124
128
|
mcpLogger.debug(`[${functionTag}] Found ${activatedTools.length} total tools after activation`);
|
|
125
129
|
// Filter to get real, individual tools (not placeholder or grouped tools)
|
|
126
130
|
const realTools = activatedTools.filter((toolInfo) => {
|
|
@@ -152,7 +156,7 @@ export async function getAvailableFunctionTools() {
|
|
|
152
156
|
let functionName;
|
|
153
157
|
let originalFunctionName;
|
|
154
158
|
// Convert server name to underscore format to check if it's embedded in tool name
|
|
155
|
-
const sanitizedServerCheck = toolInfo.server.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
159
|
+
const sanitizedServerCheck = (toolInfo.server || "unknown").replace(/[^a-zA-Z0-9_]/g, "_");
|
|
156
160
|
if (toolInfo.name.includes(sanitizedServerCheck) ||
|
|
157
161
|
toolInfo.name.endsWith("-tools") ||
|
|
158
162
|
toolInfo.name.startsWith("github_com_") ||
|
|
@@ -202,7 +206,7 @@ export async function getAvailableFunctionTools() {
|
|
|
202
206
|
}
|
|
203
207
|
else {
|
|
204
208
|
// Tool name doesn't include server info, create compound name
|
|
205
|
-
const sanitizedServerName = toolInfo.server.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
209
|
+
const sanitizedServerName = (toolInfo.server || "unknown").replace(/[^a-zA-Z0-9_]/g, "_");
|
|
206
210
|
const sanitizedToolName = toolInfo.name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
207
211
|
// Check if it's a filesystem tool from MCP
|
|
208
212
|
if (sanitizedServerName.includes("modelcontextprotocol") &&
|
|
@@ -235,7 +239,7 @@ export async function getAvailableFunctionTools() {
|
|
|
235
239
|
}
|
|
236
240
|
}
|
|
237
241
|
}
|
|
238
|
-
originalFunctionName = `${toolInfo.server}.${toolInfo.name}`;
|
|
242
|
+
originalFunctionName = `${toolInfo.server || "unknown"}.${toolInfo.name}`;
|
|
239
243
|
}
|
|
240
244
|
// Create AI SDK tool using the proper tool() helper
|
|
241
245
|
const aiTool = tool({
|
|
@@ -273,7 +277,7 @@ export async function getAvailableFunctionTools() {
|
|
|
273
277
|
tools.push(aiTool);
|
|
274
278
|
// Store mapping for execution - CRITICAL: Use sanitized functionName as key
|
|
275
279
|
toolMap.set(functionName, {
|
|
276
|
-
serverId: toolInfo.server,
|
|
280
|
+
serverId: toolInfo.server || "unknown",
|
|
277
281
|
toolName: toolInfo.name,
|
|
278
282
|
});
|
|
279
283
|
mcpLogger.debug(`[${functionTag}] Converted tool: ${functionName} (original: ${originalFunctionName})`);
|
|
@@ -312,7 +316,17 @@ export async function getAvailableFunctionTools() {
|
|
|
312
316
|
export async function executeFunctionCall(functionName, parameters, context) {
|
|
313
317
|
const functionTag = "executeFunctionCall";
|
|
314
318
|
try {
|
|
315
|
-
|
|
319
|
+
// CRITICAL FIX: Unwrap 'input' parameter if it exists
|
|
320
|
+
// AI function calling wraps parameters in { input: { actualParams } }
|
|
321
|
+
// but MCP tools expect direct parameters
|
|
322
|
+
let actualParameters = parameters;
|
|
323
|
+
if (parameters.input &&
|
|
324
|
+
typeof parameters.input === "object" &&
|
|
325
|
+
parameters.input !== null) {
|
|
326
|
+
actualParameters = parameters.input;
|
|
327
|
+
mcpLogger.debug(`[${functionTag}] Unwrapped input parameter for ${functionName}`, { original: parameters, unwrapped: actualParameters });
|
|
328
|
+
}
|
|
329
|
+
mcpLogger.debug(`[${functionTag}] Executing function call: ${functionName}`, { parameters: actualParameters });
|
|
316
330
|
// Create context if not provided
|
|
317
331
|
let finalContext = context;
|
|
318
332
|
if (!finalContext) {
|
|
@@ -325,23 +339,34 @@ export async function executeFunctionCall(functionName, parameters, context) {
|
|
|
325
339
|
// Parse server and tool name from function name
|
|
326
340
|
// First try underscore format (sanitized), then fallback to dot format
|
|
327
341
|
let parts = functionName.split("_");
|
|
328
|
-
let serverId;
|
|
329
|
-
let toolName;
|
|
342
|
+
let serverId = "unknown";
|
|
343
|
+
let toolName = functionName;
|
|
330
344
|
if (parts.length >= 2) {
|
|
331
345
|
// Handle underscore format (sanitized names)
|
|
332
|
-
|
|
346
|
+
const firstPart = parts[0];
|
|
347
|
+
if (firstPart && typeof firstPart === "string" && firstPart.length > 0) {
|
|
348
|
+
serverId = firstPart;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
serverId = "unknown";
|
|
352
|
+
}
|
|
333
353
|
toolName = parts.slice(1).join("_"); // Rejoin in case tool name had underscores
|
|
334
354
|
}
|
|
335
355
|
else {
|
|
336
356
|
// Fallback to dot format for backward compatibility
|
|
337
357
|
parts = functionName.split(".");
|
|
338
358
|
if (parts.length === 2) {
|
|
339
|
-
|
|
359
|
+
const parsedServerId = parts[0];
|
|
360
|
+
const parsedToolName = parts[1];
|
|
361
|
+
if (parsedServerId && parsedToolName) {
|
|
362
|
+
serverId = parsedServerId;
|
|
363
|
+
toolName = parsedToolName;
|
|
364
|
+
}
|
|
340
365
|
}
|
|
341
366
|
else {
|
|
342
367
|
// Can't parse - try executing as-is through unified registry
|
|
343
368
|
mcpLogger.debug(`[${functionTag}] Cannot parse function name format: ${functionName}, trying unified registry`);
|
|
344
|
-
const result = await
|
|
369
|
+
const result = await unifiedRegistry.executeTool(functionName, actualParameters, finalContext);
|
|
345
370
|
return result;
|
|
346
371
|
}
|
|
347
372
|
}
|
|
@@ -352,15 +377,15 @@ export async function executeFunctionCall(functionName, parameters, context) {
|
|
|
352
377
|
serverId === "neurolink_ai_core") {
|
|
353
378
|
mcpLogger.debug(`[${functionTag}] Executing built-in tool: ${toolName} from ${serverId}`);
|
|
354
379
|
// Import and execute from default registry
|
|
355
|
-
const { defaultToolRegistry } = await import("./registry.js");
|
|
356
|
-
return await defaultToolRegistry.executeTool(toolName,
|
|
380
|
+
const { defaultToolRegistry } = await import("./tool-registry.js");
|
|
381
|
+
return await defaultToolRegistry.executeTool(toolName, actualParameters, finalContext);
|
|
357
382
|
}
|
|
358
383
|
// Handle filesystem server with special activation logic
|
|
359
384
|
if (serverId === "filesystem") {
|
|
360
385
|
mcpLogger.debug(`[${functionTag}] Executing filesystem tool: ${toolName}`);
|
|
361
386
|
try {
|
|
362
387
|
// First, try to execute through unified registry
|
|
363
|
-
const result = await
|
|
388
|
+
const result = await unifiedRegistry.executeTool(functionName, actualParameters, finalContext);
|
|
364
389
|
if (result.success) {
|
|
365
390
|
return result;
|
|
366
391
|
}
|
|
@@ -372,7 +397,7 @@ export async function executeFunctionCall(functionName, parameters, context) {
|
|
|
372
397
|
try {
|
|
373
398
|
// Force activation by attempting to activate the filesystem server directly
|
|
374
399
|
// This should convert placeholders to real tools
|
|
375
|
-
await
|
|
400
|
+
await unifiedRegistry.initialize();
|
|
376
401
|
// Try a few common activation patterns
|
|
377
402
|
const activationAttempts = [
|
|
378
403
|
`${serverId}.list_directory`,
|
|
@@ -381,7 +406,7 @@ export async function executeFunctionCall(functionName, parameters, context) {
|
|
|
381
406
|
];
|
|
382
407
|
for (const attempt of activationAttempts) {
|
|
383
408
|
try {
|
|
384
|
-
const result = await
|
|
409
|
+
const result = await unifiedRegistry.executeTool(attempt, actualParameters, finalContext);
|
|
385
410
|
if (result.success) {
|
|
386
411
|
mcpLogger.debug(`[${functionTag}] Filesystem tool executed successfully with: ${attempt}`);
|
|
387
412
|
return result;
|
|
@@ -394,7 +419,7 @@ export async function executeFunctionCall(functionName, parameters, context) {
|
|
|
394
419
|
// If all attempts fail, return a helpful error
|
|
395
420
|
return {
|
|
396
421
|
success: false,
|
|
397
|
-
error: `Filesystem tool '${toolName}' could not be activated. Available parameters: ${JSON.stringify(
|
|
422
|
+
error: `Filesystem tool '${toolName}' could not be activated. Available parameters: ${JSON.stringify(actualParameters)}`,
|
|
398
423
|
metadata: {
|
|
399
424
|
functionName,
|
|
400
425
|
timestamp: Date.now(),
|
|
@@ -417,7 +442,7 @@ export async function executeFunctionCall(functionName, parameters, context) {
|
|
|
417
442
|
}
|
|
418
443
|
// For other tools, use unified registry
|
|
419
444
|
mcpLogger.debug(`[${functionTag}] Executing through unified registry: ${functionName}`);
|
|
420
|
-
const result = await
|
|
445
|
+
const result = await unifiedRegistry.executeTool(functionName, actualParameters, finalContext);
|
|
421
446
|
mcpLogger.debug(`[${functionTag}] Function call completed: ${functionName}`, {
|
|
422
447
|
success: result.success,
|
|
423
448
|
hasData: !!result.data,
|