@juspay/neurolink 5.0.0 → 5.2.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 +20 -7
- package/README.md +160 -172
- package/dist/agent/direct-tools.d.ts +6 -6
- package/dist/chat/sse-handler.js +5 -4
- package/dist/chat/websocket-chat-handler.js +9 -9
- package/dist/cli/commands/config.d.ts +3 -3
- package/dist/cli/commands/mcp.js +9 -8
- package/dist/cli/commands/ollama.js +3 -3
- package/dist/cli/factories/command-factory.d.ts +18 -0
- package/dist/cli/factories/command-factory.js +183 -0
- package/dist/cli/index.js +105 -157
- package/dist/cli/utils/interactive-setup.js +2 -2
- package/dist/core/base-provider.d.ts +423 -0
- package/dist/core/base-provider.js +365 -0
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/dynamic-models.d.ts +6 -6
- package/dist/core/evaluation.d.ts +19 -80
- package/dist/core/evaluation.js +185 -484
- package/dist/core/factory.d.ts +3 -3
- package/dist/core/factory.js +31 -91
- package/dist/core/service-registry.d.ts +47 -0
- package/dist/core/service-registry.js +112 -0
- package/dist/core/types.d.ts +49 -49
- package/dist/core/types.js +1 -0
- package/dist/factories/compatibility-factory.d.ts +20 -0
- package/dist/factories/compatibility-factory.js +69 -0
- package/dist/factories/provider-factory.d.ts +72 -0
- package/dist/factories/provider-factory.js +144 -0
- package/dist/factories/provider-generate-factory.d.ts +20 -0
- package/dist/factories/provider-generate-factory.js +87 -0
- package/dist/factories/provider-registry.d.ts +38 -0
- package/dist/factories/provider-registry.js +107 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +5 -5
- package/dist/lib/agent/direct-tools.d.ts +6 -6
- package/dist/lib/chat/sse-handler.js +5 -4
- package/dist/lib/chat/websocket-chat-handler.js +9 -9
- package/dist/lib/core/base-provider.d.ts +423 -0
- package/dist/lib/core/base-provider.js +365 -0
- package/dist/lib/core/constants.d.ts +1 -1
- package/dist/lib/core/constants.js +1 -1
- package/dist/lib/core/dynamic-models.d.ts +6 -6
- package/dist/lib/core/evaluation.d.ts +19 -80
- package/dist/lib/core/evaluation.js +185 -484
- package/dist/lib/core/factory.d.ts +3 -3
- package/dist/lib/core/factory.js +30 -91
- package/dist/lib/core/service-registry.d.ts +47 -0
- package/dist/lib/core/service-registry.js +112 -0
- package/dist/lib/core/types.d.ts +49 -49
- package/dist/lib/core/types.js +1 -0
- package/dist/lib/factories/compatibility-factory.d.ts +20 -0
- package/dist/lib/factories/compatibility-factory.js +69 -0
- package/dist/lib/factories/provider-factory.d.ts +72 -0
- package/dist/lib/factories/provider-factory.js +144 -0
- package/dist/lib/factories/provider-generate-factory.d.ts +20 -0
- package/dist/lib/factories/provider-generate-factory.js +87 -0
- package/dist/lib/factories/provider-registry.d.ts +38 -0
- package/dist/lib/factories/provider-registry.js +107 -0
- package/dist/lib/index.d.ts +8 -5
- package/dist/lib/index.js +5 -5
- package/dist/lib/mcp/client.js +5 -5
- package/dist/lib/mcp/config.js +28 -3
- package/dist/lib/mcp/dynamic-orchestrator.js +8 -8
- package/dist/lib/mcp/external-client.js +2 -2
- package/dist/lib/mcp/factory.d.ts +1 -1
- package/dist/lib/mcp/factory.js +1 -1
- package/dist/lib/mcp/function-calling.js +1 -1
- package/dist/lib/mcp/initialize-tools.d.ts +1 -1
- package/dist/lib/mcp/initialize-tools.js +45 -1
- package/dist/lib/mcp/initialize.js +16 -6
- package/dist/lib/mcp/neurolink-mcp-client.js +10 -10
- package/dist/lib/mcp/orchestrator.js +4 -4
- package/dist/lib/mcp/servers/agent/direct-tools-server.d.ts +8 -0
- package/dist/lib/mcp/servers/agent/direct-tools-server.js +109 -0
- package/dist/lib/mcp/servers/ai-providers/ai-analysis-tools.js +10 -10
- package/dist/lib/mcp/servers/ai-providers/ai-core-server.js +8 -6
- package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
- package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.js +16 -16
- package/dist/lib/mcp/unified-registry.d.ts +4 -0
- package/dist/lib/mcp/unified-registry.js +42 -9
- package/dist/lib/neurolink.d.ts +161 -174
- package/dist/lib/neurolink.js +723 -397
- package/dist/lib/providers/amazon-bedrock.d.ts +32 -0
- package/dist/lib/providers/amazon-bedrock.js +143 -0
- package/dist/lib/providers/analytics-helper.js +7 -4
- package/dist/lib/providers/anthropic-baseprovider.d.ts +23 -0
- package/dist/lib/providers/anthropic-baseprovider.js +114 -0
- package/dist/lib/providers/anthropic.d.ts +19 -39
- package/dist/lib/providers/anthropic.js +84 -378
- package/dist/lib/providers/azure-openai.d.ts +20 -0
- package/dist/lib/providers/azure-openai.js +89 -0
- package/dist/lib/providers/function-calling-provider.d.ts +14 -12
- package/dist/lib/providers/function-calling-provider.js +114 -64
- package/dist/lib/providers/google-ai-studio.d.ts +23 -0
- package/dist/lib/providers/google-ai-studio.js +107 -0
- package/dist/lib/providers/google-vertex.d.ts +47 -0
- package/dist/lib/providers/google-vertex.js +205 -0
- package/dist/lib/providers/huggingFace.d.ts +33 -27
- package/dist/lib/providers/huggingFace.js +103 -400
- package/dist/lib/providers/index.d.ts +9 -9
- package/dist/lib/providers/index.js +9 -9
- package/dist/lib/providers/mcp-provider.d.ts +13 -8
- package/dist/lib/providers/mcp-provider.js +63 -18
- package/dist/lib/providers/mistral.d.ts +42 -0
- package/dist/lib/providers/mistral.js +160 -0
- package/dist/lib/providers/ollama.d.ts +52 -35
- package/dist/lib/providers/ollama.js +297 -477
- package/dist/lib/providers/openAI.d.ts +21 -21
- package/dist/lib/providers/openAI.js +81 -245
- package/dist/lib/sdk/tool-extension.d.ts +181 -0
- package/dist/lib/sdk/tool-extension.js +283 -0
- package/dist/lib/sdk/tool-registration.d.ts +95 -0
- package/dist/lib/sdk/tool-registration.js +167 -0
- package/dist/lib/types/generate-types.d.ts +80 -0
- package/dist/lib/types/generate-types.js +1 -0
- package/dist/lib/types/mcp-types.d.ts +116 -0
- package/dist/lib/types/mcp-types.js +5 -0
- package/dist/lib/types/stream-types.d.ts +95 -0
- package/dist/lib/types/stream-types.js +1 -0
- package/dist/lib/types/universal-provider-options.d.ts +87 -0
- package/dist/lib/types/universal-provider-options.js +53 -0
- package/dist/lib/utils/providerUtils-fixed.js +1 -1
- package/dist/lib/utils/streaming-utils.d.ts +14 -2
- package/dist/lib/utils/streaming-utils.js +0 -3
- package/dist/mcp/client.js +5 -5
- package/dist/mcp/config.js +28 -3
- package/dist/mcp/dynamic-orchestrator.js +8 -8
- package/dist/mcp/external-client.js +2 -2
- package/dist/mcp/factory.d.ts +1 -1
- package/dist/mcp/factory.js +1 -1
- package/dist/mcp/function-calling.js +1 -1
- package/dist/mcp/initialize-tools.d.ts +1 -1
- package/dist/mcp/initialize-tools.js +45 -1
- package/dist/mcp/initialize.js +16 -6
- package/dist/mcp/neurolink-mcp-client.js +10 -10
- package/dist/mcp/orchestrator.js +4 -4
- package/dist/mcp/servers/agent/direct-tools-server.d.ts +8 -0
- package/dist/mcp/servers/agent/direct-tools-server.js +109 -0
- package/dist/mcp/servers/ai-providers/ai-analysis-tools.js +10 -10
- package/dist/mcp/servers/ai-providers/ai-core-server.js +8 -6
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +16 -16
- package/dist/mcp/unified-registry.d.ts +4 -0
- package/dist/mcp/unified-registry.js +42 -9
- package/dist/neurolink.d.ts +161 -174
- package/dist/neurolink.js +723 -397
- package/dist/providers/amazon-bedrock.d.ts +32 -0
- package/dist/providers/amazon-bedrock.js +143 -0
- package/dist/providers/analytics-helper.js +7 -4
- package/dist/providers/anthropic-baseprovider.d.ts +23 -0
- package/dist/providers/anthropic-baseprovider.js +114 -0
- package/dist/providers/anthropic.d.ts +19 -39
- package/dist/providers/anthropic.js +83 -377
- package/dist/providers/azure-openai.d.ts +20 -0
- package/dist/providers/azure-openai.js +89 -0
- package/dist/providers/function-calling-provider.d.ts +14 -12
- package/dist/providers/function-calling-provider.js +114 -64
- package/dist/providers/google-ai-studio.d.ts +23 -0
- package/dist/providers/google-ai-studio.js +108 -0
- package/dist/providers/google-vertex.d.ts +47 -0
- package/dist/providers/google-vertex.js +205 -0
- package/dist/providers/huggingFace.d.ts +33 -27
- package/dist/providers/huggingFace.js +102 -399
- package/dist/providers/index.d.ts +9 -9
- package/dist/providers/index.js +9 -9
- package/dist/providers/mcp-provider.d.ts +13 -8
- package/dist/providers/mcp-provider.js +63 -18
- package/dist/providers/mistral.d.ts +42 -0
- package/dist/providers/mistral.js +160 -0
- package/dist/providers/ollama.d.ts +52 -35
- package/dist/providers/ollama.js +297 -476
- package/dist/providers/openAI.d.ts +21 -21
- package/dist/providers/openAI.js +81 -246
- package/dist/sdk/tool-extension.d.ts +181 -0
- package/dist/sdk/tool-extension.js +283 -0
- package/dist/sdk/tool-registration.d.ts +95 -0
- package/dist/sdk/tool-registration.js +168 -0
- package/dist/types/generate-types.d.ts +80 -0
- package/dist/types/generate-types.js +1 -0
- package/dist/types/mcp-types.d.ts +116 -0
- package/dist/types/mcp-types.js +5 -0
- package/dist/types/stream-types.d.ts +95 -0
- package/dist/types/stream-types.js +1 -0
- package/dist/types/universal-provider-options.d.ts +87 -0
- package/dist/types/universal-provider-options.js +53 -0
- package/dist/utils/providerUtils-fixed.js +1 -1
- package/dist/utils/streaming-utils.d.ts +14 -2
- package/dist/utils/streaming-utils.js +0 -3
- package/package.json +15 -10
- package/dist/lib/providers/agent-enhanced-provider.d.ts +0 -89
- package/dist/lib/providers/agent-enhanced-provider.js +0 -614
- package/dist/lib/providers/amazonBedrock.d.ts +0 -19
- package/dist/lib/providers/amazonBedrock.js +0 -334
- package/dist/lib/providers/azureOpenAI.d.ts +0 -39
- package/dist/lib/providers/azureOpenAI.js +0 -436
- package/dist/lib/providers/googleAIStudio.d.ts +0 -49
- package/dist/lib/providers/googleAIStudio.js +0 -333
- package/dist/lib/providers/googleVertexAI.d.ts +0 -38
- package/dist/lib/providers/googleVertexAI.js +0 -519
- package/dist/lib/providers/mistralAI.d.ts +0 -34
- package/dist/lib/providers/mistralAI.js +0 -294
- package/dist/providers/agent-enhanced-provider.d.ts +0 -89
- package/dist/providers/agent-enhanced-provider.js +0 -614
- package/dist/providers/amazonBedrock.d.ts +0 -19
- package/dist/providers/amazonBedrock.js +0 -334
- package/dist/providers/azureOpenAI.d.ts +0 -39
- package/dist/providers/azureOpenAI.js +0 -437
- package/dist/providers/googleAIStudio.d.ts +0 -49
- package/dist/providers/googleAIStudio.js +0 -333
- package/dist/providers/googleVertexAI.d.ts +0 -38
- package/dist/providers/googleVertexAI.js +0 -519
- package/dist/providers/mistralAI.d.ts +0 -34
- package/dist/providers/mistralAI.js +0 -294
package/dist/neurolink.js
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NeuroLink - Unified AI Interface with Real MCP Tool Integration
|
|
3
3
|
*
|
|
4
|
+
* REDESIGNED FALLBACK CHAIN - NO CIRCULAR DEPENDENCIES
|
|
4
5
|
* Enhanced AI provider system with natural MCP tool access.
|
|
5
6
|
* Uses real MCP infrastructure for tool discovery and execution.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
+
// Load environment variables from .env file (critical for SDK usage)
|
|
9
|
+
import { config as dotenvConfig } from "dotenv";
|
|
10
|
+
try {
|
|
11
|
+
dotenvConfig(); // Load .env from current working directory
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
// Environment variables should be set externally in production
|
|
15
|
+
}
|
|
16
|
+
import { AIProviderFactory } from "./core/factory.js";
|
|
8
17
|
import { ContextManager } from "./mcp/context-manager.js";
|
|
9
18
|
import { mcpLogger } from "./mcp/logging.js";
|
|
10
19
|
import { toolRegistry } from "./mcp/tool-registry.js";
|
|
11
20
|
import { unifiedRegistry } from "./mcp/unified-registry.js";
|
|
12
21
|
import { logger } from "./utils/logger.js";
|
|
13
22
|
import { getBestProvider } from "./utils/providerUtils-fixed.js";
|
|
14
|
-
import {
|
|
23
|
+
import { ProviderRegistry } from "./factories/provider-registry.js";
|
|
24
|
+
import { validateTool, createMCPServerFromTools, } from "./sdk/tool-registration.js";
|
|
25
|
+
// Core types imported from core/types.js
|
|
15
26
|
export class NeuroLink {
|
|
16
27
|
mcpInitialized = false;
|
|
17
28
|
contextManager;
|
|
29
|
+
// Tool registration support
|
|
30
|
+
customTools = new Map();
|
|
31
|
+
inMemoryServers = new Map();
|
|
18
32
|
constructor() {
|
|
33
|
+
// SDK always disables manual MCP config for security
|
|
34
|
+
ProviderRegistry.setOptions({
|
|
35
|
+
enableManualMCP: false,
|
|
36
|
+
});
|
|
19
37
|
this.contextManager = new ContextManager();
|
|
20
38
|
}
|
|
21
39
|
/**
|
|
@@ -28,92 +46,171 @@ export class NeuroLink {
|
|
|
28
46
|
}
|
|
29
47
|
try {
|
|
30
48
|
mcpLogger.debug("[NeuroLink] Starting isolated MCP initialization...");
|
|
31
|
-
//
|
|
32
|
-
const initTimeout = 3000; // 3
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
// Initialize tool registry with timeout protection
|
|
50
|
+
const initTimeout = 3000; // 3 second timeout
|
|
51
|
+
await Promise.race([
|
|
52
|
+
Promise.resolve(), // toolRegistry doesn't need explicit initialization
|
|
35
53
|
new Promise((_, reject) => {
|
|
36
|
-
setTimeout(() =>
|
|
37
|
-
reject(new Error("MCP initialization timeout after 3s"));
|
|
38
|
-
}, initTimeout);
|
|
54
|
+
setTimeout(() => reject(new Error("MCP initialization timeout")), initTimeout);
|
|
39
55
|
}),
|
|
40
56
|
]);
|
|
41
|
-
|
|
57
|
+
// Register all providers with lazy loading support
|
|
58
|
+
await ProviderRegistry.registerAllProviders();
|
|
42
59
|
this.mcpInitialized = true;
|
|
43
|
-
mcpLogger.debug("[NeuroLink] MCP
|
|
60
|
+
mcpLogger.debug("[NeuroLink] MCP initialization completed successfully");
|
|
44
61
|
}
|
|
45
62
|
catch (error) {
|
|
46
|
-
mcpLogger.warn("[NeuroLink] MCP initialization failed
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
mcpLogger.warn("[NeuroLink] MCP initialization failed", {
|
|
64
|
+
error: error instanceof Error ? error.message : String(error),
|
|
65
|
+
});
|
|
66
|
+
// Continue without MCP - graceful degradation
|
|
49
67
|
}
|
|
50
68
|
}
|
|
51
69
|
/**
|
|
52
|
-
*
|
|
70
|
+
* MAIN ENTRY POINT: Enhanced generate method with new function signature
|
|
71
|
+
* Replaces both generateText and legacy methods
|
|
53
72
|
*/
|
|
54
|
-
async
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
mcpLogger.debug("[NeuroLink] Essential MCP tools initialized successfully");
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
mcpLogger.warn("[NeuroLink] Isolated MCP initialization failed:", error);
|
|
68
|
-
throw error;
|
|
73
|
+
async generate(optionsOrPrompt) {
|
|
74
|
+
const startTime = Date.now();
|
|
75
|
+
// Convert string prompt to full options
|
|
76
|
+
const options = typeof optionsOrPrompt === "string"
|
|
77
|
+
? { input: { text: optionsOrPrompt } }
|
|
78
|
+
: optionsOrPrompt;
|
|
79
|
+
// Validate prompt
|
|
80
|
+
if (!options.input?.text || typeof options.input.text !== "string") {
|
|
81
|
+
throw new Error("Input text is required and must be a non-empty string");
|
|
69
82
|
}
|
|
83
|
+
// Convert to internal TextGenerationOptions for compatibility
|
|
84
|
+
const textOptions = {
|
|
85
|
+
prompt: options.input.text,
|
|
86
|
+
provider: options.provider,
|
|
87
|
+
model: options.model,
|
|
88
|
+
temperature: options.temperature,
|
|
89
|
+
maxTokens: options.maxTokens,
|
|
90
|
+
systemPrompt: options.systemPrompt,
|
|
91
|
+
disableTools: options.disableTools, // FIX: Pass disableTools flag
|
|
92
|
+
// 🔧 FIX: Include analytics and evaluation options!
|
|
93
|
+
enableAnalytics: options.enableAnalytics,
|
|
94
|
+
enableEvaluation: options.enableEvaluation,
|
|
95
|
+
context: options.context,
|
|
96
|
+
evaluationDomain: options.evaluationDomain,
|
|
97
|
+
toolUsageContext: options.toolUsageContext,
|
|
98
|
+
};
|
|
99
|
+
// Use redesigned generation logic
|
|
100
|
+
const textResult = await this.generateTextInternal(textOptions);
|
|
101
|
+
// Convert back to GenerateResult
|
|
102
|
+
const generateResult = {
|
|
103
|
+
content: textResult.content,
|
|
104
|
+
provider: textResult.provider,
|
|
105
|
+
model: textResult.model,
|
|
106
|
+
usage: textResult.usage
|
|
107
|
+
? {
|
|
108
|
+
inputTokens: textResult.usage.promptTokens || 0,
|
|
109
|
+
outputTokens: textResult.usage.completionTokens || 0,
|
|
110
|
+
totalTokens: textResult.usage.totalTokens || 0,
|
|
111
|
+
}
|
|
112
|
+
: undefined,
|
|
113
|
+
responseTime: textResult.responseTime,
|
|
114
|
+
toolsUsed: textResult.toolsUsed,
|
|
115
|
+
toolExecutions: textResult.toolExecutions?.map((te) => ({
|
|
116
|
+
name: te.toolName || te.name || "",
|
|
117
|
+
input: te.input || {},
|
|
118
|
+
output: te.output || te.result,
|
|
119
|
+
duration: te.executionTime || te.duration || 0,
|
|
120
|
+
})),
|
|
121
|
+
enhancedWithTools: textResult.enhancedWithTools,
|
|
122
|
+
availableTools: textResult.availableTools?.map((tool) => ({
|
|
123
|
+
name: tool.name || "",
|
|
124
|
+
description: tool.description || "",
|
|
125
|
+
parameters: tool.parameters || {},
|
|
126
|
+
})),
|
|
127
|
+
analytics: textResult.analytics,
|
|
128
|
+
evaluation: textResult.evaluation,
|
|
129
|
+
};
|
|
130
|
+
return generateResult;
|
|
70
131
|
}
|
|
71
132
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
133
|
+
* BACKWARD COMPATIBILITY: Legacy generateText method
|
|
134
|
+
* Internally calls generate() and converts result format
|
|
74
135
|
*/
|
|
75
136
|
async generateText(options) {
|
|
76
|
-
//
|
|
77
|
-
if (!options ||
|
|
137
|
+
// Validate required parameters for backward compatibility
|
|
138
|
+
if (!options.prompt ||
|
|
78
139
|
typeof options.prompt !== "string" ||
|
|
79
140
|
options.prompt.trim() === "") {
|
|
80
|
-
throw new Error("options
|
|
81
|
-
}
|
|
82
|
-
// Tools are DEFAULT behavior unless explicitly disabled
|
|
83
|
-
if (options.disableTools === true) {
|
|
84
|
-
return this.generateTextRegular(options);
|
|
141
|
+
throw new Error("GenerateText options must include prompt as a non-empty string");
|
|
85
142
|
}
|
|
86
|
-
//
|
|
87
|
-
return this.
|
|
143
|
+
// Use internal generation method directly
|
|
144
|
+
return await this.generateTextInternal(options);
|
|
88
145
|
}
|
|
89
146
|
/**
|
|
90
|
-
*
|
|
147
|
+
* REDESIGNED INTERNAL GENERATION - NO CIRCULAR DEPENDENCIES
|
|
148
|
+
*
|
|
149
|
+
* This method implements a clean fallback chain:
|
|
150
|
+
* 1. Try MCP-enhanced generation if available
|
|
151
|
+
* 2. Fall back to direct provider generation
|
|
152
|
+
* 3. No recursive calls - each method has a specific purpose
|
|
91
153
|
*/
|
|
92
|
-
async
|
|
154
|
+
async generateTextInternal(options) {
|
|
93
155
|
const startTime = Date.now();
|
|
94
|
-
const functionTag = "NeuroLink.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const context = this.contextManager.createContext({
|
|
99
|
-
sessionId: `neurolink-${Date.now()}`,
|
|
100
|
-
userId: "neurolink-user",
|
|
101
|
-
aiProvider: options.provider || "auto",
|
|
156
|
+
const functionTag = "NeuroLink.generateTextInternal";
|
|
157
|
+
logger.debug(`[${functionTag}] Starting generation`, {
|
|
158
|
+
provider: options.provider || "auto",
|
|
159
|
+
promptLength: options.prompt?.length || 0,
|
|
102
160
|
});
|
|
103
|
-
// Determine provider to use
|
|
104
|
-
const providerName = options.provider === "auto" || !options.provider
|
|
105
|
-
? await getBestProvider()
|
|
106
|
-
: options.provider;
|
|
107
161
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
162
|
+
// Try MCP-enhanced generation first (if not explicitly disabled)
|
|
163
|
+
if (!options.disableTools) {
|
|
164
|
+
try {
|
|
165
|
+
const mcpResult = await this.tryMCPGeneration(options);
|
|
166
|
+
if (mcpResult && mcpResult.content) {
|
|
167
|
+
logger.debug(`[${functionTag}] MCP generation successful`);
|
|
168
|
+
return mcpResult;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
logger.debug(`[${functionTag}] MCP generation failed, falling back`, {
|
|
173
|
+
error: error instanceof Error ? error.message : String(error),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Fall back to direct provider generation
|
|
178
|
+
const directResult = await this.directProviderGeneration(options);
|
|
179
|
+
logger.debug(`[${functionTag}] Direct generation successful`);
|
|
180
|
+
return directResult;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
logger.error(`[${functionTag}] All generation methods failed`, {
|
|
184
|
+
error: error instanceof Error ? error.message : String(error),
|
|
112
185
|
});
|
|
113
|
-
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Try MCP-enhanced generation (no fallback recursion)
|
|
191
|
+
*/
|
|
192
|
+
async tryMCPGeneration(options) {
|
|
193
|
+
const functionTag = "NeuroLink.tryMCPGeneration";
|
|
194
|
+
const startTime = Date.now(); // 🔧 FIX: Add proper timing
|
|
195
|
+
try {
|
|
196
|
+
// Initialize MCP if needed
|
|
197
|
+
await this.initializeMCP();
|
|
198
|
+
if (!this.mcpInitialized) {
|
|
199
|
+
return null; // Skip MCP if not available
|
|
200
|
+
}
|
|
201
|
+
// Create execution context
|
|
202
|
+
const context = this.contextManager.createContext({
|
|
203
|
+
sessionId: `neurolink-${Date.now()}`,
|
|
204
|
+
userId: "neurolink-user",
|
|
205
|
+
aiProvider: options.provider || "auto",
|
|
206
|
+
});
|
|
207
|
+
// Determine provider
|
|
208
|
+
const providerName = options.provider === "auto" || !options.provider
|
|
209
|
+
? await getBestProvider()
|
|
210
|
+
: options.provider;
|
|
211
|
+
// Get available tools
|
|
114
212
|
let availableTools = [];
|
|
115
213
|
try {
|
|
116
|
-
// Use toolRegistry directly instead of unified registry to avoid hanging
|
|
117
214
|
const allTools = await toolRegistry.listTools();
|
|
118
215
|
availableTools = allTools.map((tool) => ({
|
|
119
216
|
name: tool.name,
|
|
@@ -121,85 +218,52 @@ export class NeuroLink {
|
|
|
121
218
|
server: tool.server,
|
|
122
219
|
category: tool.category,
|
|
123
220
|
}));
|
|
124
|
-
mcpLogger.debug(`[${functionTag}] Found ${availableTools.length} available tools from default registry`, {
|
|
125
|
-
tools: availableTools.map((t) => t.name),
|
|
126
|
-
});
|
|
127
221
|
}
|
|
128
222
|
catch (error) {
|
|
129
|
-
mcpLogger.warn(`[${functionTag}] Failed to get
|
|
130
|
-
error,
|
|
131
|
-
});
|
|
223
|
+
mcpLogger.warn(`[${functionTag}] Failed to get tools`, { error });
|
|
132
224
|
}
|
|
133
225
|
// Create tool-aware system prompt
|
|
134
226
|
const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
|
|
135
|
-
// Create provider
|
|
136
|
-
const provider = await AIProviderFactory.
|
|
137
|
-
|
|
138
|
-
const result = await provider.
|
|
139
|
-
|
|
140
|
-
temperature: options.temperature,
|
|
141
|
-
maxTokens: options.maxTokens,
|
|
227
|
+
// Create provider and generate
|
|
228
|
+
const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
|
|
229
|
+
this);
|
|
230
|
+
const result = await provider.generate({
|
|
231
|
+
...options,
|
|
142
232
|
systemPrompt: enhancedSystemPrompt,
|
|
143
|
-
timeout: options.timeout,
|
|
144
|
-
// NEW: Pass enhancement options
|
|
145
|
-
enableAnalytics: options.enableAnalytics,
|
|
146
|
-
enableEvaluation: options.enableEvaluation,
|
|
147
|
-
context: options.context,
|
|
148
|
-
// NEW: Lighthouse-compatible domain-aware evaluation
|
|
149
|
-
evaluationDomain: options.evaluationDomain,
|
|
150
|
-
toolUsageContext: options.toolUsageContext,
|
|
151
|
-
conversationHistory: options.conversationHistory,
|
|
152
|
-
}, options.schema);
|
|
153
|
-
if (!result) {
|
|
154
|
-
throw new Error("No response received from AI provider");
|
|
155
|
-
}
|
|
156
|
-
const responseTime = Date.now() - startTime;
|
|
157
|
-
// Extract MCP metadata if available
|
|
158
|
-
const metadata = result.metadata || {};
|
|
159
|
-
mcpLogger.debug(`[${functionTag}] MCP-enabled generation completed`, {
|
|
160
|
-
responseTime,
|
|
161
|
-
toolsUsed: metadata.toolsUsed || [],
|
|
162
|
-
enhancedWithTools: metadata.enhancedWithTools || false,
|
|
163
|
-
availableToolsCount: availableTools.length,
|
|
164
233
|
});
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
hasText: !!result.text,
|
|
170
|
-
textLength: result.text?.length || 0,
|
|
171
|
-
});
|
|
172
|
-
// Fall back to regular generation if MCP generation returns empty
|
|
173
|
-
return this.generateTextRegular(options);
|
|
234
|
+
const responseTime = Date.now() - startTime; // 🔧 FIX: Proper timing calculation
|
|
235
|
+
// Check if result is meaningful
|
|
236
|
+
if (!result || !result.content || result.content.trim().length === 0) {
|
|
237
|
+
return null; // Let caller fall back to direct generation
|
|
174
238
|
}
|
|
239
|
+
// Return enhanced result
|
|
175
240
|
return {
|
|
176
|
-
content: result.
|
|
241
|
+
content: result.content,
|
|
177
242
|
provider: providerName,
|
|
178
243
|
usage: result.usage,
|
|
179
244
|
responseTime,
|
|
180
|
-
toolsUsed:
|
|
181
|
-
enhancedWithTools:
|
|
245
|
+
toolsUsed: [],
|
|
246
|
+
enhancedWithTools: true,
|
|
182
247
|
availableTools: availableTools.length > 0 ? availableTools : undefined,
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
248
|
+
// 🔧 FIX: Include analytics and evaluation from BaseProvider
|
|
249
|
+
analytics: result.analytics,
|
|
250
|
+
evaluation: result.evaluation,
|
|
186
251
|
};
|
|
187
252
|
}
|
|
188
253
|
catch (error) {
|
|
189
|
-
|
|
190
|
-
mcpLogger.warn(`[${functionTag}] MCP generation failed, falling back to regular`, {
|
|
254
|
+
mcpLogger.warn(`[${functionTag}] MCP generation failed`, {
|
|
191
255
|
error: error instanceof Error ? error.message : String(error),
|
|
192
256
|
});
|
|
193
|
-
return
|
|
257
|
+
return null; // Let caller fall back
|
|
194
258
|
}
|
|
195
259
|
}
|
|
196
260
|
/**
|
|
197
|
-
*
|
|
261
|
+
* Direct provider generation (no MCP, no recursion)
|
|
198
262
|
*/
|
|
199
|
-
async
|
|
263
|
+
async directProviderGeneration(options) {
|
|
200
264
|
const startTime = Date.now();
|
|
201
|
-
const functionTag = "NeuroLink.
|
|
202
|
-
// Define
|
|
265
|
+
const functionTag = "NeuroLink.directProviderGeneration";
|
|
266
|
+
// Define provider priority for fallback
|
|
203
267
|
const providerPriority = [
|
|
204
268
|
"openai",
|
|
205
269
|
"vertex",
|
|
@@ -213,91 +277,55 @@ export class NeuroLink {
|
|
|
213
277
|
const requestedProvider = options.provider === "auto" ? undefined : options.provider;
|
|
214
278
|
// If specific provider requested, only use that provider (no fallback)
|
|
215
279
|
const tryProviders = requestedProvider
|
|
216
|
-
? [requestedProvider]
|
|
280
|
+
? [requestedProvider]
|
|
217
281
|
: providerPriority;
|
|
218
|
-
logger.debug(`[${functionTag}] Starting
|
|
282
|
+
logger.debug(`[${functionTag}] Starting direct generation`, {
|
|
219
283
|
requestedProvider: requestedProvider || "auto",
|
|
220
284
|
tryProviders,
|
|
221
285
|
allowFallback: !requestedProvider,
|
|
222
|
-
promptLength: options.prompt?.length || 0,
|
|
223
286
|
});
|
|
224
287
|
let lastError = null;
|
|
288
|
+
// Try each provider in order
|
|
225
289
|
for (const providerName of tryProviders) {
|
|
226
290
|
try {
|
|
227
|
-
logger.debug(`[${functionTag}] Attempting provider
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
prompt: options.prompt,
|
|
233
|
-
model: options.model,
|
|
234
|
-
temperature: options.temperature,
|
|
235
|
-
maxTokens: options.maxTokens,
|
|
236
|
-
systemPrompt: options.systemPrompt,
|
|
237
|
-
timeout: options.timeout,
|
|
238
|
-
// NEW: Pass enhancement options
|
|
239
|
-
enableAnalytics: options.enableAnalytics,
|
|
240
|
-
enableEvaluation: options.enableEvaluation,
|
|
241
|
-
context: options.context,
|
|
242
|
-
// NEW: Lighthouse-compatible domain-aware evaluation
|
|
243
|
-
evaluationDomain: options.evaluationDomain,
|
|
244
|
-
toolUsageContext: options.toolUsageContext,
|
|
245
|
-
conversationHistory: options.conversationHistory,
|
|
246
|
-
}, options.schema);
|
|
291
|
+
logger.debug(`[${functionTag}] Attempting provider: ${providerName}`);
|
|
292
|
+
const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
|
|
293
|
+
this);
|
|
294
|
+
const result = await provider.generate(options);
|
|
295
|
+
const responseTime = Date.now() - startTime;
|
|
247
296
|
if (!result) {
|
|
248
|
-
throw new Error(
|
|
297
|
+
throw new Error(`Provider ${providerName} returned null result`);
|
|
249
298
|
}
|
|
250
|
-
|
|
251
|
-
if (!result.text || result.text.trim() === "") {
|
|
252
|
-
logger.warn(`[${functionTag}] Empty response from provider`, {
|
|
253
|
-
provider: providerName,
|
|
254
|
-
hasText: !!result.text,
|
|
255
|
-
textLength: result.text?.length || 0,
|
|
256
|
-
});
|
|
257
|
-
// Continue to next provider if available
|
|
258
|
-
throw new Error(`Empty response from ${providerName}`);
|
|
259
|
-
}
|
|
260
|
-
const responseTime = Date.now() - startTime;
|
|
261
|
-
logger.debug(`[${functionTag}] Provider succeeded`, {
|
|
262
|
-
provider: providerName,
|
|
299
|
+
logger.debug(`[${functionTag}] Provider ${providerName} succeeded`, {
|
|
263
300
|
responseTime,
|
|
264
|
-
|
|
301
|
+
contentLength: result.content?.length || 0,
|
|
265
302
|
});
|
|
266
303
|
return {
|
|
267
|
-
content: result.
|
|
304
|
+
content: result.content || "",
|
|
268
305
|
provider: providerName,
|
|
306
|
+
model: result.model,
|
|
269
307
|
usage: result.usage,
|
|
270
308
|
responseTime,
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
309
|
+
toolsUsed: [],
|
|
310
|
+
enhancedWithTools: false,
|
|
311
|
+
analytics: result.analytics,
|
|
312
|
+
evaluation: result.evaluation,
|
|
274
313
|
};
|
|
275
314
|
}
|
|
276
315
|
catch (error) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (error instanceof TimeoutError) {
|
|
281
|
-
logger.warn(`[${functionTag}] Provider timed out`, {
|
|
282
|
-
provider: providerName,
|
|
283
|
-
timeout: error.timeout,
|
|
284
|
-
operation: error.operation,
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
logger.debug(`[${functionTag}] Provider failed, trying next`, {
|
|
288
|
-
provider: providerName,
|
|
289
|
-
error: errorMessage,
|
|
290
|
-
isTimeout: error instanceof TimeoutError,
|
|
291
|
-
remainingProviders: tryProviders.slice(tryProviders.indexOf(providerName) + 1),
|
|
316
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
317
|
+
logger.warn(`[${functionTag}] Provider ${providerName} failed`, {
|
|
318
|
+
error: lastError.message,
|
|
292
319
|
});
|
|
293
320
|
// Continue to next provider
|
|
294
|
-
continue;
|
|
295
321
|
}
|
|
296
322
|
}
|
|
297
323
|
// All providers failed
|
|
298
|
-
|
|
324
|
+
const responseTime = Date.now() - startTime;
|
|
325
|
+
logger.error(`[${functionTag}] All providers failed`, {
|
|
299
326
|
triedProviders: tryProviders,
|
|
300
327
|
lastError: lastError?.message,
|
|
328
|
+
responseTime,
|
|
301
329
|
});
|
|
302
330
|
throw new Error(`Failed to generate text with all providers. Last error: ${lastError?.message || "Unknown error"}`);
|
|
303
331
|
}
|
|
@@ -305,283 +333,581 @@ export class NeuroLink {
|
|
|
305
333
|
* Create tool-aware system prompt that informs AI about available tools
|
|
306
334
|
*/
|
|
307
335
|
createToolAwareSystemPrompt(originalSystemPrompt, availableTools) {
|
|
308
|
-
const basePrompt = originalSystemPrompt || "You are a helpful AI assistant.";
|
|
309
336
|
if (availableTools.length === 0) {
|
|
310
|
-
return
|
|
337
|
+
return originalSystemPrompt || "";
|
|
311
338
|
}
|
|
312
339
|
const toolDescriptions = availableTools
|
|
313
|
-
.map((tool) => `- ${tool.name}: ${tool.description}`)
|
|
340
|
+
.map((tool) => `- ${tool.name}: ${tool.description} (from ${tool.server})`)
|
|
314
341
|
.join("\n");
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
Available tools that can be used when relevant:
|
|
318
|
-
|
|
319
|
-
${toolDescriptions}
|
|
320
|
-
|
|
321
|
-
You can mention these capabilities when they're relevant to user questions. For example:
|
|
322
|
-
- For time questions: "I can get the current time"
|
|
323
|
-
- For provider questions: "I can check AI provider status"
|
|
324
|
-
- For tool questions: "I can list available tools"
|
|
325
|
-
|
|
326
|
-
Note: Tool integration is currently in development. Please provide helpful responses based on your knowledge while mentioning tool capabilities when relevant.`;
|
|
342
|
+
const toolPrompt = `\n\nAvailable Tools:\n${toolDescriptions}\n\nYou can use these tools when appropriate to enhance your responses.`;
|
|
343
|
+
return (originalSystemPrompt || "") + toolPrompt;
|
|
327
344
|
}
|
|
328
345
|
/**
|
|
329
|
-
*
|
|
346
|
+
* BACKWARD COMPATIBILITY: Legacy streamText method
|
|
347
|
+
* Internally calls stream() and converts result format
|
|
330
348
|
*/
|
|
331
|
-
async
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
349
|
+
async streamText(prompt, options) {
|
|
350
|
+
// Convert legacy format to new StreamOptions
|
|
351
|
+
const streamOptions = {
|
|
352
|
+
input: { text: prompt },
|
|
353
|
+
...options,
|
|
354
|
+
};
|
|
355
|
+
// Call the new stream method
|
|
356
|
+
const result = await this.stream(streamOptions);
|
|
357
|
+
// Convert StreamResult to simple string async iterable
|
|
358
|
+
async function* stringStream() {
|
|
359
|
+
for await (const chunk of result.stream) {
|
|
360
|
+
yield chunk.content;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return stringStream();
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* PRIMARY METHOD: Stream content using AI (recommended for new code)
|
|
367
|
+
* Future-ready for multi-modal capabilities with current text focus
|
|
368
|
+
*/
|
|
369
|
+
async stream(options) {
|
|
370
|
+
const startTime = Date.now();
|
|
371
|
+
const functionTag = "NeuroLink.stream";
|
|
372
|
+
// Validate input
|
|
373
|
+
if (!options?.input?.text ||
|
|
374
|
+
typeof options.input.text !== "string" ||
|
|
375
|
+
options.input.text.trim() === "") {
|
|
376
|
+
throw new Error("Stream options must include input.text as a non-empty string");
|
|
377
|
+
}
|
|
378
|
+
// Initialize MCP if needed
|
|
379
|
+
await this.initializeMCP();
|
|
380
|
+
// Create execution context for tool operations
|
|
381
|
+
const context = this.contextManager.createContext({
|
|
382
|
+
sessionId: `neurolink-stream-${Date.now()}`,
|
|
383
|
+
userId: "neurolink-user",
|
|
384
|
+
aiProvider: options.provider || "auto",
|
|
385
|
+
});
|
|
386
|
+
// Determine provider to use
|
|
387
|
+
const providerName = options.provider === "auto" || !options.provider
|
|
388
|
+
? await getBestProvider()
|
|
389
|
+
: options.provider;
|
|
390
|
+
try {
|
|
391
|
+
mcpLogger.debug(`[${functionTag}] Starting MCP-enabled streaming`, {
|
|
392
|
+
provider: providerName,
|
|
393
|
+
prompt: (options.input.text?.substring(0, 100) || "No text") + "...",
|
|
394
|
+
contextId: context.sessionId,
|
|
395
|
+
});
|
|
396
|
+
// Create provider using the same factory pattern as generate
|
|
397
|
+
const provider = await AIProviderFactory.createBestProvider(providerName, options.model, true, this);
|
|
398
|
+
// Call the provider's stream method directly
|
|
399
|
+
const streamResult = await provider.stream(options);
|
|
400
|
+
// Extract the stream from the result
|
|
401
|
+
const stream = streamResult.stream;
|
|
402
|
+
const responseTime = Date.now() - startTime;
|
|
403
|
+
mcpLogger.debug(`[${functionTag}] MCP-enabled streaming completed`, {
|
|
404
|
+
responseTime,
|
|
405
|
+
provider: providerName,
|
|
406
|
+
});
|
|
407
|
+
// Convert to StreamResult format
|
|
408
|
+
return {
|
|
409
|
+
stream,
|
|
410
|
+
provider: providerName,
|
|
411
|
+
model: options.model,
|
|
412
|
+
metadata: {
|
|
413
|
+
streamId: `neurolink-${Date.now()}`,
|
|
414
|
+
startTime,
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
// Fall back to regular streaming if MCP fails
|
|
420
|
+
mcpLogger.warn(`[${functionTag}] MCP streaming failed, falling back to regular`, {
|
|
421
|
+
error: error instanceof Error ? error.message : String(error),
|
|
422
|
+
});
|
|
423
|
+
// Use factory to create provider without MCP
|
|
424
|
+
const provider = await AIProviderFactory.createBestProvider(providerName, options.model, false, // Disable MCP for fallback
|
|
425
|
+
this);
|
|
426
|
+
const streamResult = await provider.stream(options);
|
|
427
|
+
const responseTime = Date.now() - startTime;
|
|
428
|
+
return {
|
|
429
|
+
stream: streamResult.stream,
|
|
430
|
+
provider: providerName,
|
|
431
|
+
model: options.model,
|
|
432
|
+
metadata: {
|
|
433
|
+
streamId: `neurolink-${Date.now()}`,
|
|
434
|
+
startTime,
|
|
435
|
+
responseTime,
|
|
436
|
+
fallback: true,
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// ========================================
|
|
442
|
+
// Tool Registration API
|
|
443
|
+
// ========================================
|
|
444
|
+
/**
|
|
445
|
+
* Register a custom tool that will be available to all AI providers
|
|
446
|
+
* @param name - Unique name for the tool
|
|
447
|
+
* @param tool - Tool configuration
|
|
448
|
+
*/
|
|
449
|
+
registerTool(name, tool) {
|
|
450
|
+
try {
|
|
451
|
+
// Validate tool configuration
|
|
452
|
+
validateTool(name, tool);
|
|
453
|
+
// Store the tool
|
|
454
|
+
this.customTools.set(name, tool);
|
|
455
|
+
// Convert to MCP server format for integration
|
|
456
|
+
const serverId = `custom-tool-${name}`;
|
|
457
|
+
const mcpServer = createMCPServerFromTools(serverId, { [name]: tool }, {
|
|
458
|
+
title: `Custom Tool: ${name}`,
|
|
459
|
+
category: "custom",
|
|
460
|
+
});
|
|
461
|
+
// Store as in-memory server
|
|
462
|
+
this.inMemoryServers.set(serverId, mcpServer);
|
|
463
|
+
logger.info(`Registered custom tool: ${name}`);
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
logger.error(`Failed to register tool ${name}:`, error);
|
|
467
|
+
throw error;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Register multiple tools at once
|
|
472
|
+
* @param tools - Object mapping tool names to configurations
|
|
473
|
+
*/
|
|
474
|
+
registerTools(tools) {
|
|
475
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
476
|
+
this.registerTool(name, tool);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Unregister a custom tool
|
|
481
|
+
* @param name - Name of the tool to remove
|
|
482
|
+
* @returns true if the tool was removed, false if it didn't exist
|
|
483
|
+
*/
|
|
484
|
+
unregisterTool(name) {
|
|
485
|
+
const removed = this.customTools.delete(name);
|
|
486
|
+
if (removed) {
|
|
487
|
+
const serverId = `custom-tool-${name}`;
|
|
488
|
+
this.inMemoryServers.delete(serverId);
|
|
489
|
+
logger.info(`Unregistered custom tool: ${name}`);
|
|
490
|
+
}
|
|
491
|
+
return removed;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Get all registered custom tools
|
|
495
|
+
* @returns Map of tool names to configurations
|
|
496
|
+
*/
|
|
497
|
+
getCustomTools() {
|
|
498
|
+
return new Map(this.customTools);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Add an in-memory MCP server (from git diff)
|
|
502
|
+
* Allows registration of pre-instantiated server objects
|
|
503
|
+
* @param serverId - Unique identifier for the server
|
|
504
|
+
* @param config - Server configuration
|
|
505
|
+
*/
|
|
506
|
+
async addInMemoryMCPServer(serverId, config) {
|
|
507
|
+
try {
|
|
508
|
+
mcpLogger.debug(`[NeuroLink] Registering in-memory MCP server: ${serverId}`);
|
|
509
|
+
// Validate server configuration
|
|
510
|
+
if (!config.server) {
|
|
511
|
+
throw new Error(`Server object is required for ${serverId}`);
|
|
512
|
+
}
|
|
513
|
+
// Store in registry
|
|
514
|
+
this.inMemoryServers.set(serverId, config);
|
|
515
|
+
mcpLogger.info(`[NeuroLink] Successfully registered in-memory server: ${serverId}`, {
|
|
516
|
+
category: config.category,
|
|
517
|
+
provider: config.metadata?.provider,
|
|
518
|
+
version: config.metadata?.version,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
catch (error) {
|
|
522
|
+
mcpLogger.error(`[NeuroLink] Failed to register in-memory server ${serverId}:`, error);
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Get all registered in-memory servers
|
|
528
|
+
* @returns Map of server IDs to configurations
|
|
529
|
+
*/
|
|
530
|
+
getInMemoryServers() {
|
|
531
|
+
return new Map(this.inMemoryServers);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Execute a specific tool by name (from git diff)
|
|
535
|
+
* Supports both custom tools and MCP server tools
|
|
536
|
+
* @param toolName - Name of the tool to execute
|
|
537
|
+
* @param params - Parameters to pass to the tool
|
|
538
|
+
* @param options - Execution options
|
|
539
|
+
* @returns Tool execution result
|
|
540
|
+
*/
|
|
541
|
+
async executeTool(toolName, params = {}, options) {
|
|
542
|
+
const functionTag = "NeuroLink.executeTool";
|
|
543
|
+
try {
|
|
544
|
+
mcpLogger.debug(`[${functionTag}] Executing tool: ${toolName}`, {
|
|
545
|
+
toolName,
|
|
546
|
+
params,
|
|
547
|
+
options,
|
|
548
|
+
});
|
|
549
|
+
// First check custom tools
|
|
550
|
+
if (this.customTools.has(toolName)) {
|
|
551
|
+
const tool = this.customTools.get(toolName);
|
|
552
|
+
const context = {
|
|
553
|
+
sessionId: `direct-execution-${Date.now()}`,
|
|
554
|
+
logger,
|
|
555
|
+
};
|
|
556
|
+
const startTime = Date.now();
|
|
557
|
+
const result = await tool.execute(params, context);
|
|
558
|
+
const executionTime = Date.now() - startTime;
|
|
559
|
+
mcpLogger.debug(`[${functionTag}] Custom tool executed successfully`, {
|
|
560
|
+
toolName,
|
|
561
|
+
executionTime,
|
|
562
|
+
});
|
|
563
|
+
return result;
|
|
564
|
+
}
|
|
565
|
+
// Then check in-memory servers
|
|
566
|
+
for (const [serverId, serverConfig] of this.inMemoryServers.entries()) {
|
|
567
|
+
const server = serverConfig.server;
|
|
568
|
+
// Check if this server has the requested tool
|
|
569
|
+
if (server && server.tools) {
|
|
570
|
+
const tool = server.tools instanceof Map
|
|
571
|
+
? server.tools.get(toolName)
|
|
572
|
+
: server.tools[toolName];
|
|
573
|
+
if (tool && typeof tool.execute === "function") {
|
|
574
|
+
mcpLogger.debug(`[${functionTag}] Found tool in in-memory server: ${serverId}`);
|
|
575
|
+
const startTime = Date.now();
|
|
576
|
+
try {
|
|
577
|
+
const result = await tool.execute(params);
|
|
578
|
+
const executionTime = Date.now() - startTime;
|
|
579
|
+
mcpLogger.debug(`[${functionTag}] Tool executed successfully`, {
|
|
580
|
+
toolName,
|
|
581
|
+
serverId,
|
|
582
|
+
executionTime,
|
|
583
|
+
});
|
|
584
|
+
// Handle MCP-style results
|
|
585
|
+
if (result && typeof result === "object" && "success" in result) {
|
|
586
|
+
if (result.success) {
|
|
587
|
+
return result.data;
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
throw new Error(result.error || "Tool execution failed");
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return result;
|
|
594
|
+
}
|
|
595
|
+
catch (toolError) {
|
|
596
|
+
mcpLogger.error(`[${functionTag}] Tool execution failed`, {
|
|
597
|
+
toolName,
|
|
598
|
+
serverId,
|
|
599
|
+
error: toolError instanceof Error
|
|
600
|
+
? toolError.message
|
|
601
|
+
: String(toolError),
|
|
602
|
+
});
|
|
603
|
+
throw toolError;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
// If not found in custom tools or in-memory servers, try unified registry
|
|
609
|
+
try {
|
|
610
|
+
// Ensure built-in tools are initialized
|
|
611
|
+
const { initializeNeuroLinkMCP, isNeuroLinkMCPInitialized } = await import("./mcp/initialize.js");
|
|
612
|
+
if (!isNeuroLinkMCPInitialized()) {
|
|
613
|
+
mcpLogger.debug(`[${functionTag}] Initializing built-in MCP servers`);
|
|
614
|
+
await initializeNeuroLinkMCP();
|
|
615
|
+
}
|
|
616
|
+
// Create minimal execution context for external tools
|
|
617
|
+
const context = this.contextManager.createContext({
|
|
618
|
+
sessionId: `neurolink-tool-${Date.now()}`,
|
|
619
|
+
userId: "neurolink-user",
|
|
620
|
+
aiProvider: "auto",
|
|
621
|
+
});
|
|
622
|
+
const result = (await unifiedRegistry.executeTool(toolName, params, context));
|
|
623
|
+
return result;
|
|
624
|
+
}
|
|
625
|
+
catch (error) {
|
|
626
|
+
mcpLogger.warn(`[${functionTag}] External tool execution failed`, {
|
|
627
|
+
toolName,
|
|
628
|
+
error: error instanceof Error ? error.message : String(error),
|
|
629
|
+
});
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
catch (error) {
|
|
634
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
635
|
+
mcpLogger.error(`[${functionTag}] Tool execution failed: ${errorMessage}`, {
|
|
636
|
+
toolName,
|
|
637
|
+
error: errorMessage,
|
|
638
|
+
});
|
|
639
|
+
throw new Error(`Failed to execute tool '${toolName}': ${errorMessage}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get all available tools including custom and in-memory ones
|
|
644
|
+
* @returns Array of available tools with metadata
|
|
645
|
+
*/
|
|
646
|
+
async getAllAvailableTools() {
|
|
647
|
+
const { getAllAvailableTools } = await import("./mcp/initialize-tools.js");
|
|
648
|
+
return getAllAvailableTools(this.inMemoryServers);
|
|
649
|
+
}
|
|
650
|
+
// ============================================================================
|
|
651
|
+
// PROVIDER DIAGNOSTICS - SDK-First Architecture
|
|
652
|
+
// ============================================================================
|
|
653
|
+
/**
|
|
654
|
+
* Get comprehensive status of all AI providers
|
|
655
|
+
* Primary method for provider health checking and diagnostics
|
|
656
|
+
*/
|
|
657
|
+
async getProviderStatus() {
|
|
658
|
+
const { AIProviderFactory } = await import("./core/factory.js");
|
|
659
|
+
const { hasProviderEnvVars } = await import("./utils/providerUtils.js");
|
|
660
|
+
const providers = [
|
|
335
661
|
"openai",
|
|
336
|
-
"vertex",
|
|
337
662
|
"bedrock",
|
|
663
|
+
"vertex",
|
|
664
|
+
"google-vertex",
|
|
338
665
|
"anthropic",
|
|
339
666
|
"azure",
|
|
340
667
|
"google-ai",
|
|
341
668
|
"huggingface",
|
|
342
669
|
"ollama",
|
|
670
|
+
"mistral",
|
|
343
671
|
];
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
tryProviders,
|
|
352
|
-
allowFallback: !requestedProvider,
|
|
353
|
-
promptLength: options.prompt.length,
|
|
354
|
-
});
|
|
355
|
-
let lastError = null;
|
|
356
|
-
for (const providerName of tryProviders) {
|
|
357
|
-
try {
|
|
358
|
-
logger.debug(`[${functionTag}] Attempting provider`, {
|
|
672
|
+
const results = [];
|
|
673
|
+
for (const providerName of providers) {
|
|
674
|
+
const startTime = Date.now();
|
|
675
|
+
// Check if provider has required environment variables
|
|
676
|
+
const hasEnvVars = await this.hasProviderEnvVars(providerName);
|
|
677
|
+
if (!hasEnvVars && providerName !== "ollama") {
|
|
678
|
+
results.push({
|
|
359
679
|
provider: providerName,
|
|
680
|
+
status: "not-configured",
|
|
681
|
+
configured: false,
|
|
682
|
+
authenticated: false,
|
|
683
|
+
error: "Missing required environment variables",
|
|
684
|
+
responseTime: 0,
|
|
360
685
|
});
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
// Special handling for Ollama
|
|
689
|
+
if (providerName === "ollama") {
|
|
690
|
+
try {
|
|
691
|
+
const response = await fetch("http://localhost:11434/api/tags", {
|
|
692
|
+
method: "GET",
|
|
693
|
+
signal: AbortSignal.timeout(2000),
|
|
694
|
+
});
|
|
695
|
+
if (!response.ok) {
|
|
696
|
+
throw new Error("Ollama service not responding");
|
|
697
|
+
}
|
|
698
|
+
const { models } = await response.json();
|
|
699
|
+
const defaultOllamaModel = "llama3.2:latest";
|
|
700
|
+
const modelIsAvailable = models.some((m) => m.name === defaultOllamaModel);
|
|
701
|
+
if (modelIsAvailable) {
|
|
702
|
+
results.push({
|
|
703
|
+
provider: providerName,
|
|
704
|
+
status: "working",
|
|
705
|
+
configured: true,
|
|
706
|
+
authenticated: true,
|
|
707
|
+
responseTime: Date.now() - startTime,
|
|
708
|
+
model: defaultOllamaModel,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
results.push({
|
|
713
|
+
provider: providerName,
|
|
714
|
+
status: "failed",
|
|
715
|
+
configured: true,
|
|
716
|
+
authenticated: false,
|
|
717
|
+
error: `Ollama service running but model '${defaultOllamaModel}' not found`,
|
|
718
|
+
responseTime: Date.now() - startTime,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
372
721
|
}
|
|
373
|
-
|
|
722
|
+
catch (error) {
|
|
723
|
+
results.push({
|
|
724
|
+
provider: providerName,
|
|
725
|
+
status: "failed",
|
|
726
|
+
configured: false,
|
|
727
|
+
authenticated: false,
|
|
728
|
+
error: error instanceof Error
|
|
729
|
+
? error.message
|
|
730
|
+
: "Ollama service not running",
|
|
731
|
+
responseTime: Date.now() - startTime,
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
// Test other providers with actual generation call
|
|
737
|
+
try {
|
|
738
|
+
const testTimeout = 5000;
|
|
739
|
+
const testPromise = this.testProviderConnection(providerName);
|
|
740
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
741
|
+
setTimeout(() => reject(new Error("Provider test timeout (5s)")), testTimeout);
|
|
742
|
+
});
|
|
743
|
+
await Promise.race([testPromise, timeoutPromise]);
|
|
744
|
+
results.push({
|
|
374
745
|
provider: providerName,
|
|
746
|
+
status: "working",
|
|
747
|
+
configured: true,
|
|
748
|
+
authenticated: true,
|
|
749
|
+
responseTime: Date.now() - startTime,
|
|
375
750
|
});
|
|
376
|
-
// Convert the AI SDK stream to our expected format
|
|
377
|
-
async function* convertStream() {
|
|
378
|
-
if (result && result.textStream) {
|
|
379
|
-
for await (const chunk of result.textStream) {
|
|
380
|
-
yield { content: chunk };
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
return convertStream();
|
|
385
751
|
}
|
|
386
752
|
catch (error) {
|
|
387
753
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
388
|
-
|
|
389
|
-
// Special handling for timeout errors
|
|
390
|
-
if (error instanceof TimeoutError) {
|
|
391
|
-
logger.warn(`[${functionTag}] Provider timed out`, {
|
|
392
|
-
provider: providerName,
|
|
393
|
-
timeout: error.timeout,
|
|
394
|
-
operation: error.operation,
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
logger.debug(`[${functionTag}] Provider failed, trying next`, {
|
|
754
|
+
results.push({
|
|
398
755
|
provider: providerName,
|
|
756
|
+
status: "failed",
|
|
757
|
+
configured: true,
|
|
758
|
+
authenticated: false,
|
|
399
759
|
error: errorMessage,
|
|
400
|
-
|
|
401
|
-
remainingProviders: tryProviders.slice(tryProviders.indexOf(providerName) + 1),
|
|
760
|
+
responseTime: Date.now() - startTime,
|
|
402
761
|
});
|
|
403
|
-
// Continue to next provider
|
|
404
|
-
continue;
|
|
405
762
|
}
|
|
406
763
|
}
|
|
407
|
-
|
|
408
|
-
logger.debug(`[${functionTag}] All providers failed`, {
|
|
409
|
-
triedProviders: tryProviders,
|
|
410
|
-
lastError: lastError?.message,
|
|
411
|
-
});
|
|
412
|
-
throw new Error(`Failed to stream text with all providers. Last error: ${lastError?.message || "Unknown error"}`);
|
|
764
|
+
return results;
|
|
413
765
|
}
|
|
414
766
|
/**
|
|
415
|
-
*
|
|
767
|
+
* Test a specific AI provider's connectivity and authentication
|
|
768
|
+
* @param providerName - Name of the provider to test
|
|
769
|
+
* @returns Promise resolving to true if provider is working
|
|
416
770
|
*/
|
|
417
|
-
async
|
|
418
|
-
return await getBestProvider();
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Test a specific provider
|
|
422
|
-
*/
|
|
423
|
-
async testProvider(providerName, testPrompt = "test") {
|
|
771
|
+
async testProvider(providerName) {
|
|
424
772
|
try {
|
|
425
|
-
|
|
426
|
-
await provider.generateText({
|
|
427
|
-
prompt: testPrompt,
|
|
428
|
-
enableAnalytics: false,
|
|
429
|
-
enableEvaluation: false,
|
|
430
|
-
});
|
|
773
|
+
await this.testProviderConnection(providerName);
|
|
431
774
|
return true;
|
|
432
775
|
}
|
|
433
|
-
catch
|
|
776
|
+
catch {
|
|
434
777
|
return false;
|
|
435
778
|
}
|
|
436
779
|
}
|
|
437
780
|
/**
|
|
438
|
-
*
|
|
781
|
+
* Internal method to test provider connection with minimal generation call
|
|
439
782
|
*/
|
|
440
|
-
|
|
441
|
-
|
|
783
|
+
async testProviderConnection(providerName) {
|
|
784
|
+
const { AIProviderFactory } = await import("./core/factory.js");
|
|
785
|
+
const provider = await AIProviderFactory.createProvider(providerName, null, false);
|
|
786
|
+
await provider.generate({
|
|
787
|
+
prompt: "test",
|
|
788
|
+
maxTokens: 1,
|
|
789
|
+
disableTools: true,
|
|
790
|
+
});
|
|
442
791
|
}
|
|
443
792
|
/**
|
|
444
|
-
*
|
|
793
|
+
* Check if a provider has required environment variables configured
|
|
794
|
+
* @param providerName - Name of the provider to check
|
|
795
|
+
* @returns True if provider has required environment variables
|
|
445
796
|
*/
|
|
446
|
-
async
|
|
447
|
-
await
|
|
448
|
-
|
|
449
|
-
const availableServers = unifiedRegistry.getAvailableServerCount();
|
|
450
|
-
const autoDiscoveredServers = unifiedRegistry.getAutoDiscoveredServers();
|
|
451
|
-
const allTools = await unifiedRegistry.listAllTools();
|
|
452
|
-
return {
|
|
453
|
-
mcpInitialized: this.mcpInitialized,
|
|
454
|
-
totalServers,
|
|
455
|
-
availableServers,
|
|
456
|
-
autoDiscoveredCount: autoDiscoveredServers.length,
|
|
457
|
-
totalTools: allTools.length,
|
|
458
|
-
autoDiscoveredServers: autoDiscoveredServers.map((server) => ({
|
|
459
|
-
id: server.metadata.name,
|
|
460
|
-
name: server.metadata.name,
|
|
461
|
-
source: server.source,
|
|
462
|
-
status: "discovered",
|
|
463
|
-
hasServer: true,
|
|
464
|
-
})),
|
|
465
|
-
};
|
|
797
|
+
async hasProviderEnvVars(providerName) {
|
|
798
|
+
const { hasProviderEnvVars } = await import("./utils/providerUtils.js");
|
|
799
|
+
return hasProviderEnvVars(providerName);
|
|
466
800
|
}
|
|
467
801
|
/**
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* tool ecosystem management. Perfect for integrating external services
|
|
472
|
-
* like Bitbucket, Slack, databases, etc.
|
|
473
|
-
*
|
|
474
|
-
* @param serverId - Unique identifier for the server (e.g., 'bitbucket', 'slack-api')
|
|
475
|
-
* @param config - Server configuration with command and execution parameters
|
|
476
|
-
* @returns Promise that resolves when server is successfully added and connected
|
|
477
|
-
*
|
|
478
|
-
* @example
|
|
479
|
-
* ```typescript
|
|
480
|
-
* // Add Bitbucket MCP server
|
|
481
|
-
* await neurolink.addMCPServer('bitbucket', {
|
|
482
|
-
* command: 'npx',
|
|
483
|
-
* args: ['-y', '@nexus2520/bitbucket-mcp-server'],
|
|
484
|
-
* env: {
|
|
485
|
-
* BITBUCKET_USERNAME: 'your-username',
|
|
486
|
-
* BITBUCKET_APP_PASSWORD: 'your-app-password'
|
|
487
|
-
* }
|
|
488
|
-
* });
|
|
489
|
-
*
|
|
490
|
-
* // Add custom database connector
|
|
491
|
-
* await neurolink.addMCPServer('database', {
|
|
492
|
-
* command: 'node',
|
|
493
|
-
* args: ['./custom-db-mcp-server.js'],
|
|
494
|
-
* env: { DB_CONNECTION_STRING: 'postgresql://...' }
|
|
495
|
-
* });
|
|
496
|
-
* ```
|
|
802
|
+
* Get the best available AI provider based on configuration and availability
|
|
803
|
+
* @param requestedProvider - Optional preferred provider name
|
|
804
|
+
* @returns Promise resolving to the best provider name
|
|
497
805
|
*/
|
|
498
|
-
async
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
command: config.command,
|
|
502
|
-
argsCount: config.args?.length || 0,
|
|
503
|
-
hasEnv: Object.keys(config.env || {}).length > 0,
|
|
504
|
-
});
|
|
505
|
-
try {
|
|
506
|
-
// Ensure MCP is initialized
|
|
507
|
-
await this.initializeMCP();
|
|
508
|
-
// Add server to unified registry with configurable transport type
|
|
509
|
-
const transportType = config.type || "stdio";
|
|
510
|
-
// Validate URL requirement for non-stdio transports
|
|
511
|
-
if ((transportType === "sse" || transportType === "http") &&
|
|
512
|
-
!config.url) {
|
|
513
|
-
throw new Error(`URL is required for ${transportType} transport. Please provide config.url for server '${serverId}'.`);
|
|
514
|
-
}
|
|
515
|
-
const transportConfig = {
|
|
516
|
-
type: transportType,
|
|
517
|
-
...(transportType === "stdio" && {
|
|
518
|
-
command: config.command,
|
|
519
|
-
args: config.args || [],
|
|
520
|
-
env: config.env || {},
|
|
521
|
-
cwd: config.cwd,
|
|
522
|
-
}),
|
|
523
|
-
...(transportType === "sse" && {
|
|
524
|
-
url: config.url,
|
|
525
|
-
headers: config.headers,
|
|
526
|
-
timeout: config.timeout,
|
|
527
|
-
}),
|
|
528
|
-
...(transportType === "http" && {
|
|
529
|
-
url: config.url,
|
|
530
|
-
headers: config.headers,
|
|
531
|
-
timeout: config.timeout,
|
|
532
|
-
}),
|
|
533
|
-
};
|
|
534
|
-
await unifiedRegistry.addExternalServer(serverId, transportConfig);
|
|
535
|
-
// Check if server is actually connected vs just registered
|
|
536
|
-
const isConnected = unifiedRegistry.isConnected(serverId);
|
|
537
|
-
if (isConnected) {
|
|
538
|
-
mcpLogger.info(`[${functionTag}] Successfully connected to MCP server: ${serverId}`);
|
|
539
|
-
}
|
|
540
|
-
else {
|
|
541
|
-
mcpLogger.info(`[${functionTag}] MCP server registered: ${serverId} (connection failed, but server available for retry)`);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
catch (error) {
|
|
545
|
-
mcpLogger.error(`[${functionTag}] Failed to add MCP server: ${serverId}`, {
|
|
546
|
-
error: error instanceof Error ? error.message : String(error),
|
|
547
|
-
});
|
|
548
|
-
const newError = new Error(`Failed to add MCP server '${serverId}': ${error instanceof Error ? error.message : String(error)}`);
|
|
549
|
-
if (error instanceof Error && error.stack) {
|
|
550
|
-
newError.stack = `${newError.stack}\nCaused by: ${error.stack}`;
|
|
551
|
-
}
|
|
552
|
-
throw newError;
|
|
553
|
-
}
|
|
806
|
+
async getBestProvider(requestedProvider) {
|
|
807
|
+
const { getBestProvider } = await import("./utils/providerUtils.js");
|
|
808
|
+
return getBestProvider(requestedProvider);
|
|
554
809
|
}
|
|
555
810
|
/**
|
|
556
|
-
*
|
|
557
|
-
* @
|
|
558
|
-
* @returns Promise resolving to text generation result
|
|
811
|
+
* Get list of all available AI provider names
|
|
812
|
+
* @returns Array of supported provider names
|
|
559
813
|
*/
|
|
560
|
-
async
|
|
561
|
-
|
|
814
|
+
async getAvailableProviders() {
|
|
815
|
+
const { getAvailableProviders } = await import("./utils/providerUtils.js");
|
|
816
|
+
return getAvailableProviders();
|
|
562
817
|
}
|
|
563
818
|
/**
|
|
564
|
-
*
|
|
565
|
-
* @param
|
|
566
|
-
* @returns
|
|
819
|
+
* Validate if a provider name is supported
|
|
820
|
+
* @param providerName - Provider name to validate
|
|
821
|
+
* @returns True if provider name is valid
|
|
567
822
|
*/
|
|
568
|
-
async
|
|
569
|
-
|
|
823
|
+
async isValidProvider(providerName) {
|
|
824
|
+
const { isValidProvider } = await import("./utils/providerUtils.js");
|
|
825
|
+
return isValidProvider(providerName);
|
|
826
|
+
}
|
|
827
|
+
// ============================================================================
|
|
828
|
+
// MCP DIAGNOSTICS - SDK-First Architecture
|
|
829
|
+
// ============================================================================
|
|
830
|
+
/**
|
|
831
|
+
* Get comprehensive MCP (Model Context Protocol) status information
|
|
832
|
+
* @returns Promise resolving to MCP status details
|
|
833
|
+
*/
|
|
834
|
+
async getMCPStatus() {
|
|
835
|
+
const { unifiedRegistry } = await import("./mcp/unified-registry.js");
|
|
836
|
+
try {
|
|
837
|
+
const totalServers = unifiedRegistry.getTotalServerCount();
|
|
838
|
+
const availableServers = unifiedRegistry.getAvailableServerCount();
|
|
839
|
+
const autoDiscoveredServers = unifiedRegistry.getAutoDiscoveredServers();
|
|
840
|
+
const allTools = await unifiedRegistry.listAllTools();
|
|
841
|
+
return {
|
|
842
|
+
mcpInitialized: this.mcpInitialized,
|
|
843
|
+
totalServers,
|
|
844
|
+
availableServers,
|
|
845
|
+
autoDiscoveredCount: autoDiscoveredServers.length,
|
|
846
|
+
totalTools: allTools.length,
|
|
847
|
+
autoDiscoveredServers: autoDiscoveredServers.map((server) => ({
|
|
848
|
+
id: server.metadata.name,
|
|
849
|
+
name: server.metadata.name,
|
|
850
|
+
source: server.source,
|
|
851
|
+
status: "discovered",
|
|
852
|
+
hasServer: true,
|
|
853
|
+
})),
|
|
854
|
+
customToolsCount: this.customTools.size,
|
|
855
|
+
inMemoryServersCount: this.inMemoryServers.size,
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
catch (error) {
|
|
859
|
+
return {
|
|
860
|
+
mcpInitialized: false,
|
|
861
|
+
totalServers: 0,
|
|
862
|
+
availableServers: 0,
|
|
863
|
+
autoDiscoveredCount: 0,
|
|
864
|
+
totalTools: 0,
|
|
865
|
+
autoDiscoveredServers: [],
|
|
866
|
+
customToolsCount: this.customTools.size,
|
|
867
|
+
inMemoryServersCount: this.inMemoryServers.size,
|
|
868
|
+
error: error instanceof Error ? error.message : String(error),
|
|
869
|
+
};
|
|
870
|
+
}
|
|
570
871
|
}
|
|
571
872
|
/**
|
|
572
|
-
*
|
|
573
|
-
* @
|
|
574
|
-
* @returns Client connection object or undefined if not connected
|
|
873
|
+
* List all configured MCP servers with their status
|
|
874
|
+
* @returns Promise resolving to array of MCP server information
|
|
575
875
|
*/
|
|
576
|
-
|
|
577
|
-
|
|
876
|
+
async listMCPServers() {
|
|
877
|
+
const { unifiedRegistry } = await import("./mcp/unified-registry.js");
|
|
878
|
+
try {
|
|
879
|
+
const servers = unifiedRegistry.getAutoDiscoveredServers();
|
|
880
|
+
return servers.map((server) => ({
|
|
881
|
+
id: server.metadata.name,
|
|
882
|
+
name: server.metadata.name,
|
|
883
|
+
source: server.source,
|
|
884
|
+
status: unifiedRegistry.isConnected(server.metadata.name)
|
|
885
|
+
? "connected"
|
|
886
|
+
: "discovered",
|
|
887
|
+
hasServer: true,
|
|
888
|
+
metadata: server.metadata,
|
|
889
|
+
}));
|
|
890
|
+
}
|
|
891
|
+
catch (error) {
|
|
892
|
+
logger.warn("Failed to list MCP servers", { error });
|
|
893
|
+
return [];
|
|
894
|
+
}
|
|
578
895
|
}
|
|
579
896
|
/**
|
|
580
|
-
*
|
|
581
|
-
* @param serverId -
|
|
582
|
-
* @returns
|
|
897
|
+
* Test connectivity to a specific MCP server
|
|
898
|
+
* @param serverId - ID of the MCP server to test
|
|
899
|
+
* @returns Promise resolving to true if server is reachable
|
|
583
900
|
*/
|
|
584
|
-
|
|
585
|
-
|
|
901
|
+
async testMCPServer(serverId) {
|
|
902
|
+
const { unifiedRegistry } = await import("./mcp/unified-registry.js");
|
|
903
|
+
try {
|
|
904
|
+
return unifiedRegistry.isConnected(serverId);
|
|
905
|
+
}
|
|
906
|
+
catch {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
586
909
|
}
|
|
587
910
|
}
|
|
911
|
+
// Create default instance
|
|
912
|
+
export const neurolink = new NeuroLink();
|
|
913
|
+
export default neurolink;
|