jezweb-mcp-core
Version:
Jezweb Model Context Protocol (MCP) Core - A universal server for providing AI tools and resources, designed for seamless integration with various AI models and clients. Features adaptable multi-provider support, comprehensive tool and resource management
423 lines • 16.1 kB
JavaScript
/**
* Provider Registry System - MVP Implementation
*
* Simplified registry for managing LLM providers with minimal complexity.
* This is a Minimum Viable Product focused on core functionality.
*
* MVP Features:
* - Basic provider registration and retrieval
* - Simple default provider selection
* - Essential configuration support
*
* Advanced features (commented out for future implementation):
* - Health monitoring, complex selection strategies, dynamic management
*/
import {
// MVP: Advanced features - implement later
// LLMProviderStatus,
// LLMProviderError,
createProviderError, } from './llm-service.js';
import { MCPError, ErrorCodes, } from '../types/index.js';
// MVP: Advanced features - implement later
// /**
// * Provider Selection Strategy
// */
// export type ProviderSelectionStrategy =
// | 'default' // Use configured default provider
// | 'priority' // Use highest priority available provider
// | 'round-robin' // Rotate between available providers
// | 'capability-based' // Select based on required capabilities
// | 'health-based'; // Select healthiest provider
// /**
// * Provider Selection Context
// */
// export interface ProviderSelectionContext {
// /** Required capabilities for the operation */
// requiredCapabilities?: string[];
// /** Preferred provider name */
// preferredProvider?: string;
// /** Selection strategy to use */
// strategy?: ProviderSelectionStrategy;
// /** Operation type (for capability-based selection) */
// operationType?: string;
// /** Additional context metadata */
// metadata?: Record<string, any>;
// }
// /**
// * Registry Statistics
// */
// export interface RegistryStatistics {
// /** Total number of registered providers */
// totalProviders: number;
// /** Number of available providers */
// availableProviders: number;
// /** Number of healthy providers */
// healthyProviders: number;
// /** Current default provider */
// defaultProvider?: string;
// /** Provider status summary */
// providerStatus: Record<string, LLMProviderStatus>;
// /** Last registry update timestamp */
// lastUpdated: Date;
// }
// /**
// * Provider Registry Events
// */
// export interface ProviderRegistryEvents {
// /** Provider registered */
// 'provider:registered': (metadata: LLMProviderMetadata) => void;
// /** Provider unregistered */
// 'provider:unregistered': (providerName: string) => void;
// /** Provider status changed */
// 'provider:status-changed': (providerName: string, status: LLMProviderStatus) => void;
// /** Default provider changed */
// 'default-provider:changed': (oldProvider?: string, newProvider?: string) => void;
// /** Registry error occurred */
// 'registry:error': (error: Error) => void;
// }
/**
* MVP Provider Registry Class
* Simplified provider management focused on core functionality
*/
export class ProviderRegistry {
providers = new Map();
factories = new Map();
configs = new Map();
// MVP: Advanced features - implement later
// private status = new Map<string, LLMProviderStatus>();
// private healthCheckIntervals = new Map<string, NodeJS.Timeout>();
// private eventListeners = new Map<keyof ProviderRegistryEvents, Function[]>();
config;
initialized = false;
// MVP: Advanced features - implement later
// private roundRobinIndex = 0;
constructor(config) {
this.config = {
defaultProvider: config.defaultProvider || '',
providers: config.providers || [],
// MVP: Advanced features - implement later
// enableHealthChecks: config.enableHealthChecks ?? true,
// healthCheckInterval: config.healthCheckInterval ?? 60000, // 1 minute
// maxRetryAttempts: config.maxRetryAttempts ?? 3,
// retryDelay: config.retryDelay ?? 5000, // 5 seconds
};
}
/**
* MVP Initialize the registry with configured providers
*/
async initialize() {
if (this.initialized) {
throw new MCPError(ErrorCodes.INTERNAL_ERROR, 'Provider registry already initialized');
}
try {
// Initialize configured providers
for (const providerConfig of this.config.providers) {
if (providerConfig.enabled !== false) {
await this.initializeProvider(providerConfig);
}
}
// Set default provider if not specified
if (!this.config.defaultProvider && this.providers.size > 0) {
const firstProvider = Array.from(this.providers.keys())[0];
this.config.defaultProvider = firstProvider;
// MVP: Advanced features - implement later
// this.emit('default-provider:changed', undefined, firstProvider);
}
// MVP: Advanced features - implement later
// // Start health checks if enabled
// if (this.config.enableHealthChecks) {
// this.startHealthChecks();
// }
this.initialized = true;
}
catch (error) {
// MVP: Advanced features - implement later
// this.emit('registry:error', error as Error);
throw new MCPError(ErrorCodes.INTERNAL_ERROR, `Failed to initialize provider registry: ${error instanceof Error ? error.message : 'Unknown error'}`, { originalError: error });
}
}
/**
* Register a provider factory
*/
registerFactory(factory) {
const metadata = factory.getMetadata();
this.factories.set(metadata.name, factory);
}
/**
* MVP Register a provider instance directly
*/
async registerProvider(provider, config) {
const metadata = provider.metadata;
// Validate provider
if (!await provider.validateConnection()) {
throw createProviderError(metadata.name, 'Provider failed connection validation', undefined, ErrorCodes.INTERNAL_ERROR);
}
// Store provider and config
this.providers.set(metadata.name, provider);
if (config) {
this.configs.set(metadata.name, config);
}
// MVP: Advanced features - implement later
// // Initialize status
// this.status.set(metadata.name, {
// name: metadata.name,
// available: true,
// healthy: true,
// lastHealthCheck: new Date(),
// });
// Set as default if none exists
if (!this.config.defaultProvider) {
this.config.defaultProvider = metadata.name;
// MVP: Advanced features - implement later
// this.emit('default-provider:changed', undefined, metadata.name);
}
// MVP: Advanced features - implement later
// this.emit('provider:registered', metadata);
}
// MVP: Advanced features - implement later
// /**
// * Unregister a provider
// */
// async unregisterProvider(providerName: string): Promise<void> {
// if (!this.providers.has(providerName)) {
// throw new MCPError(ErrorCodes.NOT_FOUND, `Provider not found: ${providerName}`);
// }
// // Stop health checks
// const interval = this.healthCheckIntervals.get(providerName);
// if (interval) {
// clearInterval(interval);
// this.healthCheckIntervals.delete(providerName);
// }
// // Remove provider data
// this.providers.delete(providerName);
// this.configs.delete(providerName);
// this.status.delete(providerName);
// // Update default provider if necessary
// if (this.config.defaultProvider === providerName) {
// const remainingProviders = Array.from(this.providers.keys());
// const newDefault = remainingProviders.length > 0 ? remainingProviders[0] : undefined;
// this.config.defaultProvider = newDefault || '';
// this.emit('default-provider:changed', providerName, newDefault);
// }
// this.emit('provider:unregistered', providerName);
// }
/**
* Get a provider by name
*/
getProvider(providerName) {
return this.providers.get(providerName);
}
/**
* Get the default provider
*/
getDefaultProvider() {
if (!this.config.defaultProvider) {
return undefined;
}
return this.providers.get(this.config.defaultProvider);
}
/**
* MVP Select a provider - simplified to default selection only
*/
selectProvider() {
// MVP: Simplified to only return default provider
const defaultProvider = this.getDefaultProvider();
if (!defaultProvider) {
// Fallback to first available provider
const availableProviders = Array.from(this.providers.values());
if (availableProviders.length === 0) {
throw new MCPError(ErrorCodes.INTERNAL_ERROR, 'No available providers');
}
return availableProviders[0];
}
return defaultProvider;
}
// MVP: Advanced features - implement later
// /**
// * Select a provider based on context and strategy
// */
// selectProvider(context: ProviderSelectionContext = {}): LLMProvider {
// const strategy = context.strategy || 'default';
// const availableProviders = this.getAvailableProviders();
// if (availableProviders.length === 0) {
// throw new MCPError(ErrorCodes.INTERNAL_ERROR, 'No available providers');
// }
// let selectedProvider: LLMProvider | undefined;
// switch (strategy) {
// case 'default':
// selectedProvider = this.getDefaultProvider();
// break;
// case 'priority':
// selectedProvider = this.selectByPriority(availableProviders);
// break;
// case 'round-robin':
// selectedProvider = this.selectRoundRobin(availableProviders);
// break;
// case 'capability-based':
// selectedProvider = this.selectByCapabilities(availableProviders, context.requiredCapabilities || []);
// break;
// case 'health-based':
// selectedProvider = this.selectByHealth(availableProviders);
// break;
// default:
// selectedProvider = this.getDefaultProvider();
// }
// // Fallback to preferred provider if specified
// if (!selectedProvider && context.preferredProvider) {
// selectedProvider = this.providers.get(context.preferredProvider);
// }
// // Final fallback to first available provider
// if (!selectedProvider) {
// selectedProvider = availableProviders[0];
// }
// if (!selectedProvider) {
// throw new MCPError(ErrorCodes.INTERNAL_ERROR, 'No suitable provider found');
// }
// return selectedProvider;
// }
// MVP: Advanced features - implement later
// /**
// * Get all available (healthy) providers
// */
// getAvailableProviders(): LLMProvider[] {
// return Array.from(this.providers.entries())
// .filter(([name]) => {
// const status = this.status.get(name);
// return status?.available && status?.healthy;
// })
// .map(([, provider]) => provider);
// }
// /**
// * Get provider status
// */
// getProviderStatus(providerName: string): LLMProviderStatus | undefined {
// return this.status.get(providerName);
// }
// /**
// * Get all provider statuses
// */
// getAllProviderStatuses(): Record<string, LLMProviderStatus> {
// const statuses: Record<string, LLMProviderStatus> = {};
// for (const [name, status] of this.status.entries()) {
// statuses[name] = status;
// }
// return statuses;
// }
// /**
// * Get registry statistics
// */
// getStatistics(): RegistryStatistics {
// const statuses = this.getAllProviderStatuses();
// const availableCount = Object.values(statuses).filter(s => s.available).length;
// const healthyCount = Object.values(statuses).filter(s => s.healthy).length;
// return {
// totalProviders: this.providers.size,
// availableProviders: availableCount,
// healthyProviders: healthyCount,
// defaultProvider: this.config.defaultProvider,
// providerStatus: statuses,
// lastUpdated: new Date(),
// };
// }
/**
* MVP Set default provider
*/
setDefaultProvider(providerName) {
if (!this.providers.has(providerName)) {
throw new MCPError(ErrorCodes.NOT_FOUND, `Provider not found: ${providerName}`);
}
this.config.defaultProvider = providerName;
// MVP: Advanced features - implement later
// const oldDefault = this.config.defaultProvider;
// this.emit('default-provider:changed', oldDefault, providerName);
}
// MVP: Advanced features - implement later
// /**
// * Event listener management
// */
// on<K extends keyof ProviderRegistryEvents>(event: K, listener: ProviderRegistryEvents[K]): void {
// if (!this.eventListeners.has(event)) {
// this.eventListeners.set(event, []);
// }
// this.eventListeners.get(event)!.push(listener);
// }
// off<K extends keyof ProviderRegistryEvents>(event: K, listener: ProviderRegistryEvents[K]): void {
// const listeners = this.eventListeners.get(event);
// if (listeners) {
// const index = listeners.indexOf(listener);
// if (index > -1) {
// listeners.splice(index, 1);
// }
// }
// }
/**
* MVP Shutdown the registry
*/
async shutdown() {
// MVP: Advanced features - implement later
// // Stop all health checks
// for (const interval of this.healthCheckIntervals.values()) {
// clearInterval(interval);
// }
// this.healthCheckIntervals.clear();
// Clear all data
this.providers.clear();
this.factories.clear();
this.configs.clear();
// MVP: Advanced features - implement later
// this.status.clear();
// this.eventListeners.clear();
this.initialized = false;
}
/**
* Private Methods
*/
async initializeProvider(config) {
const factory = this.factories.get(config.provider);
if (!factory) {
throw new MCPError(ErrorCodes.NOT_FOUND, `Provider factory not found: ${config.provider}`);
}
// Validate configuration
if (!factory.validateConfig(config.config)) {
throw new MCPError(ErrorCodes.INVALID_PARAMS, `Invalid configuration for provider: ${config.provider}`);
}
// Create and initialize provider
const provider = await factory.create(config.config);
await provider.initialize(config.config);
// Register the provider
await this.registerProvider(provider, config);
}
}
/**
* Global provider registry instance
*/
let globalRegistry;
/**
* Get or create the global provider registry
*/
export function getGlobalProviderRegistry() {
if (!globalRegistry) {
throw new MCPError(ErrorCodes.INTERNAL_ERROR, 'Global provider registry not initialized. Call initializeGlobalProviderRegistry() first.');
}
return globalRegistry;
}
/**
* Initialize the global provider registry
*/
export function initializeGlobalProviderRegistry(config) {
if (globalRegistry) {
throw new MCPError(ErrorCodes.INTERNAL_ERROR, 'Global provider registry already initialized');
}
globalRegistry = new ProviderRegistry(config);
return globalRegistry;
}
/**
* Shutdown the global provider registry
*/
export async function shutdownGlobalProviderRegistry() {
if (globalRegistry) {
await globalRegistry.shutdown();
globalRegistry = undefined;
}
}
//# sourceMappingURL=provider-registry.js.map