Skip to main content
Glama
version.ts26.1 kB
/** * Version tool for MCP server * * Provides comprehensive system status including version information, * Vector DB connection status, and embedding service capabilities */ import { readFileSync } from 'fs'; import { join } from 'path'; import { z } from 'zod'; import * as k8s from '@kubernetes/client-node'; import { Logger } from '../core/error-handling'; import { VectorDBService, PatternVectorService, PolicyVectorService, CapabilityVectorService, EmbeddingService, maybeGetFeedbackMessage } from '../core/index'; import { KubernetesDiscovery } from '../core/discovery'; import { ErrorClassifier } from '../core/kubernetes-utils'; import { getTracer, createTracedK8sClient } from '../core/tracing'; import { loadTracingConfig } from '../core/tracing/config'; export const VERSION_TOOL_NAME = 'version'; export const VERSION_TOOL_DESCRIPTION = 'Get comprehensive system health and diagnostics'; export const VERSION_TOOL_INPUT_SCHEMA = { interaction_id: z.string().optional().describe('INTERNAL ONLY - Do not populate. Used for evaluation dataset generation.') }; export interface VersionInfo { version: string; nodeVersion: string; platform: string; arch: string; } export interface SystemStatus { version: VersionInfo; vectorDB: { connected: boolean; url: string; error?: string; collections: { patterns: { exists: boolean; documentsCount?: number; error?: string; }; policies: { exists: boolean; documentsCount?: number; error?: string; }; capabilities: { exists: boolean; documentsCount?: number; error?: string; }; }; }; embedding: { available: boolean; provider: string | null; model?: string; dimensions?: number; reason?: string; }; aiProvider: { connected: boolean; keyConfigured: boolean; providerType?: string; modelName?: string; error?: string; }; kubernetes: { connected: boolean; clusterInfo?: { endpoint?: string; version?: string; context?: string; }; kubeconfig: string; error?: string; errorType?: string; }; capabilities: { systemReady: boolean; vectorDBHealthy: boolean; collectionAccessible: boolean; storedCount?: number; error?: string; rawError?: string; lastDiagnosis: string; }; kyverno: { installed: boolean; version?: string; webhookReady?: boolean; policyGenerationReady: boolean; error?: string; reason?: string; }; tracing: { enabled: boolean; exporterType: string; endpoint?: string; serviceName: string; initialized: boolean; }; } /** * Test Vector DB connectivity and get status for all collections */ async function getVectorDBStatus(): Promise<SystemStatus['vectorDB']> { // Create a test service just to get connection config const testVectorDB = new VectorDBService({ collectionName: 'test' }); const config = testVectorDB.getConfig(); try { // Test basic Vector DB connectivity (independent of collections) const isHealthy = await testVectorDB.healthCheck(); if (!isHealthy) { return { connected: false, url: config.url || 'unknown', error: 'Health check failed - Vector DB not responding', collections: { patterns: { exists: false, error: 'Vector DB not accessible' }, policies: { exists: false, error: 'Vector DB not accessible' }, capabilities: { exists: false, error: 'Vector DB not accessible' } } }; } // Test each collection separately const embeddingService = new EmbeddingService(); // Test patterns collection const patternsStatus = await testCollectionStatus('patterns', () => { const patternVectorDB = new VectorDBService({ collectionName: 'patterns' }); const patternService = new PatternVectorService('patterns', patternVectorDB, embeddingService); return patternService.getPatternsCount(); }); // Test policies collection const policiesStatus = await testCollectionStatus('policies', () => { const policyVectorDB = new VectorDBService({ collectionName: 'policies' }); const policyService = new PolicyVectorService(policyVectorDB, embeddingService); return policyService.getDataCount(); }); // Test capabilities collection const capabilitiesStatus = await testCollectionStatus('capabilities', () => { const capabilityService = new CapabilityVectorService(); return capabilityService.getCapabilitiesCount(); }); return { connected: true, url: config.url || 'unknown', collections: { patterns: patternsStatus, policies: policiesStatus, capabilities: capabilitiesStatus } }; } catch (error) { return { connected: false, url: config.url || 'unknown', error: error instanceof Error ? error.message : String(error), collections: { patterns: { exists: false, error: 'Vector DB connection failed' }, policies: { exists: false, error: 'Vector DB connection failed' }, capabilities: { exists: false, error: 'Vector DB connection failed' } } }; } } /** * Helper function to test individual collection status */ async function testCollectionStatus( collectionName: string, getCountFn: () => Promise<number> ): Promise<{ exists: boolean; documentsCount?: number; error?: string; }> { try { const documentsCount = await getCountFn(); return { exists: true, documentsCount }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); // Check if error indicates collection doesn't exist (vs other errors) const collectionNotExists = errorMessage.toLowerCase().includes('collection') && (errorMessage.toLowerCase().includes('not exist') || errorMessage.toLowerCase().includes('does not exist')); return { exists: false, error: collectionNotExists ? `${collectionName} collection does not exist` : errorMessage }; } } /** * Test embedding service status * Performs actual embedding generation test to verify service works */ async function getEmbeddingStatus(): Promise<SystemStatus['embedding']> { const embeddingService = new EmbeddingService(); const status = embeddingService.getStatus(); // If service reports available, actually test it to verify embeddings work if (status.available) { try { // Test embedding generation with a simple query await embeddingService.generateEmbedding('test'); return { available: true, provider: status.provider, model: status.model, dimensions: status.dimensions }; } catch (error) { // Embedding service initialized but doesn't actually work const errorMessage = error instanceof Error ? error.message : String(error); return { available: false, provider: status.provider, model: status.model, dimensions: status.dimensions, reason: `Embedding generation test failed: ${errorMessage}` }; } } return { available: false, provider: status.provider, model: status.model, dimensions: status.dimensions, reason: status.reason }; } /** * Test capability system readiness */ async function getCapabilityStatus(): Promise<SystemStatus['capabilities']> { const timestamp = new Date().toISOString(); try { const capabilityService = new CapabilityVectorService(); // Test Vector DB health for capabilities let vectorDBHealthy = false; let collectionAccessible = false; let storedCount: number | undefined; try { vectorDBHealthy = await capabilityService.healthCheck(); } catch (error) { return { systemReady: false, vectorDBHealthy: false, collectionAccessible: false, error: `Vector DB health check failed: ${error instanceof Error ? error.message : String(error)}`, lastDiagnosis: timestamp }; } if (vectorDBHealthy) { // Test collection accessibility and storage operations try { await capabilityService.initialize(); storedCount = await capabilityService.getCapabilitiesCount(); collectionAccessible = true; // Test MCP-used operations: verify vector operations work const embeddingService = new EmbeddingService(); const embeddingStatus = embeddingService.getStatus(); const expectedDimensions = embeddingStatus.dimensions || 1536; // Use provider's dimension or default const testEmbedding = await embeddingService.generateEmbedding('diagnostic test query'); if (!testEmbedding || testEmbedding.length !== expectedDimensions) { throw new Error(`Embedding dimension mismatch: expected ${expectedDimensions}, got ${testEmbedding?.length || 'null'} dimensions`); } // Validate embedding values are numbers (not NaN, Infinity, etc.) if (testEmbedding.some(val => !Number.isFinite(val))) { throw new Error('Embedding contains invalid values (NaN or Infinity)'); } // Test core MCP operations: verify we can list capabilities (most basic operation) const capabilities = await capabilityService.getAllCapabilities(1); if (capabilities.length === 0 && storedCount > 0) { throw new Error('Capability listing failed - cannot retrieve stored capabilities'); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); // Check for actual dimension mismatch errors (be specific) const isDimensionMismatch = ( errorMessage.toLowerCase().includes('dimension') && (errorMessage.toLowerCase().includes('mismatch') || errorMessage.toLowerCase().includes('expected')) ) || ( errorMessage.toLowerCase().includes('vector') && errorMessage.toLowerCase().includes('size') ); // Since core MCP functionality works (list, search, ID-based get), downgrade severity const isCoreSystemWorking = vectorDBHealthy && collectionAccessible && (storedCount || 0) > 0; return { systemReady: isCoreSystemWorking, // Core system is ready if MCP operations work vectorDBHealthy: true, collectionAccessible: collectionAccessible, storedCount: storedCount, error: isDimensionMismatch ? `Vector dimension mismatch detected: ${errorMessage}. The capabilities collection exists but has incompatible vector dimensions. Delete the collection to allow recreation with correct dimensions.` : `Capability system test failed: ${errorMessage}`, lastDiagnosis: timestamp, // Add raw error for debugging rawError: errorMessage }; } } // Check embedding service (required for semantic search) const embeddingService = new EmbeddingService(); const embeddingAvailable = embeddingService.isAvailable(); // System is ready if Vector DB is healthy, collection is accessible, and embeddings available const systemReady = vectorDBHealthy && collectionAccessible && embeddingAvailable; return { systemReady, vectorDBHealthy, collectionAccessible, storedCount, lastDiagnosis: timestamp }; } catch (error) { return { systemReady: false, vectorDBHealthy: false, collectionAccessible: false, error: `Capability diagnostics failed: ${error instanceof Error ? error.message : String(error)}`, lastDiagnosis: timestamp }; } } /** * Test Kyverno installation and readiness for policy generation using shared client */ export async function getKyvernoStatus(): Promise<SystemStatus['kyverno']> { try { // Create discovery instance and establish connection const discovery = new KubernetesDiscovery({}); await discovery.connect(); // First test if we can connect to Kubernetes at all const testResult = await discovery.testConnection(); if (!testResult.connected) { return { installed: false, policyGenerationReady: false, error: 'Cannot detect Kyverno - Kubernetes cluster is not accessible' }; } // Check if Kyverno CRDs are installed using the original approach const crdOutput = await discovery.executeKubectl(['get', 'crd', '--no-headers']); const kyvernoCRDs = crdOutput.split('\n').filter(line => line.includes('kyverno.io') && ( line.includes('clusterpolicies') || line.includes('policies') || line.includes('policyreports') ) ); if (kyvernoCRDs.length === 0) { return { installed: false, policyGenerationReady: false, reason: 'Kyverno CRDs not found in cluster - Kyverno is not installed' }; } // Check if Kyverno deployment is ready using the client let deploymentReady = false; let webhookReady = false; let version: string | undefined; try { // Get client and check deployment status const client = discovery.getClient(); const appsV1Api = createTracedK8sClient( client.makeApiClient(k8s.AppsV1Api), 'AppsV1Api' ); const deploymentResponse = await appsV1Api.listNamespacedDeployment({ namespace: 'kyverno' }); const kyvernoDeployments = deploymentResponse.items.filter((deployment: any) => deployment.metadata?.name?.startsWith('kyverno-') ); if (kyvernoDeployments.length > 0) { // Check if all Kyverno deployments are ready deploymentReady = kyvernoDeployments.every((deployment: any) => { const readyReplicas = deployment.status?.readyReplicas || 0; const replicas = deployment.status?.replicas || 0; return readyReplicas > 0 && readyReplicas === replicas; }); // Try to get version from image tag of the first deployment (usually admission controller) const firstDeployment = kyvernoDeployments[0]; const container = firstDeployment.spec?.template.spec?.containers?.[0]; if (container?.image) { const imageMatch = container.image.match(/:v?([0-9]+\.[0-9]+\.[0-9]+)/); if (imageMatch) { version = imageMatch[1]; } } } } catch (error) { // Kyverno might be in a different namespace or not exist deploymentReady = false; } // Check admission controller webhook try { const client = discovery.getClient(); const admissionApi = createTracedK8sClient( client.makeApiClient(k8s.AdmissionregistrationV1Api), 'AdmissionregistrationV1Api' ); const webhookResponse = await admissionApi.listValidatingWebhookConfiguration(); webhookReady = webhookResponse.items.some((webhook: any) => webhook.metadata?.name?.includes('kyverno') ); } catch (error) { webhookReady = false; } // Determine if policy generation is ready const policyGenerationReady = deploymentReady && webhookReady; if (!policyGenerationReady) { let reason = 'Kyverno is partially installed but not fully operational'; if (!deploymentReady && !webhookReady) { reason = 'Kyverno deployment and admission webhook are not ready'; } else if (!deploymentReady) { reason = 'Kyverno deployment is not ready'; } else if (!webhookReady) { reason = 'Kyverno admission webhook is not ready'; } return { installed: true, version, webhookReady, policyGenerationReady, reason }; } return { installed: true, version, webhookReady: true, policyGenerationReady: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { installed: false, policyGenerationReady: false, error: `Kyverno detection failed: ${errorMessage}` }; } } /** * Test Kubernetes cluster connectivity using shared client */ async function getKubernetesStatus(): Promise<SystemStatus['kubernetes']> { try { // Create discovery instance and establish connection const discovery = new KubernetesDiscovery({}); await discovery.connect(); // Get connection info using the shared approach const connectionInfo = discovery.getConnectionInfo(); const testResult = await discovery.testConnection(); if (testResult.connected) { return { connected: true, clusterInfo: { endpoint: connectionInfo.server, version: testResult.version, context: connectionInfo.context }, kubeconfig: connectionInfo.kubeconfig }; } else { return { connected: false, kubeconfig: connectionInfo.kubeconfig, error: testResult.error, errorType: testResult.errorType }; } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const classified = ErrorClassifier.classifyError(error as Error); return { connected: false, kubeconfig: process.env.KUBECONFIG || '~/.kube/config', error: errorMessage, errorType: classified.type }; } } /** * Test AI provider connectivity */ async function getAIProviderStatus(interaction_id?: string): Promise<SystemStatus['aiProvider']> { try { // Import AI provider factory and test connectivity const { createAIProvider } = await import('../core/ai-provider-factory'); const aiProvider = createAIProvider(); if (!aiProvider.isInitialized()) { return { connected: false, keyConfigured: false, providerType: aiProvider.getProviderType(), error: 'AI provider API key not configured' }; } // Test with a minimal request to check connectivity await aiProvider.sendMessage('test', 'version-connectivity-check', { user_intent: 'Test AI provider connectivity and system version check', interaction_id: interaction_id }); return { connected: true, keyConfigured: true, providerType: aiProvider.getProviderType(), modelName: aiProvider.getModelName() }; } catch (error) { // Try to get provider type and model name even on error let providerType: string | undefined; let modelName: string | undefined; try { const { createAIProvider } = await import('../core/ai-provider-factory'); const aiProvider = createAIProvider(); providerType = aiProvider.getProviderType(); modelName = aiProvider.getModelName(); } catch { providerType = undefined; modelName = undefined; } // Capture detailed error information for debugging let errorMessage: string; if (error instanceof Error) { errorMessage = error.message; // If there are additional properties on the error object, include them const errorObj = error as any; const details: string[] = []; if (errorObj.cause) { details.push(`cause: ${String(errorObj.cause)}`); } if (errorObj.url) { details.push(`url: ${errorObj.url}`); } if (errorObj.statusCode || errorObj.status) { details.push(`status: ${errorObj.statusCode || errorObj.status}`); } if (errorObj.statusText) { details.push(`statusText: ${errorObj.statusText}`); } if (errorObj.responseBody) { try { details.push(`response: ${JSON.stringify(errorObj.responseBody)}`); } catch { details.push(`response: ${String(errorObj.responseBody)}`); } } // Add stack trace first line for context (shows where error originated) if (error.stack) { const stackLines = error.stack.split('\n'); if (stackLines.length > 1) { details.push(`at: ${stackLines[1].trim()}`); } } if (details.length > 0) { errorMessage += ` (${details.join(', ')})`; } } else { errorMessage = String(error); } return { connected: false, keyConfigured: false, providerType, modelName, error: errorMessage }; } } /** * Get version information from package.json */ export function getVersionInfo(): VersionInfo { // Find package.json relative to this module's location (MCP server's installation) let packageJson: any; try { // Get the directory where this module is installed // __dirname points to the compiled JS location (dist/tools/), go up two levels to find package.json const mcpServerDir = join(__dirname, '..', '..'); const packageJsonPath = join(mcpServerDir, 'package.json'); packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); } catch (error) { // If package.json not found, use unknown version packageJson = { version: 'unknown' }; } return { version: packageJson.version, nodeVersion: process.version, platform: process.platform, arch: process.arch }; } /** * Get OpenTelemetry tracing status */ export function getTracingStatus(): SystemStatus['tracing'] { const config = loadTracingConfig(); const tracer = getTracer(); return { enabled: config.enabled, exporterType: config.exporterType, endpoint: config.otlpEndpoint, serviceName: config.serviceName, initialized: tracer.isEnabled() }; } /** * Handle version tool request with comprehensive system diagnostics */ export async function handleVersionTool( args: any, logger: Logger, requestId: string ): Promise<any> { try { // Extract interaction_id for evaluation dataset generation const interaction_id = args.interaction_id ? VERSION_TOOL_INPUT_SCHEMA.interaction_id.parse(args.interaction_id) : undefined; logger.info('Processing version tool request with system diagnostics', { requestId }); // Get version info const version = getVersionInfo(); // Run all diagnostics in parallel for better performance logger.info('Running system diagnostics...', { requestId }); const [vectorDBStatus, embeddingStatus, aiProviderStatus, kubernetesStatus, capabilityStatus, kyvernoStatus] = await Promise.all([ getVectorDBStatus(), getEmbeddingStatus(), getAIProviderStatus(interaction_id), getKubernetesStatus(), getCapabilityStatus(), getKyvernoStatus() ]); // Get tracing status synchronously (no async operations) const tracingStatus = getTracingStatus(); const systemStatus: SystemStatus = { version, vectorDB: vectorDBStatus, embedding: embeddingStatus, aiProvider: aiProviderStatus, kubernetes: kubernetesStatus, capabilities: capabilityStatus, kyverno: kyvernoStatus, tracing: tracingStatus }; // Log summary of system health logger.info('System diagnostics completed', { requestId, version: version.version, vectorDBConnected: vectorDBStatus.connected, embeddingAvailable: embeddingStatus.available, aiProviderConnected: aiProviderStatus.connected, kubernetesConnected: kubernetesStatus.connected, capabilitySystemReady: capabilityStatus.systemReady, kyvernoReady: kyvernoStatus.policyGenerationReady }); // Check if we should show feedback message const feedbackMessage = maybeGetFeedbackMessage(); return { content: [{ type: 'text' as const, text: JSON.stringify({ status: 'success', system: systemStatus, summary: { overall: vectorDBStatus.connected && aiProviderStatus.connected && kubernetesStatus.connected && capabilityStatus.systemReady ? 'healthy' : 'degraded', patternSearch: embeddingStatus.available ? 'semantic+keyword' : 'keyword-only', capabilityScanning: capabilityStatus.systemReady && kubernetesStatus.connected ? 'ready' : 'not-ready', kubernetesAccess: kubernetesStatus.connected ? 'connected' : 'disconnected', policyIntentManagement: vectorDBStatus.connected && embeddingStatus.available ? 'ready' : 'not-ready', kyvernoPolicyGeneration: kyvernoStatus.policyGenerationReady ? 'ready' : 'not-ready', capabilities: [ vectorDBStatus.connected && vectorDBStatus.collections.patterns.exists ? 'pattern-management' : null, // Policy intent management is available if Vector DB and embedding service are ready vectorDBStatus.connected && embeddingStatus.available ? 'policy-intent-management' : null, capabilityStatus.systemReady && kubernetesStatus.connected ? 'capability-scanning' : null, embeddingStatus.available ? 'semantic-search' : null, aiProviderStatus.connected ? 'ai-recommendations' : null, kubernetesStatus.connected ? 'kubernetes-integration' : null, // Kyverno policy generation is only available when Kyverno is installed kyvernoStatus.policyGenerationReady ? 'kyverno-policy-generation' : null ].filter(Boolean) }, timestamp: new Date().toISOString(), ...(feedbackMessage ? { message: feedbackMessage } : {}) }, null, 2) }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error('Version tool request failed', error as Error, { requestId }); return { content: [{ type: 'text' as const, text: JSON.stringify({ status: 'error', error: errorMessage, timestamp: new Date().toISOString() }, null, 2) }] }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vfarcic/dot-ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server