@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,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Ecosystem Integration - Bridge Between Research Blueprint and NeuroLink
|
|
3
|
+
* Provides unified interface for plugin management and execution
|
|
4
|
+
*/
|
|
5
|
+
import { pluginManager } from "./plugin-manager.js";
|
|
6
|
+
import { SecurityManager } from "./security-manager.js";
|
|
7
|
+
import { mcpLogger } from "./logging.js";
|
|
8
|
+
import { tool } from "ai";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
/**
|
|
11
|
+
* MCP Ecosystem - Main Interface for Plugin Operations
|
|
12
|
+
*/
|
|
13
|
+
export class MCPEcosystem {
|
|
14
|
+
static instance;
|
|
15
|
+
securityManager;
|
|
16
|
+
initialized = false;
|
|
17
|
+
constructor() {
|
|
18
|
+
this.securityManager = new SecurityManager("moderate");
|
|
19
|
+
}
|
|
20
|
+
static getInstance() {
|
|
21
|
+
if (!MCPEcosystem.instance) {
|
|
22
|
+
MCPEcosystem.instance = new MCPEcosystem();
|
|
23
|
+
}
|
|
24
|
+
return MCPEcosystem.instance;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Initialize the MCP ecosystem
|
|
28
|
+
*/
|
|
29
|
+
async initialize() {
|
|
30
|
+
if (this.initialized) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
mcpLogger.info("[MCPEcosystem] Initializing MCP ecosystem...");
|
|
34
|
+
await pluginManager.initialize();
|
|
35
|
+
this.initialized = true;
|
|
36
|
+
const stats = pluginManager.getDiscoveryStats();
|
|
37
|
+
mcpLogger.info("[MCPEcosystem] MCP ecosystem initialized", stats);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* List all available MCPs
|
|
41
|
+
*/
|
|
42
|
+
async list() {
|
|
43
|
+
await this.ensureInitialized();
|
|
44
|
+
return pluginManager.listDiscovered();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get metadata for a specific MCP
|
|
48
|
+
*/
|
|
49
|
+
async getMetadata(name) {
|
|
50
|
+
await this.ensureInitialized();
|
|
51
|
+
return pluginManager.getMetadata(name);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create and execute an MCP instance
|
|
55
|
+
*/
|
|
56
|
+
async execute(name, config, args, context) {
|
|
57
|
+
await this.ensureInitialized();
|
|
58
|
+
const functionTag = "MCPEcosystem.execute";
|
|
59
|
+
try {
|
|
60
|
+
// Get MCP metadata for permissions
|
|
61
|
+
const metadata = await this.getMetadata(name);
|
|
62
|
+
if (!metadata) {
|
|
63
|
+
throw new Error(`MCP ${name} not found`);
|
|
64
|
+
}
|
|
65
|
+
// Create execution context
|
|
66
|
+
const executionContext = this.securityManager.createExecutionContext(context?.sessionId || `mcp-${Date.now()}`, context?.userId || "mcp-user", metadata.permissions, config?.basePath);
|
|
67
|
+
// Create MCP instance
|
|
68
|
+
const mcpInstance = await pluginManager.createInstance(name, config);
|
|
69
|
+
// Execute the MCP
|
|
70
|
+
mcpLogger.debug(`[${functionTag}] Executing ${name}`, { args });
|
|
71
|
+
const result = await mcpInstance.execute(executionContext, args);
|
|
72
|
+
mcpLogger.debug(`[${functionTag}] ${name} execution completed`, {
|
|
73
|
+
success: true,
|
|
74
|
+
resultType: typeof result,
|
|
75
|
+
});
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
mcpLogger.error(`[${functionTag}] ${name} execution failed:`, error);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Execute filesystem operations using FileSystemMCP
|
|
85
|
+
*/
|
|
86
|
+
async filesystem(operation) {
|
|
87
|
+
return this.execute("@neurolink-mcp/filesystem", { basePath: operation.basePath || process.cwd() }, {
|
|
88
|
+
operation: operation.action,
|
|
89
|
+
path: operation.path,
|
|
90
|
+
content: operation.content,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get ecosystem statistics
|
|
95
|
+
*/
|
|
96
|
+
async getStats() {
|
|
97
|
+
const available = await this.list();
|
|
98
|
+
return {
|
|
99
|
+
initialized: this.initialized,
|
|
100
|
+
pluginsDiscovered: available.length,
|
|
101
|
+
pluginsBySource: pluginManager.getDiscoveryStats(),
|
|
102
|
+
availablePlugins: available.map((p) => p.name),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create an MCP instance for direct use
|
|
107
|
+
*/
|
|
108
|
+
async createInstance(name, config) {
|
|
109
|
+
await this.ensureInitialized();
|
|
110
|
+
const metadata = await this.getMetadata(name);
|
|
111
|
+
if (!metadata) {
|
|
112
|
+
throw new Error(`MCP ${name} not found`);
|
|
113
|
+
}
|
|
114
|
+
return pluginManager.createInstance(name, config);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Create execution context for manual MCP usage
|
|
118
|
+
*/
|
|
119
|
+
createExecutionContext(sessionId, userId, permissions, basePath) {
|
|
120
|
+
return this.securityManager.createExecutionContext(sessionId, userId, permissions, basePath);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Ensure ecosystem is initialized
|
|
124
|
+
*/
|
|
125
|
+
async ensureInitialized() {
|
|
126
|
+
if (!this.initialized) {
|
|
127
|
+
await this.initialize();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get all tools formatted for AI providers
|
|
132
|
+
*/
|
|
133
|
+
async getToolsForAI() {
|
|
134
|
+
await this.ensureInitialized();
|
|
135
|
+
const plugins = await this.list();
|
|
136
|
+
const tools = {};
|
|
137
|
+
for (const plugin of plugins) {
|
|
138
|
+
// This is a simplified representation. A real implementation
|
|
139
|
+
// would parse the plugin's metadata to get tool definitions.
|
|
140
|
+
if (plugin.name.includes("filesystem")) {
|
|
141
|
+
tools["filesystem_list"] = tool({
|
|
142
|
+
description: "List files in a directory",
|
|
143
|
+
parameters: z.object({
|
|
144
|
+
path: z.string().describe("The path to list files from"),
|
|
145
|
+
}),
|
|
146
|
+
execute: async ({ path }) => this.filesystem({ action: "listFiles", path }),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return tools;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Dispose of all resources
|
|
154
|
+
*/
|
|
155
|
+
async dispose() {
|
|
156
|
+
await pluginManager.dispose();
|
|
157
|
+
this.initialized = false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Export singleton instance
|
|
161
|
+
export const mcpEcosystem = MCPEcosystem.getInstance();
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NeuroLink External MCP Client
|
|
3
|
+
* Connects to external MCP servers via stdio transport following MCP specification
|
|
4
|
+
* Bridges external tools into NeuroLink's factory pattern ecosystem
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from "events";
|
|
7
|
+
import type { NeuroLinkMCPTool, NeuroLinkExecutionContext, ToolResult } from "./factory.js";
|
|
8
|
+
/**
|
|
9
|
+
* External MCP Server Configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface ExternalMCPServerConfig {
|
|
12
|
+
name: string;
|
|
13
|
+
command: string;
|
|
14
|
+
args: string[];
|
|
15
|
+
transport: "stdio";
|
|
16
|
+
env?: Record<string, string>;
|
|
17
|
+
timeout?: number;
|
|
18
|
+
retryAttempts?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* External MCP Client for stdio transport
|
|
22
|
+
*/
|
|
23
|
+
export declare class ExternalMCPClient extends EventEmitter {
|
|
24
|
+
private config;
|
|
25
|
+
private process;
|
|
26
|
+
private isConnected;
|
|
27
|
+
private messageId;
|
|
28
|
+
private pendingRequests;
|
|
29
|
+
private tools;
|
|
30
|
+
private buffer;
|
|
31
|
+
constructor(config: ExternalMCPServerConfig);
|
|
32
|
+
/**
|
|
33
|
+
* Connect to the external MCP server
|
|
34
|
+
*/
|
|
35
|
+
connect(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Initialize MCP session
|
|
38
|
+
*/
|
|
39
|
+
private initialize;
|
|
40
|
+
/**
|
|
41
|
+
* Discover available tools from the server
|
|
42
|
+
*/
|
|
43
|
+
private discoverTools;
|
|
44
|
+
/**
|
|
45
|
+
* Execute a tool on the external server
|
|
46
|
+
*/
|
|
47
|
+
executeTool(toolName: string, params: any, context: NeuroLinkExecutionContext): Promise<ToolResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Get available tools as NeuroLink MCP tools
|
|
50
|
+
*/
|
|
51
|
+
getNeuroLinkTools(): Record<string, NeuroLinkMCPTool>;
|
|
52
|
+
/**
|
|
53
|
+
* Send a request to the MCP server
|
|
54
|
+
*/
|
|
55
|
+
private sendRequest;
|
|
56
|
+
/**
|
|
57
|
+
* Handle incoming messages from the server
|
|
58
|
+
*/
|
|
59
|
+
private handleMessage;
|
|
60
|
+
/**
|
|
61
|
+
* Process a parsed MCP message
|
|
62
|
+
*/
|
|
63
|
+
private processMessage;
|
|
64
|
+
/**
|
|
65
|
+
* Disconnect from the server
|
|
66
|
+
*/
|
|
67
|
+
disconnect(): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Check if connected
|
|
70
|
+
*/
|
|
71
|
+
isConnectedToServer(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Get server information
|
|
74
|
+
*/
|
|
75
|
+
getServerInfo(): {
|
|
76
|
+
name: string;
|
|
77
|
+
command: string;
|
|
78
|
+
args: string[];
|
|
79
|
+
transport: "stdio";
|
|
80
|
+
isConnected: boolean;
|
|
81
|
+
toolCount: number;
|
|
82
|
+
tools: string[];
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Create an external MCP client
|
|
87
|
+
*/
|
|
88
|
+
export declare function createExternalMCPClient(config: ExternalMCPServerConfig): ExternalMCPClient;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NeuroLink External MCP Client
|
|
3
|
+
* Connects to external MCP servers via stdio transport following MCP specification
|
|
4
|
+
* Bridges external tools into NeuroLink's factory pattern ecosystem
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from "child_process";
|
|
7
|
+
import { EventEmitter } from "events";
|
|
8
|
+
import { mcpLogger as logger } from "./logging.js";
|
|
9
|
+
/**
|
|
10
|
+
* External MCP Client for stdio transport
|
|
11
|
+
*/
|
|
12
|
+
export class ExternalMCPClient extends EventEmitter {
|
|
13
|
+
config;
|
|
14
|
+
process = null;
|
|
15
|
+
isConnected = false;
|
|
16
|
+
messageId = 0;
|
|
17
|
+
pendingRequests = new Map();
|
|
18
|
+
tools = new Map();
|
|
19
|
+
buffer = "";
|
|
20
|
+
constructor(config) {
|
|
21
|
+
super();
|
|
22
|
+
this.config = {
|
|
23
|
+
timeout: 30000,
|
|
24
|
+
retryAttempts: 3,
|
|
25
|
+
...config,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Connect to the external MCP server
|
|
30
|
+
*/
|
|
31
|
+
async connect() {
|
|
32
|
+
if (this.isConnected) {
|
|
33
|
+
logger.debug(`[External MCP] Already connected to ${this.config.name}`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
logger.info(`[External MCP] Connecting to ${this.config.name}...`);
|
|
38
|
+
// Spawn the MCP server process
|
|
39
|
+
this.process = spawn(this.config.command, this.config.args, {
|
|
40
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
41
|
+
env: { ...process.env, ...this.config.env },
|
|
42
|
+
});
|
|
43
|
+
if (!this.process.stdout || !this.process.stdin || !this.process.stderr) {
|
|
44
|
+
throw new Error("Failed to create stdio pipes");
|
|
45
|
+
}
|
|
46
|
+
// Setup stdout handler for receiving messages
|
|
47
|
+
this.process.stdout.on("data", (data) => {
|
|
48
|
+
this.handleMessage(data.toString());
|
|
49
|
+
});
|
|
50
|
+
// Setup stderr handler for debugging
|
|
51
|
+
this.process.stderr.on("data", (data) => {
|
|
52
|
+
logger.debug(`[External MCP] ${this.config.name} stderr:`, data.toString().trim());
|
|
53
|
+
});
|
|
54
|
+
// Handle process exit
|
|
55
|
+
this.process.on("exit", (code, signal) => {
|
|
56
|
+
logger.warn(`[External MCP] ${this.config.name} process exited`, {
|
|
57
|
+
code,
|
|
58
|
+
signal,
|
|
59
|
+
});
|
|
60
|
+
this.isConnected = false;
|
|
61
|
+
this.emit("disconnected", { code, signal });
|
|
62
|
+
});
|
|
63
|
+
// Handle process errors
|
|
64
|
+
this.process.on("error", (error) => {
|
|
65
|
+
logger.error(`[External MCP] ${this.config.name} process error:`, error);
|
|
66
|
+
this.emit("error", error);
|
|
67
|
+
});
|
|
68
|
+
// Initialize MCP session
|
|
69
|
+
await this.initialize();
|
|
70
|
+
// Discover available tools
|
|
71
|
+
await this.discoverTools();
|
|
72
|
+
this.isConnected = true;
|
|
73
|
+
logger.info(`[External MCP] Connected to ${this.config.name} with ${this.tools.size} tools`);
|
|
74
|
+
this.emit("connected");
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
logger.error(`[External MCP] Failed to connect to ${this.config.name}:`, error);
|
|
78
|
+
await this.disconnect();
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Initialize MCP session
|
|
84
|
+
*/
|
|
85
|
+
async initialize() {
|
|
86
|
+
const initResponse = await this.sendRequest("initialize", {
|
|
87
|
+
protocolVersion: "2024-11-05",
|
|
88
|
+
capabilities: {
|
|
89
|
+
tools: {},
|
|
90
|
+
},
|
|
91
|
+
clientInfo: {
|
|
92
|
+
name: "neurolink-mcp-client",
|
|
93
|
+
version: "1.0.0",
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
logger.debug(`[External MCP] ${this.config.name} initialized:`, initResponse);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Discover available tools from the server
|
|
100
|
+
*/
|
|
101
|
+
async discoverTools() {
|
|
102
|
+
try {
|
|
103
|
+
const response = await this.sendRequest("tools/list", {});
|
|
104
|
+
if (response.tools && Array.isArray(response.tools)) {
|
|
105
|
+
this.tools.clear();
|
|
106
|
+
for (const tool of response.tools) {
|
|
107
|
+
this.tools.set(tool.name, {
|
|
108
|
+
name: tool.name,
|
|
109
|
+
description: tool.description,
|
|
110
|
+
inputSchema: tool.inputSchema,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
logger.debug(`[External MCP] Discovered ${this.tools.size} tools from ${this.config.name}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
logger.warn(`[External MCP] Failed to discover tools from ${this.config.name}:`, error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Execute a tool on the external server
|
|
122
|
+
*/
|
|
123
|
+
async executeTool(toolName, params, context) {
|
|
124
|
+
if (!this.isConnected) {
|
|
125
|
+
throw new Error(`Not connected to MCP server: ${this.config.name}`);
|
|
126
|
+
}
|
|
127
|
+
const startTime = Date.now();
|
|
128
|
+
try {
|
|
129
|
+
logger.debug(`[External MCP] Executing tool ${toolName} on ${this.config.name}`);
|
|
130
|
+
const response = await this.sendRequest("tools/call", {
|
|
131
|
+
name: toolName,
|
|
132
|
+
arguments: params,
|
|
133
|
+
});
|
|
134
|
+
const executionTime = Date.now() - startTime;
|
|
135
|
+
// Transform MCP response to NeuroLink format
|
|
136
|
+
const result = {
|
|
137
|
+
success: !response.isError,
|
|
138
|
+
data: response.content || response.result || response,
|
|
139
|
+
metadata: {
|
|
140
|
+
toolName,
|
|
141
|
+
serverId: this.config.name,
|
|
142
|
+
serverTitle: this.config.name,
|
|
143
|
+
sessionId: context.sessionId,
|
|
144
|
+
timestamp: Date.now(),
|
|
145
|
+
executionTime,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
if (response.isError) {
|
|
149
|
+
result.error = response.content?.[0]?.text || "Tool execution failed";
|
|
150
|
+
}
|
|
151
|
+
logger.debug(`[External MCP] Tool ${toolName} executed in ${executionTime}ms`);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const executionTime = Date.now() - startTime;
|
|
156
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
157
|
+
logger.error(`[External MCP] Tool execution failed: ${toolName}`, error);
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
error: errorMessage,
|
|
161
|
+
metadata: {
|
|
162
|
+
toolName,
|
|
163
|
+
serverId: this.config.name,
|
|
164
|
+
serverTitle: this.config.name,
|
|
165
|
+
sessionId: context.sessionId,
|
|
166
|
+
timestamp: Date.now(),
|
|
167
|
+
executionTime,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get available tools as NeuroLink MCP tools
|
|
174
|
+
*/
|
|
175
|
+
getNeuroLinkTools() {
|
|
176
|
+
const neurolinkTools = {};
|
|
177
|
+
for (const [name, toolInfo] of this.tools) {
|
|
178
|
+
neurolinkTools[name] = {
|
|
179
|
+
name,
|
|
180
|
+
description: toolInfo.description || `External tool from ${this.config.name}`,
|
|
181
|
+
category: "external",
|
|
182
|
+
isImplemented: true,
|
|
183
|
+
execute: async (params, context) => {
|
|
184
|
+
return this.executeTool(name, params, context);
|
|
185
|
+
},
|
|
186
|
+
inputSchema: toolInfo.inputSchema,
|
|
187
|
+
metadata: {
|
|
188
|
+
serverId: this.config.name,
|
|
189
|
+
serverTitle: this.config.name,
|
|
190
|
+
external: true,
|
|
191
|
+
transport: "stdio",
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return neurolinkTools;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Send a request to the MCP server
|
|
199
|
+
*/
|
|
200
|
+
async sendRequest(method, params) {
|
|
201
|
+
return new Promise((resolve, reject) => {
|
|
202
|
+
if (!this.process?.stdin) {
|
|
203
|
+
reject(new Error("Process not available"));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const id = ++this.messageId;
|
|
207
|
+
const message = {
|
|
208
|
+
jsonrpc: "2.0",
|
|
209
|
+
id,
|
|
210
|
+
method,
|
|
211
|
+
params,
|
|
212
|
+
};
|
|
213
|
+
// Setup timeout
|
|
214
|
+
const timeout = setTimeout(() => {
|
|
215
|
+
this.pendingRequests.delete(id);
|
|
216
|
+
reject(new Error(`Request timeout: ${method}`));
|
|
217
|
+
}, this.config.timeout);
|
|
218
|
+
// Store request for response handling
|
|
219
|
+
this.pendingRequests.set(id, { resolve, reject, timeout });
|
|
220
|
+
// Send message
|
|
221
|
+
const messageStr = JSON.stringify(message) + "\n";
|
|
222
|
+
this.process.stdin.write(messageStr);
|
|
223
|
+
logger.debug(`[External MCP] Sent request ${id}: ${method}`);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Handle incoming messages from the server
|
|
228
|
+
*/
|
|
229
|
+
handleMessage(data) {
|
|
230
|
+
this.buffer += data;
|
|
231
|
+
let lineEnd;
|
|
232
|
+
while ((lineEnd = this.buffer.indexOf("\n")) !== -1) {
|
|
233
|
+
const line = this.buffer.slice(0, lineEnd);
|
|
234
|
+
this.buffer = this.buffer.slice(lineEnd + 1);
|
|
235
|
+
if (line.trim()) {
|
|
236
|
+
try {
|
|
237
|
+
const message = JSON.parse(line);
|
|
238
|
+
this.processMessage(message);
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
logger.warn(`[External MCP] Failed to parse message from ${this.config.name}:`, line);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Process a parsed MCP message
|
|
248
|
+
*/
|
|
249
|
+
processMessage(message) {
|
|
250
|
+
if (message.id !== undefined && this.pendingRequests.has(message.id)) {
|
|
251
|
+
// Response to our request
|
|
252
|
+
const pending = this.pendingRequests.get(message.id);
|
|
253
|
+
this.pendingRequests.delete(message.id);
|
|
254
|
+
clearTimeout(pending.timeout);
|
|
255
|
+
if (message.error) {
|
|
256
|
+
pending.reject(new Error(message.error.message));
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
pending.resolve(message.result);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
else if (message.method) {
|
|
263
|
+
// Notification or request from server
|
|
264
|
+
logger.debug(`[External MCP] Received notification: ${message.method}`);
|
|
265
|
+
this.emit("notification", message);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Disconnect from the server
|
|
270
|
+
*/
|
|
271
|
+
async disconnect() {
|
|
272
|
+
if (!this.isConnected && !this.process) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
logger.info(`[External MCP] Disconnecting from ${this.config.name}`);
|
|
276
|
+
// Clear pending requests
|
|
277
|
+
for (const pending of this.pendingRequests.values()) {
|
|
278
|
+
clearTimeout(pending.timeout);
|
|
279
|
+
pending.reject(new Error("Connection closed"));
|
|
280
|
+
}
|
|
281
|
+
this.pendingRequests.clear();
|
|
282
|
+
// Close process
|
|
283
|
+
if (this.process) {
|
|
284
|
+
this.process.kill("SIGTERM");
|
|
285
|
+
// Force kill after timeout
|
|
286
|
+
setTimeout(() => {
|
|
287
|
+
if (this.process && !this.process.killed) {
|
|
288
|
+
this.process.kill("SIGKILL");
|
|
289
|
+
}
|
|
290
|
+
}, 5000);
|
|
291
|
+
this.process = null;
|
|
292
|
+
}
|
|
293
|
+
this.isConnected = false;
|
|
294
|
+
this.tools.clear();
|
|
295
|
+
this.emit("disconnected");
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Check if connected
|
|
299
|
+
*/
|
|
300
|
+
isConnectedToServer() {
|
|
301
|
+
return this.isConnected && this.process !== null;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Get server information
|
|
305
|
+
*/
|
|
306
|
+
getServerInfo() {
|
|
307
|
+
return {
|
|
308
|
+
name: this.config.name,
|
|
309
|
+
command: this.config.command,
|
|
310
|
+
args: this.config.args,
|
|
311
|
+
transport: this.config.transport,
|
|
312
|
+
isConnected: this.isConnected,
|
|
313
|
+
toolCount: this.tools.size,
|
|
314
|
+
tools: Array.from(this.tools.keys()),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Create an external MCP client
|
|
320
|
+
*/
|
|
321
|
+
export function createExternalMCPClient(config) {
|
|
322
|
+
return new ExternalMCPClient(config);
|
|
323
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
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 { ExternalMCPClient } from "./external-client.js";
|
|
7
|
+
import type { NeuroLinkMCPServer, NeuroLinkExecutionContext, ToolResult } from "./factory.js";
|
|
8
|
+
import { MCPToolRegistry } from "./tool-registry.js";
|
|
9
|
+
/**
|
|
10
|
+
* External server connection info
|
|
11
|
+
*/
|
|
12
|
+
interface ExternalServerInfo {
|
|
13
|
+
name: string;
|
|
14
|
+
client: ExternalMCPClient;
|
|
15
|
+
server: NeuroLinkMCPServer;
|
|
16
|
+
status: "connecting" | "connected" | "disconnected" | "error";
|
|
17
|
+
lastError?: string;
|
|
18
|
+
toolCount: number;
|
|
19
|
+
retryCount: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* External MCP Manager
|
|
23
|
+
* Bridges external MCP servers into NeuroLink's factory pattern ecosystem
|
|
24
|
+
*/
|
|
25
|
+
export declare class ExternalMCPManager {
|
|
26
|
+
private servers;
|
|
27
|
+
private registry;
|
|
28
|
+
private configPath;
|
|
29
|
+
private config;
|
|
30
|
+
private isInitialized;
|
|
31
|
+
constructor(configPath?: string, registry?: MCPToolRegistry);
|
|
32
|
+
/**
|
|
33
|
+
* Initialize the external MCP manager
|
|
34
|
+
* Reads config and connects to all external servers
|
|
35
|
+
*/
|
|
36
|
+
initialize(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Load MCP configuration from file
|
|
39
|
+
*/
|
|
40
|
+
private loadConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Connect to a specific external server
|
|
43
|
+
*/
|
|
44
|
+
connectToServer(serverName: string): Promise<ExternalServerInfo | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Register tools from an external server with the NeuroLink registry
|
|
47
|
+
*/
|
|
48
|
+
private registerServerTools;
|
|
49
|
+
/**
|
|
50
|
+
* Get all connected external servers
|
|
51
|
+
*/
|
|
52
|
+
getConnectedServers(): ExternalServerInfo[];
|
|
53
|
+
/**
|
|
54
|
+
* Get server status
|
|
55
|
+
*/
|
|
56
|
+
getServerStatus(serverName: string): ExternalServerInfo | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Get all server statuses
|
|
59
|
+
*/
|
|
60
|
+
getAllServerStatuses(): Record<string, ExternalServerInfo>;
|
|
61
|
+
/**
|
|
62
|
+
* Execute a tool on any connected server
|
|
63
|
+
*/
|
|
64
|
+
executeTool(toolName: string, params: any, context: NeuroLinkExecutionContext): Promise<ToolResult>;
|
|
65
|
+
/**
|
|
66
|
+
* List all available tools from external servers
|
|
67
|
+
*/
|
|
68
|
+
listExternalTools(): Promise<import("./tool-registry.js").ToolInfo[]>;
|
|
69
|
+
/**
|
|
70
|
+
* Get registry instance
|
|
71
|
+
*/
|
|
72
|
+
getRegistry(): MCPToolRegistry;
|
|
73
|
+
/**
|
|
74
|
+
* Disconnect from a specific server
|
|
75
|
+
*/
|
|
76
|
+
disconnectServer(serverName: string): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Disconnect from all servers
|
|
79
|
+
*/
|
|
80
|
+
disconnectAll(): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Refresh connections - reload config and reconnect
|
|
83
|
+
*/
|
|
84
|
+
refresh(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Get comprehensive status
|
|
87
|
+
*/
|
|
88
|
+
getStatus(): {
|
|
89
|
+
isInitialized: boolean;
|
|
90
|
+
configPath: string;
|
|
91
|
+
totalServers: number;
|
|
92
|
+
connected: number;
|
|
93
|
+
connecting: number;
|
|
94
|
+
disconnected: number;
|
|
95
|
+
errors: number;
|
|
96
|
+
totalTools: number;
|
|
97
|
+
servers: Record<string, ExternalServerInfo>;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Default external MCP manager instance
|
|
102
|
+
*/
|
|
103
|
+
export declare const defaultExternalMCPManager: ExternalMCPManager;
|
|
104
|
+
/**
|
|
105
|
+
* Initialize external MCP servers with default manager
|
|
106
|
+
*/
|
|
107
|
+
export declare function initializeExternalMCP(configPath?: string): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Get the default external MCP manager
|
|
110
|
+
*/
|
|
111
|
+
export declare function getExternalMCPManager(): ExternalMCPManager;
|
|
112
|
+
export {};
|