@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and
929 lines (928 loc) • 40.3 kB
JavaScript
/**
* NeuroLink - Unified AI Interface with Real MCP Tool Integration
*
* REDESIGNED FALLBACK CHAIN - NO CIRCULAR DEPENDENCIES
* Enhanced AI provider system with natural MCP tool access.
* Uses real MCP infrastructure for tool discovery and execution.
*/
// Load environment variables from .env file (critical for SDK usage)
import { config as dotenvConfig } from "dotenv";
try {
dotenvConfig(); // Load .env from current working directory
}
catch (error) {
// Environment variables should be set externally in production
}
import { AIProviderFactory } from "./core/factory.js";
import { mcpLogger } from "./utils/logger.js";
import { SYSTEM_LIMITS } from "./core/constants.js";
import pLimit from "p-limit";
import { toolRegistry } from "./mcp/toolRegistry.js";
import { logger } from "./utils/logger.js";
import { getBestProvider } from "./utils/providerUtils.js";
import { ProviderRegistry } from "./factories/providerRegistry.js";
import { validateTool, createMCPServerFromTools, } from "./sdk/toolRegistration.js";
// Core types imported from core/types.js
export class NeuroLink {
mcpInitialized = false;
// Tool registration support
customTools = new Map();
inMemoryServers = new Map();
constructor() {
// SDK always disables manual MCP config for security
ProviderRegistry.setOptions({
enableManualMCP: false,
});
}
/**
* Initialize MCP registry with enhanced error handling and resource cleanup
* Uses isolated async context to prevent hanging
*/
async initializeMCP() {
if (this.mcpInitialized) {
return;
}
try {
mcpLogger.debug("[NeuroLink] Starting isolated MCP initialization...");
// Initialize tool registry with timeout protection
const initTimeout = 3000; // 3 second timeout
await Promise.race([
Promise.resolve(), // toolRegistry doesn't need explicit initialization
new Promise((_, reject) => {
setTimeout(() => reject(new Error("MCP initialization timeout")), initTimeout);
}),
]);
// Register all providers with lazy loading support
await ProviderRegistry.registerAllProviders();
this.mcpInitialized = true;
mcpLogger.debug("[NeuroLink] MCP initialization completed successfully");
}
catch (error) {
mcpLogger.warn("[NeuroLink] MCP initialization failed", {
error: error instanceof Error ? error.message : String(error),
});
// Continue without MCP - graceful degradation
}
}
/**
* MAIN ENTRY POINT: Enhanced generate method with new function signature
* Replaces both generateText and legacy methods
*/
async generate(optionsOrPrompt) {
const startTime = Date.now();
// Convert string prompt to full options
const options = typeof optionsOrPrompt === "string"
? { input: { text: optionsOrPrompt } }
: optionsOrPrompt;
// Validate prompt
if (!options.input?.text || typeof options.input.text !== "string") {
throw new Error("Input text is required and must be a non-empty string");
}
// Convert to internal TextGenerationOptions for compatibility
const textOptions = {
prompt: options.input.text,
provider: options.provider,
model: options.model,
temperature: options.temperature,
maxTokens: options.maxTokens,
systemPrompt: options.systemPrompt,
disableTools: options.disableTools, // FIX: Pass disableTools flag
// 🔧 FIX: Include analytics and evaluation options!
enableAnalytics: options.enableAnalytics,
enableEvaluation: options.enableEvaluation,
context: options.context,
evaluationDomain: options.evaluationDomain,
toolUsageContext: options.toolUsageContext,
};
// Use redesigned generation logic
const textResult = await this.generateTextInternal(textOptions);
// Convert back to GenerateResult
const generateResult = {
content: textResult.content,
provider: textResult.provider,
model: textResult.model,
usage: textResult.usage
? {
inputTokens: textResult.usage.promptTokens || 0,
outputTokens: textResult.usage.completionTokens || 0,
totalTokens: textResult.usage.totalTokens || 0,
}
: undefined,
responseTime: textResult.responseTime,
toolsUsed: textResult.toolsUsed,
toolExecutions: textResult.toolExecutions?.map((te) => {
const teRecord = te;
return {
name: te.toolName || teRecord.name || "",
input: teRecord.input ||
teRecord.args ||
{},
output: teRecord.output ||
teRecord.result ||
(te.success ? "success" : "failed"),
duration: te.executionTime || teRecord.duration || 0,
};
}),
enhancedWithTools: textResult.enhancedWithTools,
availableTools: textResult.availableTools?.map((tool) => {
const toolRecord = tool;
return {
name: tool.name || "",
description: tool.description || "",
parameters: toolRecord.parameters ||
toolRecord.schema ||
{},
};
}),
analytics: textResult.analytics,
evaluation: textResult.evaluation
? {
...textResult.evaluation,
isOffTopic: textResult.evaluation
.isOffTopic ?? false,
alertSeverity: textResult.evaluation.alertSeverity ?? "none",
reasoning: textResult.evaluation.reasoning ??
"No evaluation provided",
evaluationModel: textResult.evaluation
.evaluationModel ?? "unknown",
evaluationTime: textResult.evaluation
.evaluationTime ?? Date.now(),
}
: undefined,
};
return generateResult;
}
/**
* BACKWARD COMPATIBILITY: Legacy generateText method
* Internally calls generate() and converts result format
*/
async generateText(options) {
// Validate required parameters for backward compatibility
if (!options.prompt ||
typeof options.prompt !== "string" ||
options.prompt.trim() === "") {
throw new Error("GenerateText options must include prompt as a non-empty string");
}
// Use internal generation method directly
return await this.generateTextInternal(options);
}
/**
* REDESIGNED INTERNAL GENERATION - NO CIRCULAR DEPENDENCIES
*
* This method implements a clean fallback chain:
* 1. Try MCP-enhanced generation if available
* 2. Fall back to direct provider generation
* 3. No recursive calls - each method has a specific purpose
*/
async generateTextInternal(options) {
const startTime = Date.now();
const functionTag = "NeuroLink.generateTextInternal";
logger.debug(`[${functionTag}] Starting generation`, {
provider: options.provider || "auto",
promptLength: options.prompt?.length || 0,
});
try {
// Try MCP-enhanced generation first (if not explicitly disabled)
if (!options.disableTools) {
try {
const mcpResult = await this.tryMCPGeneration(options);
if (mcpResult && mcpResult.content) {
logger.debug(`[${functionTag}] MCP generation successful`);
return mcpResult;
}
}
catch (error) {
logger.debug(`[${functionTag}] MCP generation failed, falling back`, {
error: error instanceof Error ? error.message : String(error),
});
}
}
// Fall back to direct provider generation
const directResult = await this.directProviderGeneration(options);
logger.debug(`[${functionTag}] Direct generation successful`);
return directResult;
}
catch (error) {
logger.error(`[${functionTag}] All generation methods failed`, {
error: error instanceof Error ? error.message : String(error),
});
throw error;
}
}
/**
* Try MCP-enhanced generation (no fallback recursion)
*/
async tryMCPGeneration(options) {
const functionTag = "NeuroLink.tryMCPGeneration";
const startTime = Date.now(); // 🔧 FIX: Add proper timing
try {
// Initialize MCP if needed
await this.initializeMCP();
if (!this.mcpInitialized) {
return null; // Skip MCP if not available
}
// Context creation removed - was never used
// Determine provider
const providerName = options.provider === "auto" || !options.provider
? await getBestProvider()
: options.provider;
// Get available tools
let availableTools = [];
try {
const allTools = await toolRegistry.listTools();
availableTools = allTools.map((tool) => ({
name: tool.name || "Unknown",
description: tool.description || "No description available",
server: tool.server || "Unknown",
category: tool.category,
}));
}
catch (error) {
mcpLogger.warn(`[${functionTag}] Failed to get tools`, { error });
}
// Create tool-aware system prompt
const enhancedSystemPrompt = this.createToolAwareSystemPrompt(options.systemPrompt, availableTools);
// Create provider and generate
const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
this);
const result = await provider.generate({
...options,
systemPrompt: enhancedSystemPrompt,
});
const responseTime = Date.now() - startTime; // 🔧 FIX: Proper timing calculation
// Check if result is meaningful
if (!result || !result.content || result.content.trim().length === 0) {
return null; // Let caller fall back to direct generation
}
// Return enhanced result
return {
content: result.content,
provider: providerName,
usage: result.usage,
responseTime,
toolsUsed: result.toolsUsed || [],
enhancedWithTools: true,
availableTools: availableTools.length > 0 ? availableTools : undefined,
// Include analytics and evaluation from BaseProvider
analytics: result.analytics,
evaluation: result.evaluation,
};
}
catch (error) {
mcpLogger.warn(`[${functionTag}] MCP generation failed`, {
error: error instanceof Error ? error.message : String(error),
});
return null; // Let caller fall back
}
}
/**
* Direct provider generation (no MCP, no recursion)
*/
async directProviderGeneration(options) {
const startTime = Date.now();
const functionTag = "NeuroLink.directProviderGeneration";
// Define provider priority for fallback
const providerPriority = [
"openai",
"vertex",
"bedrock",
"anthropic",
"azure",
"google-ai",
"huggingface",
"ollama",
];
const requestedProvider = options.provider === "auto" ? undefined : options.provider;
// If specific provider requested, only use that provider (no fallback)
const tryProviders = requestedProvider
? [requestedProvider]
: providerPriority;
logger.debug(`[${functionTag}] Starting direct generation`, {
requestedProvider: requestedProvider || "auto",
tryProviders,
allowFallback: !requestedProvider,
});
let lastError = null;
// Try each provider in order
for (const providerName of tryProviders) {
try {
logger.debug(`[${functionTag}] Attempting provider: ${providerName}`);
const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
this);
const result = await provider.generate(options);
const responseTime = Date.now() - startTime;
if (!result) {
throw new Error(`Provider ${providerName} returned null result`);
}
logger.debug(`[${functionTag}] Provider ${providerName} succeeded`, {
responseTime,
contentLength: result.content?.length || 0,
});
return {
content: result.content || "",
provider: providerName,
model: result.model,
usage: result.usage,
responseTime,
toolsUsed: result.toolsUsed || [],
enhancedWithTools: false,
analytics: result.analytics,
evaluation: result.evaluation,
};
}
catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
logger.warn(`[${functionTag}] Provider ${providerName} failed`, {
error: lastError.message,
});
// Continue to next provider
}
}
// All providers failed
const responseTime = Date.now() - startTime;
logger.error(`[${functionTag}] All providers failed`, {
triedProviders: tryProviders,
lastError: lastError?.message,
responseTime,
});
throw new Error(`Failed to generate text with all providers. Last error: ${lastError?.message || "Unknown error"}`);
}
/**
* Create tool-aware system prompt that informs AI about available tools
*/
createToolAwareSystemPrompt(originalSystemPrompt, availableTools) {
if (availableTools.length === 0) {
return originalSystemPrompt || "";
}
const toolDescriptions = availableTools
.map((tool) => `- ${tool.name}: ${tool.description} (from ${tool.server})`)
.join("\n");
const toolPrompt = `\n\nYou have access to these additional tools if needed:\n${toolDescriptions}\n\nIMPORTANT: You are a general-purpose AI assistant. Answer all requests directly and creatively. These tools are optional helpers - use them only when they would genuinely improve your response. For creative tasks like storytelling, writing, or general conversation, respond naturally without requiring tools.`;
return (originalSystemPrompt || "") + toolPrompt;
}
/**
* BACKWARD COMPATIBILITY: Legacy streamText method
* Internally calls stream() and converts result format
*/
async streamText(prompt, options) {
// Convert legacy format to new StreamOptions
const streamOptions = {
input: { text: prompt },
...options,
};
// Call the new stream method
const result = await this.stream(streamOptions);
// Convert StreamResult to simple string async iterable
async function* stringStream() {
for await (const chunk of result.stream) {
yield chunk.content;
}
}
return stringStream();
}
/**
* PRIMARY METHOD: Stream content using AI (recommended for new code)
* Future-ready for multi-modal capabilities with current text focus
*/
async stream(options) {
const startTime = Date.now();
const functionTag = "NeuroLink.stream";
// Validate input
if (!options?.input?.text ||
typeof options.input.text !== "string" ||
options.input.text.trim() === "") {
throw new Error("Stream options must include input.text as a non-empty string");
}
// Initialize MCP if needed
await this.initializeMCP();
// Context creation removed - was never used
// Determine provider to use
const providerName = options.provider === "auto" || !options.provider
? await getBestProvider()
: options.provider;
try {
mcpLogger.debug(`[${functionTag}] Starting MCP-enabled streaming`, {
provider: providerName,
prompt: (options.input.text?.substring(0, 100) || "No text") + "...",
});
// Create provider using the same factory pattern as generate
const provider = await AIProviderFactory.createBestProvider(providerName, options.model, true, this);
// Call the provider's stream method directly
const streamResult = await provider.stream(options);
// Extract the stream from the result
const stream = streamResult.stream;
const responseTime = Date.now() - startTime;
mcpLogger.debug(`[${functionTag}] MCP-enabled streaming completed`, {
responseTime,
provider: providerName,
});
// Convert to StreamResult format - Include analytics and evaluation from provider
return {
stream,
provider: providerName,
model: options.model,
usage: streamResult.usage,
finishReason: streamResult.finishReason,
toolCalls: streamResult.toolCalls,
toolResults: streamResult.toolResults,
analytics: streamResult.analytics, // 🔧 FIX: Pass through analytics data
evaluation: streamResult.evaluation, // 🔧 FIX: Pass through evaluation data
metadata: {
streamId: `neurolink-${Date.now()}`,
startTime,
responseTime,
},
};
}
catch (error) {
// Fall back to regular streaming if MCP fails
mcpLogger.warn(`[${functionTag}] MCP streaming failed, falling back to regular`, {
error: error instanceof Error ? error.message : String(error),
});
// Use factory to create provider without MCP
const provider = await AIProviderFactory.createBestProvider(providerName, options.model, false, // Disable MCP for fallback
this);
const streamResult = await provider.stream(options);
const responseTime = Date.now() - startTime;
return {
stream: streamResult.stream,
provider: providerName,
model: options.model,
usage: streamResult.usage,
finishReason: streamResult.finishReason,
toolCalls: streamResult.toolCalls,
toolResults: streamResult.toolResults,
analytics: streamResult.analytics, // 🔧 FIX: Pass through analytics data in fallback
evaluation: streamResult.evaluation, // 🔧 FIX: Pass through evaluation data in fallback
metadata: {
streamId: `neurolink-${Date.now()}`,
startTime,
responseTime,
fallback: true,
},
};
}
}
// ========================================
// Tool Registration API
// ========================================
/**
* Register a custom tool that will be available to all AI providers
* @param name - Unique name for the tool
* @param tool - Tool configuration
*/
registerTool(name, tool) {
try {
// Validate tool configuration
validateTool(name, tool);
// Store the tool
this.customTools.set(name, tool);
// Convert to MCP server format for integration
const serverId = `custom-tool-${name}`;
const mcpServer = createMCPServerFromTools(serverId, { [name]: tool }, {
title: `Custom Tool: ${name}`,
category: "custom",
});
// Store as in-memory server
this.inMemoryServers.set(serverId, mcpServer);
logger.info(`Registered custom tool: ${name}`);
}
catch (error) {
logger.error(`Failed to register tool ${name}:`, error);
throw error;
}
}
/**
* Register multiple tools at once
* @param tools - Object mapping tool names to configurations
*/
registerTools(tools) {
for (const [name, tool] of Object.entries(tools)) {
this.registerTool(name, tool);
}
}
/**
* Unregister a custom tool
* @param name - Name of the tool to remove
* @returns true if the tool was removed, false if it didn't exist
*/
unregisterTool(name) {
const removed = this.customTools.delete(name);
if (removed) {
const serverId = `custom-tool-${name}`;
this.inMemoryServers.delete(serverId);
logger.info(`Unregistered custom tool: ${name}`);
}
return removed;
}
/**
* Get all registered custom tools
* @returns Map of tool names to configurations
*/
getCustomTools() {
return new Map(this.customTools);
}
/**
* Add an in-memory MCP server (from git diff)
* Allows registration of pre-instantiated server objects
* @param serverId - Unique identifier for the server
* @param config - Server configuration
*/
async addInMemoryMCPServer(serverId, config) {
try {
mcpLogger.debug(`[NeuroLink] Registering in-memory MCP server: ${serverId}`);
// Validate server configuration
if (!config.server) {
throw new Error(`Server object is required for ${serverId}`);
}
// Store in registry
this.inMemoryServers.set(serverId, config);
mcpLogger.info(`[NeuroLink] Successfully registered in-memory server: ${serverId}`, {
category: config.category,
provider: config.metadata?.provider,
version: config.metadata?.version,
});
}
catch (error) {
mcpLogger.error(`[NeuroLink] Failed to register in-memory server ${serverId}:`, error);
throw error;
}
}
/**
* Get all registered in-memory servers
* @returns Map of server IDs to configurations
*/
getInMemoryServers() {
return new Map(this.inMemoryServers);
}
/**
* Execute a specific tool by name (from git diff)
* Supports both custom tools and MCP server tools
* @param toolName - Name of the tool to execute
* @param params - Parameters to pass to the tool
* @param options - Execution options
* @returns Tool execution result
*/
async executeTool(toolName, params = {}, options) {
const functionTag = "NeuroLink.executeTool";
try {
mcpLogger.debug(`[${functionTag}] Executing tool: ${toolName}`, {
toolName,
params,
options,
});
// First check custom tools
if (this.customTools.has(toolName)) {
const tool = this.customTools.get(toolName);
const context = {
sessionId: `direct-execution-${Date.now()}`,
logger,
};
const startTime = Date.now();
const result = await tool.execute(params, context);
const executionTime = Date.now() - startTime;
mcpLogger.debug(`[${functionTag}] Custom tool executed successfully`, {
toolName,
executionTime,
});
return result;
}
// Then check in-memory servers
for (const [serverId, serverConfig] of this.inMemoryServers.entries()) {
const server = serverConfig.server;
// Check if this server has the requested tool
if (server && server.tools) {
const tool = server.tools instanceof Map
? server.tools.get(toolName)
: server.tools[toolName];
if (tool && typeof tool.execute === "function") {
mcpLogger.debug(`[${functionTag}] Found tool in in-memory server: ${serverId}`);
const startTime = Date.now();
try {
const result = await tool.execute(params);
const executionTime = Date.now() - startTime;
mcpLogger.debug(`[${functionTag}] Tool executed successfully`, {
toolName,
serverId,
executionTime,
});
// Handle MCP-style results
if (result && typeof result === "object" && "success" in result) {
if (result.success) {
return result.data;
}
else {
throw new Error(result.error || "Tool execution failed");
}
}
return result;
}
catch (toolError) {
mcpLogger.error(`[${functionTag}] Tool execution failed`, {
toolName,
serverId,
error: toolError instanceof Error
? toolError.message
: String(toolError),
});
throw toolError;
}
}
}
}
// If not found in custom tools or in-memory servers, try unified registry
try {
// Built-in tools initialization simplified
mcpLogger.debug(`[${functionTag}] MCP initialization simplified`);
// Create minimal execution context for external tools
const context = {
sessionId: `neurolink-tool-${Date.now()}`,
userId: "neurolink-user",
};
const result = (await toolRegistry.executeTool(toolName, params, context));
return result;
}
catch (error) {
mcpLogger.warn(`[${functionTag}] External tool execution failed`, {
toolName,
error: error instanceof Error ? error.message : String(error),
});
throw error;
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
mcpLogger.error(`[${functionTag}] Tool execution failed: ${errorMessage}`, {
toolName,
error: errorMessage,
});
throw new Error(`Failed to execute tool '${toolName}': ${errorMessage}`);
}
}
/**
* Get all available tools including custom and in-memory ones
* @returns Array of available tools with metadata
*/
async getAllAvailableTools() {
// MCP registry already includes direct tools, so just return MCP tools
// This prevents duplication since direct tools are auto-registered in MCP
const mcpTools = await toolRegistry.listTools();
return mcpTools;
}
// ============================================================================
// PROVIDER DIAGNOSTICS - SDK-First Architecture
// ============================================================================
/**
* Get comprehensive status of all AI providers
* Primary method for provider health checking and diagnostics
*/
async getProviderStatus(options) {
// 🔧 PERFORMANCE: Track memory and timing for provider status checks
const { MemoryManager } = await import("./utils/performance.js");
const startMemory = MemoryManager.getMemoryUsageMB();
// CRITICAL FIX: Ensure providers are registered before testing
if (!options?.quiet) {
mcpLogger.debug("🔍 DEBUG: Initializing MCP for provider status...");
}
await this.initializeMCP();
if (!options?.quiet) {
mcpLogger.debug("🔍 DEBUG: MCP initialized:", this.mcpInitialized);
}
const { AIProviderFactory } = await import("./core/factory.js");
const { hasProviderEnvVars } = await import("./utils/providerUtils.js");
const providers = [
"openai",
"bedrock",
"vertex",
"googleVertex",
"anthropic",
"azure",
"google-ai",
"huggingface",
"ollama",
"mistral",
];
// 🚀 PERFORMANCE FIX: Test providers with controlled concurrency
// This reduces total time from 16s (sequential) to ~3s (parallel) while preventing resource exhaustion
const limit = pLimit(SYSTEM_LIMITS.DEFAULT_CONCURRENCY_LIMIT);
const providerTests = providers.map((providerName) => limit(async () => {
const startTime = Date.now();
try {
// Check if provider has required environment variables
const hasEnvVars = await this.hasProviderEnvVars(providerName);
if (!hasEnvVars && providerName !== "ollama") {
return {
provider: providerName,
status: "not-configured",
configured: false,
authenticated: false,
error: "Missing required environment variables",
responseTime: Date.now() - startTime,
};
}
// Special handling for Ollama
if (providerName === "ollama") {
try {
const response = await fetch("http://localhost:11434/api/tags", {
method: "GET",
signal: AbortSignal.timeout(2000),
});
if (!response.ok) {
throw new Error("Ollama service not responding");
}
const { models } = await response.json();
const defaultOllamaModel = "llama3.2:latest";
const modelIsAvailable = models.some((m) => m.name === defaultOllamaModel);
if (modelIsAvailable) {
return {
provider: providerName,
status: "working",
configured: true,
authenticated: true,
responseTime: Date.now() - startTime,
model: defaultOllamaModel,
};
}
else {
return {
provider: providerName,
status: "failed",
configured: true,
authenticated: false,
error: `Ollama service running but model '${defaultOllamaModel}' not found`,
responseTime: Date.now() - startTime,
};
}
}
catch (error) {
return {
provider: providerName,
status: "failed",
configured: false,
authenticated: false,
error: error instanceof Error
? error.message
: "Ollama service not running",
responseTime: Date.now() - startTime,
};
}
}
// Test other providers with actual generation call
const testTimeout = 5000;
const testPromise = this.testProviderConnection(providerName);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error("Provider test timeout (5s)")), testTimeout);
});
await Promise.race([testPromise, timeoutPromise]);
return {
provider: providerName,
status: "working",
configured: true,
authenticated: true,
responseTime: Date.now() - startTime,
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
provider: providerName,
status: "failed",
configured: true,
authenticated: false,
error: errorMessage,
responseTime: Date.now() - startTime,
};
}
}));
// Wait for all provider tests to complete in parallel
const results = await Promise.all(providerTests);
// 🔧 PERFORMANCE: Track memory usage and suggest cleanup if needed
const endMemory = MemoryManager.getMemoryUsageMB();
const memoryDelta = endMemory.heapUsed - startMemory.heapUsed;
if (!options?.quiet && memoryDelta > 20) {
mcpLogger.debug(`🔍 Memory usage: +${memoryDelta}MB (consider cleanup for large operations)`);
}
// Suggest garbage collection for large memory increases
if (memoryDelta > 50) {
MemoryManager.forceGC();
}
return results;
}
/**
* Test a specific AI provider's connectivity and authentication
* @param providerName - Name of the provider to test
* @returns Promise resolving to true if provider is working
*/
async testProvider(providerName) {
try {
await this.testProviderConnection(providerName);
return true;
}
catch {
return false;
}
}
/**
* Internal method to test provider connection with minimal generation call
*/
async testProviderConnection(providerName) {
const { AIProviderFactory } = await import("./core/factory.js");
const provider = await AIProviderFactory.createProvider(providerName, null, false);
await provider.generate({
prompt: "test",
maxTokens: 1,
disableTools: true,
});
}
/**
* Check if a provider has required environment variables configured
* @param providerName - Name of the provider to check
* @returns True if provider has required environment variables
*/
async hasProviderEnvVars(providerName) {
const { hasProviderEnvVars } = await import("./utils/providerUtils.js");
return hasProviderEnvVars(providerName);
}
/**
* Get the best available AI provider based on configuration and availability
* @param requestedProvider - Optional preferred provider name
* @returns Promise resolving to the best provider name
*/
async getBestProvider(requestedProvider) {
const { getBestProvider } = await import("./utils/providerUtils.js");
return getBestProvider(requestedProvider);
}
/**
* Get list of all available AI provider names
* @returns Array of supported provider names
*/
async getAvailableProviders() {
const { getAvailableProviders } = await import("./utils/providerUtils.js");
return getAvailableProviders();
}
/**
* Validate if a provider name is supported
* @param providerName - Provider name to validate
* @returns True if provider name is valid
*/
async isValidProvider(providerName) {
const { isValidProvider } = await import("./utils/providerUtils.js");
return isValidProvider(providerName);
}
// ============================================================================
// MCP DIAGNOSTICS - SDK-First Architecture
// ============================================================================
/**
* Get comprehensive MCP (Model Context Protocol) status information
* @returns Promise resolving to MCP status details
*/
async getMCPStatus() {
try {
// Simplified MCP status - unified registry removed
const allTools = await toolRegistry.listTools();
return {
mcpInitialized: this.mcpInitialized,
totalServers: 1, // Only tool registry now
availableServers: 1,
autoDiscoveredCount: 0, // No auto-discovery
totalTools: allTools.length,
autoDiscoveredServers: [], // No auto-discovery
customToolsCount: this.customTools.size,
inMemoryServersCount: this.inMemoryServers.size,
};
}
catch (error) {
return {
mcpInitialized: false,
totalServers: 0,
availableServers: 0,
autoDiscoveredCount: 0,
totalTools: 0,
autoDiscoveredServers: [],
customToolsCount: this.customTools.size,
inMemoryServersCount: this.inMemoryServers.size,
error: error instanceof Error ? error.message : String(error),
};
}
}
/**
* List all configured MCP servers with their status
* @returns Promise resolving to array of MCP server information
*/
async listMCPServers() {
// Simplified MCP servers listing - unified registry removed
return [];
}
/**
* Test connectivity to a specific MCP server
* @param serverId - ID of the MCP server to test
* @returns Promise resolving to true if server is reachable
*/
async testMCPServer(serverId) {
// Simplified MCP server testing - unified registry removed
return false; // No auto-discovery servers available
}
}
// Create default instance
export const neurolink = new NeuroLink();
export default neurolink;