UNPKG

@vfarcic/dot-ai

Version:

AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance

784 lines (783 loc) 34.2 kB
"use strict"; /** * Version tool for MCP server * * Provides comprehensive system status including version information, * Vector DB connection status, and embedding service capabilities * * PRD #343: Kubernetes interactions migrated to plugin system */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.VERSION_TOOL_INPUT_SCHEMA = exports.VERSION_TOOL_DESCRIPTION = exports.VERSION_TOOL_NAME = void 0; exports.getVersionInfo = getVersionInfo; exports.getTracingStatus = getTracingStatus; exports.getKyvernoStatusViaPlugin = getKyvernoStatusViaPlugin; exports.handleVersionTool = handleVersionTool; const fs_1 = require("fs"); const path_1 = require("path"); const zod_1 = require("zod"); const constants_1 = require("../core/constants"); const index_1 = require("../core/index"); const resource_vector_service_1 = require("../core/resource-vector-service"); const tracing_1 = require("../core/tracing"); const config_1 = require("../core/tracing/config"); const generic_session_manager_1 = require("../core/generic-session-manager"); const visualization_1 = require("../core/visualization"); const plugin_registry_1 = require("../core/plugin-registry"); const mcp_client_registry_1 = require("../core/mcp-client-registry"); const request_context_1 = require("../interfaces/request-context"); exports.VERSION_TOOL_NAME = 'version'; exports.VERSION_TOOL_DESCRIPTION = 'Get comprehensive system health and diagnostics'; exports.VERSION_TOOL_INPUT_SCHEMA = { interaction_id: zod_1.z.string().optional().describe('INTERNAL ONLY - Do not populate. Used for evaluation dataset generation.') }; /** * Helper function to create error collections object * PRD #356: Added knowledgeBase collection */ function createErrorCollections(errorMessage) { return { patterns: { exists: false, error: errorMessage }, policies: { exists: false, error: errorMessage }, capabilities: { exists: false, error: errorMessage }, resources: { exists: false, error: errorMessage }, knowledgeBase: { exists: false, error: errorMessage }, }; } /** * Test Vector DB connectivity and get status for all collections * PRD #359: Uses plugin for all Qdrant operations, URL comes from plugin */ async function getVectorDBStatus() { const fallbackUrl = 'unknown'; try { // Test basic Vector DB connectivity via plugin const healthResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'collection_stats', { collection: 'capabilities', // Use any collection for health check }); if (!healthResponse.success) { return { connected: false, url: fallbackUrl, error: 'Health check failed - Vector DB not responding', collections: createErrorCollections('Vector DB not accessible'), }; } // Extract URL from plugin response (PRD #359) const toolResult = healthResponse.result; const url = toolResult?.data?.url || fallbackUrl; // Test each collection separately const embeddingService = new index_1.EmbeddingService(); // Test patterns collection const patternsStatus = await testCollectionStatus('patterns', () => { const patternService = new index_1.PatternVectorService('patterns', embeddingService); return patternService.getPatternsCount(); }); // Test policies collection const policiesStatus = await testCollectionStatus('policies', () => { const policyService = new index_1.PolicyVectorService(embeddingService); return policyService.getDataCount(); }); // Test capabilities collection const capabilitiesStatus = await testCollectionStatus('capabilities', () => { const capabilityService = new index_1.CapabilityVectorService('capabilities', embeddingService); return capabilityService.getCapabilitiesCount(); }); // Test resources collection and get synced types const resourcesStatus = await testResourcesCollectionStatus(embeddingService); // Test knowledge base collection (PRD #356) const knowledgeBaseStatus = await testKnowledgeBaseCollectionStatus(); return { connected: true, url, collections: { patterns: patternsStatus, policies: policiesStatus, capabilities: capabilitiesStatus, resources: resourcesStatus, knowledgeBase: knowledgeBaseStatus, }, }; } catch (error) { return { connected: false, url: fallbackUrl, error: error instanceof Error ? error.message : String(error), collections: createErrorCollections('Vector DB connection failed'), }; } } /** * Helper function to test individual collection status */ async function testCollectionStatus(collectionName, getCountFn) { 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 resources collection status */ async function testResourcesCollectionStatus(embeddingService) { try { const resourceService = new resource_vector_service_1.ResourceVectorService('resources', embeddingService); const resources = await resourceService.listResources(); const documentsCount = resources.length; return { exists: true, documentsCount }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const collectionNotExists = errorMessage.toLowerCase().includes('collection') && (errorMessage.toLowerCase().includes('not exist') || errorMessage.toLowerCase().includes('does not exist')); return { exists: false, error: collectionNotExists ? 'resources collection does not exist' : errorMessage }; } } /** * Test knowledge base collection status (PRD #356) * Uses plugin to query collection stats directly */ async function testKnowledgeBaseCollectionStatus() { try { const response = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'collection_stats', { collection: 'knowledge-base', }); if (!response.success) { const errorMessage = response.error?.message || 'Failed to get collection stats'; const collectionNotExists = errorMessage.toLowerCase().includes('collection') && (errorMessage.toLowerCase().includes('not exist') || errorMessage.toLowerCase().includes('does not exist') || errorMessage.toLowerCase().includes('not found')); return { exists: false, error: collectionNotExists ? 'knowledge-base collection does not exist' : errorMessage }; } const result = response.result; // Check for nested error in result if (result.success === false) { const errorMessage = result.error || 'Collection query failed'; const collectionNotExists = errorMessage.toLowerCase().includes('collection') && (errorMessage.toLowerCase().includes('not exist') || errorMessage.toLowerCase().includes('does not exist') || errorMessage.toLowerCase().includes('not found')); return { exists: false, error: collectionNotExists ? 'knowledge-base collection does not exist' : errorMessage }; } return { exists: true, documentsCount: result.data?.pointsCount ?? 0 }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); const collectionNotExists = errorMessage.toLowerCase().includes('collection') && (errorMessage.toLowerCase().includes('not exist') || errorMessage.toLowerCase().includes('does not exist') || errorMessage.toLowerCase().includes('not found')); return { exists: false, error: collectionNotExists ? 'knowledge-base collection does not exist' : errorMessage }; } } /** * Test embedding service status * Performs actual embedding generation test to verify service works */ async function getEmbeddingStatus() { const embeddingService = new index_1.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() { const timestamp = new Date().toISOString(); try { const capabilityService = new index_1.CapabilityVectorService(); // Test Vector DB health for capabilities let vectorDBHealthy = false; let collectionAccessible = false; let storedCount; 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 index_1.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 index_1.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 AI provider connectivity */ async function getAIProviderStatus(interaction_id) { try { // Import AI provider factory and test connectivity const { createAIProvider } = await Promise.resolve().then(() => __importStar(require('../core/ai-provider-factory'))); const aiProvider = createAIProvider(); if (!aiProvider.isInitialized()) { return { connected: false, keyConfigured: false, providerType: aiProvider.getProviderType(), error: constants_1.AI_SERVICE_ERRORS.API_KEY_INVALID }; } // 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; let modelName; try { const { createAIProvider } = await Promise.resolve().then(() => __importStar(require('../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; if (error instanceof Error) { errorMessage = error.message; // If there are additional properties on the error object, include them const errorObj = error; const details = []; 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 */ function getVersionInfo() { // Find package.json relative to this module's location (MCP server's installation) let packageJson; 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 = (0, path_1.join)(__dirname, '..', '..'); const packageJsonPath = (0, path_1.join)(mcpServerDir, 'package.json'); packageJson = JSON.parse((0, fs_1.readFileSync)(packageJsonPath, 'utf8')); } catch { // If package.json not found, use unknown version packageJson = { version: 'unknown' }; } return { version: packageJson.version || 'unknown', nodeVersion: process.version, platform: process.platform, arch: process.arch }; } /** * Get OpenTelemetry tracing status */ function getTracingStatus() { const config = (0, config_1.loadTracingConfig)(); const tracer = (0, tracing_1.getTracer)(); return { enabled: config.enabled, exporterType: config.exporterType, endpoint: config.otlpEndpoint, serviceName: config.serviceName, initialized: tracer.isEnabled() }; } /** * Get Kubernetes status via plugin (PRD #343) * PRD #359: Uses unified plugin registry */ async function getKubernetesStatusViaPlugin() { try { const response = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_version', {}); if (!response.success) { return { connected: false, kubeconfig: 'in-cluster', error: response.error?.message || 'Failed to get Kubernetes version via plugin', errorType: response.error?.code }; } // Check for nested error - plugin wraps kubectl errors in { success: false, error: "..." } const result = response.result; if (result.success === false) { return { connected: false, kubeconfig: 'in-cluster', error: result.error || result.message || 'kubectl version failed', errorType: 'KUBECTL_ERROR' }; } // Parse the kubectl version JSON output const versionData = JSON.parse(result.data || '{}'); return { connected: true, clusterInfo: { version: versionData.serverVersion?.gitVersion, endpoint: undefined, // Not available from kubectl version context: 'in-cluster' }, kubeconfig: 'in-cluster' }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { connected: false, kubeconfig: 'in-cluster', error: errorMessage, errorType: 'PLUGIN_ERROR' }; } } /** * Get Kyverno status via plugin (PRD #343) * PRD #359: Uses unified plugin registry */ async function getKyvernoStatusViaPlugin() { try { // Check if Kyverno CRD exists (clusterpolicies.kyverno.io) const crdResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_get_resource_json', { resource: 'crd/clusterpolicies.kyverno.io' }); // Check both outer success and nested result.success if (!crdResponse.success) { // CRD doesn't exist - Kyverno not installed return { installed: false, policyGenerationReady: false, reason: 'Kyverno CRDs not found in cluster - Kyverno is not installed' }; } const crdResult = crdResponse.result; if (crdResult?.success === false) { // CRD doesn't exist - Kyverno not installed return { installed: false, policyGenerationReady: false, reason: 'Kyverno CRDs not found in cluster - Kyverno is not installed' }; } // Kyverno CRDs exist, check deployment status let deploymentReady = false; let version; // Try to get kyverno-admission-controller deployment const deploymentResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_get_resource_json', { resource: 'deployment/kyverno-admission-controller', namespace: 'kyverno' }); if (deploymentResponse.success) { const result = deploymentResponse.result; // Check for nested error if (result.success !== false) { const deployment = JSON.parse(result.data || '{}'); const readyReplicas = deployment.status?.readyReplicas || 0; const replicas = deployment.status?.replicas || 0; deploymentReady = readyReplicas > 0 && readyReplicas === replicas; // Extract version from image tag const container = deployment.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]; } } } } // Check webhook configuration let webhookReady = false; const webhookResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_get_resource_json', { resource: 'validatingwebhookconfiguration/kyverno-resource-validating-webhook-cfg' }); // Check both outer success and nested result.success if (webhookResponse.success) { const webhookResult = webhookResponse.result; if (webhookResult?.success !== false) { webhookReady = true; } } 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 via plugin failed: ${errorMessage}` }; } } async function handleVersionTool(args, logger, requestId) { try { // Extract interaction_id for evaluation dataset generation const interaction_id = args?.interaction_id ? exports.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(); // PRD #359: Check for plugins via unified registry const hasK8sPlugins = (0, plugin_registry_1.isPluginInitialized)(); // Run all diagnostics in parallel for better performance logger.info('Running system diagnostics...', { requestId, hasK8sPlugins }); const [vectorDBStatus, embeddingStatus, aiProviderStatus, kubernetesStatus, capabilityStatus, kyvernoStatus] = await Promise.all([ getVectorDBStatus(), getEmbeddingStatus(), getAIProviderStatus(interaction_id), hasK8sPlugins ? getKubernetesStatusViaPlugin() : Promise.resolve({ connected: false, kubeconfig: 'unknown', error: 'Kubernetes plugins not available' }), getCapabilityStatus(), hasK8sPlugins ? getKyvernoStatusViaPlugin() : Promise.resolve({ installed: false, policyGenerationReady: false, error: 'Kubernetes plugins not available' }) ]); // Get tracing status synchronously (no async operations) const tracingStatus = getTracingStatus(); // PRD #359: Add plugin stats via unified registry const pluginStats = (0, plugin_registry_1.getPluginManager)()?.getStats(); // PRD #358: Add MCP server stats const mcpManager = (0, mcp_client_registry_1.isMcpClientInitialized)() ? (0, mcp_client_registry_1.getMcpClientManager)() : null; const mcpServerStats = mcpManager ? { ...mcpManager.getStats(), servers: mcpManager.getDiscoveredServers().map(s => ({ name: s.name, version: s.version, endpoint: s.endpoint, attachTo: s.attachTo, toolCount: s.tools.length, tools: s.tools.map(t => t.name), })), } : undefined; const systemStatus = { version, vectorDB: vectorDBStatus, embedding: embeddingStatus, aiProvider: aiProviderStatus, kubernetes: kubernetesStatus, capabilities: capabilityStatus, kyverno: kyvernoStatus, tracing: tracingStatus, plugins: pluginStats, mcpServers: mcpServerStats }; // 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 }); // Build summary object const 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) }; const timestamp = new Date().toISOString(); // PRD #320: Create session for visualization const sessionManager = new generic_session_manager_1.GenericSessionManager('ver'); const session = sessionManager.createSession({ toolName: 'version', system: systemStatus, summary, timestamp, status: 'success' }); // PRD #320: Generate visualization URL if configured const visualizationUrl = (0, visualization_1.getVisualizationUrl)(session.sessionId); // PRD #380: Include authenticated user identity in output const identity = (0, request_context_1.getCurrentIdentity)(); // Build response with optional visualization URL in JSON const responseData = { status: 'success', ...(identity ? { identity } : {}), system: systemStatus, summary, timestamp, ...(visualizationUrl ? { visualizationUrl } : {}) }; // Build content blocks - JSON for REST API, agent instruction for MCP agents const content = [{ type: 'text', text: JSON.stringify(responseData, null, 2) }]; // Add agent instruction block if visualization URL is present const agentDisplayBlock = (0, index_1.buildAgentDisplayBlock)({ visualizationUrl }); if (agentDisplayBlock) { content.push(agentDisplayBlock); } return { content }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error('Version tool request failed', error, { requestId }); return { content: [{ type: 'text', text: JSON.stringify({ status: 'error', error: errorMessage, timestamp: new Date().toISOString() }, null, 2) }] }; } }