UNPKG

bottlenecks-mcp-server

Version:

Model Context Protocol server for Bottlenecks database - enables AI agents like Claude to interact with bottleneck data

465 lines • 22 kB
/** * MCP Validation Operations Tools * Provides AI agents with validation and completeness checking capabilities */ /** * Get validation status for a specific card */ export function createGetCardValidationTool(client) { return { type: 'call_tool', name: 'get_card_validation', description: 'Get detailed validation status and completeness for a specific card', inputSchema: { type: 'object', properties: { card_id: { type: 'string', description: 'The ID of the card to validate', format: 'uuid', }, include_structure_details: { type: 'boolean', description: 'Include detailed MDX structure analysis', default: true, }, }, required: ['card_id'], }, handler: async (params) => { try { const { card_id, include_structure_details = true } = params; // Call the validation API const response = await client.request('/api/admin/validate-schema', { method: 'POST', body: { type: 'single', cardId: card_id, }, }); if (!response.success) { return { content: [ { type: 'text', text: `Failed to validate card: ${response.error || 'Unknown error'}`, }, ], isError: true, }; } const validationReport = response.data.result; // Format the response for AI consumption let resultText = `šŸ” Card Validation Report\n\n`; resultText += `Card ID: ${card_id}\n`; resultText += `Title: ${validationReport.cardTitle}\n`; resultText += `Valid: ${validationReport.isValid ? 'āœ…' : 'āŒ'}\n`; resultText += `Completeness: ${validationReport.completeness}%\n`; resultText += `Validation Date: ${validationReport.validatedAt}\n\n`; // Add field validation issues const errors = validationReport.results.filter((r) => r.severity === 'error'); const warnings = validationReport.results.filter((r) => r.severity === 'warning'); if (errors.length > 0) { resultText += `āŒ Errors (${errors.length}):\n`; errors.forEach((error) => { resultText += ` • ${error.field}: ${error.error}\n`; if (error.suggestion) { resultText += ` šŸ’” ${error.suggestion}\n`; } }); resultText += '\n'; } if (warnings.length > 0) { resultText += `āš ļø Warnings (${warnings.length}):\n`; warnings.forEach((warning) => { resultText += ` • ${warning.field}: ${warning.error || 'Needs attention'}\n`; if (warning.suggestion) { resultText += ` šŸ’” ${warning.suggestion}\n`; } }); resultText += '\n'; } // Include structure details if requested if (include_structure_details && validationReport.mdxStructure) { const structure = validationReport.mdxStructure; resultText += `šŸ“ MDX Structure Analysis:\n`; resultText += `Structure Valid: ${structure.isStructureValid ? 'āœ…' : 'āŒ'}\n`; resultText += `Structure Completeness: ${structure.structureCompleteness}%\n`; resultText += `Sections Found: ${structure.sectionsFound.join(', ')}\n`; if (structure.sectionsMissing.length > 0) { resultText += `Missing Sections: ${structure.sectionsMissing.join(', ')}\n`; } const missingRequired = structure.results .filter((r) => r.severity === 'error' && !r.subsection) .map((r) => r.section); if (missingRequired.length > 0) { resultText += `āŒ Missing Required Sections: ${missingRequired.join(', ')}\n`; } resultText += '\nšŸ“‹ Recommendations:\n'; const recommendations = structure.results .filter((r) => r.suggestion) .slice(0, 5); // Limit to top 5 recommendations recommendations.forEach((rec) => { const sectionName = rec.subsection ? `${rec.section}.${rec.subsection}` : rec.section; resultText += ` • ${sectionName}: ${rec.suggestion}\n`; }); } return { content: [ { type: 'text', text: resultText, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Validation check failed: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }; } /** * Validate multiple cards and get completeness summary */ export function createBulkValidationTool(client) { return { type: 'call_tool', name: 'bulk_validate_cards', description: 'Validate multiple cards and get completeness statistics', inputSchema: { type: 'object', properties: { card_ids: { type: 'array', items: { type: 'string', format: 'uuid' }, description: 'Array of card IDs to validate (max 50)', maxItems: 50, }, validation_type: { type: 'string', enum: ['selected', 'bulk'], description: 'Type of validation - selected for specific cards, bulk for all cards', default: 'selected', }, include_details: { type: 'boolean', description: 'Include detailed validation results for each card', default: false, }, }, }, handler: async (params) => { try { const { card_ids, validation_type = 'selected', include_details = false, } = params; const requestBody = { type: validation_type }; if (validation_type === 'selected' && card_ids) { requestBody.cardIds = card_ids; } const response = await client.request('/api/admin/validate-schema', { method: 'POST', body: requestBody, }); if (!response.success) { return { content: [ { type: 'text', text: `Failed to validate cards: ${response.error || 'Unknown error'}`, }, ], isError: true, }; } const bulkResult = response.data.result; // Format summary statistics let resultText = `šŸ“Š Bulk Validation Summary\n\n`; resultText += `Total Cards: ${bulkResult.totalCards}\n`; resultText += `Valid Cards: ${bulkResult.validCards}\n`; resultText += `Invalid Cards: ${bulkResult.invalidCards}\n`; resultText += `Average Completeness: ${bulkResult.averageCompleteness}%\n`; resultText += `Validation Date: ${bulkResult.validatedAt}\n\n`; // Completeness distribution const highCompleteness = bulkResult.cardReports.filter((r) => r.completeness >= 80).length; const mediumCompleteness = bulkResult.cardReports.filter((r) => r.completeness >= 50 && r.completeness < 80).length; const lowCompleteness = bulkResult.cardReports.filter((r) => r.completeness < 50).length; resultText += `šŸ“ˆ Completeness Distribution:\n`; resultText += ` High (≄80%): ${highCompleteness} cards\n`; resultText += ` Medium (50-79%): ${mediumCompleteness} cards\n`; resultText += ` Low (<50%): ${lowCompleteness} cards\n\n`; // Common issues const allErrors = bulkResult.cardReports.flatMap((r) => r.results.filter((res) => res.severity === 'error')); const errorCounts = {}; allErrors.forEach((error) => { errorCounts[error.field] = (errorCounts[error.field] || 0) + 1; }); if (Object.keys(errorCounts).length > 0) { resultText += `āŒ Most Common Issues:\n`; Object.entries(errorCounts) .sort(([, a], [, b]) => b - a) .slice(0, 5) .forEach(([field, count]) => { resultText += ` • ${field}: ${count} cards\n`; }); resultText += '\n'; } // Include detailed results if requested if (include_details) { resultText += `šŸ“‹ Detailed Results:\n`; bulkResult.cardReports.slice(0, 10).forEach((report) => { const errorCount = report.results.filter((r) => r.severity === 'error').length; const warningCount = report.results.filter((r) => r.severity === 'warning').length; const status = report.isValid ? 'āœ…' : 'āŒ'; resultText += ` ${status} ${report.cardTitle} (${report.completeness}%)`; if (errorCount > 0) resultText += ` - ${errorCount} errors`; if (warningCount > 0) resultText += ` - ${warningCount} warnings`; resultText += '\n'; }); } return { content: [ { type: 'text', text: resultText, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Bulk validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }; } /** * Find cards that need validation attention */ export function createFindCardsNeedingValidationTool(client) { return { type: 'call_tool', name: 'find_cards_needing_validation', description: 'Find cards that need validation attention (low completeness, missing sections, etc.)', inputSchema: { type: 'object', properties: { completeness_threshold: { type: 'number', description: 'Minimum completeness percentage (cards below this need attention)', default: 70, minimum: 0, maximum: 100, }, include_valid_but_incomplete: { type: 'boolean', description: 'Include cards that are valid but have low completeness', default: true, }, limit: { type: 'number', description: 'Maximum number of cards to return', default: 20, minimum: 1, maximum: 100, }, }, }, handler: async (params) => { try { const { completeness_threshold = 70, include_valid_but_incomplete = true, limit = 20, } = params; // First get all cards with validation status const cardsResponse = await client.request('/api/admin/cards', { method: 'GET', }); if (!cardsResponse.success) { return { content: [ { type: 'text', text: 'Failed to fetch cards for validation analysis', }, ], isError: true, }; } const cards = cardsResponse.data?.data || []; // Filter cards that need attention const cardsNeedingAttention = cards .filter((card) => { const validation = card.validation_status; if (!validation) return true; // No validation data = needs validation const isIncomplete = validation.completeness < completeness_threshold; const hasErrors = !validation.is_valid; return hasErrors || (include_valid_but_incomplete && isIncomplete); }) .slice(0, limit); let resultText = `šŸ” Cards Needing Validation Attention\n\n`; resultText += `Found ${cardsNeedingAttention.length} cards needing attention\n`; resultText += `Completeness threshold: ${completeness_threshold}%\n\n`; const highPriority = cardsNeedingAttention.filter((card) => !card.validation_status || !card.validation_status.is_valid); const mediumPriority = cardsNeedingAttention.filter((card) => card.validation_status?.is_valid && card.validation_status.completeness < completeness_threshold); resultText += `šŸ“Š Priority Breakdown:\n`; resultText += ` High Priority (invalid): ${highPriority.length} cards\n`; resultText += ` Medium Priority (valid but incomplete): ${mediumPriority.length} cards\n\n`; resultText += `šŸŽÆ Cards to Focus On:\n`; cardsNeedingAttention.forEach((card, index) => { const validation = card.validation_status; const priority = validation?.is_valid ? 'Medium' : 'High'; const completeness = validation?.completeness || 0; const status = validation?.is_valid ? 'āš ļø' : 'āŒ'; resultText += `${index + 1}. ${status} ${card.title}\n`; resultText += ` Priority: ${priority} | Completeness: ${completeness}%\n`; resultText += ` ID: ${card.id}\n`; if (!validation) { resultText += ` Action: Run validation to assess current status\n`; } else if (!validation.is_valid) { resultText += ` Action: Fix validation errors first\n`; } else { resultText += ` Action: Improve completeness by adding optional sections\n`; } resultText += '\n'; }); resultText += `šŸ’” Next Steps:\n`; resultText += `• Use get_card_validation on high-priority cards to see specific issues\n`; resultText += `• Focus on fixing validation errors before improving completeness\n`; resultText += `• Missing required sections like "impact" should be added first\n`; resultText += `• Consider bulk validation for cards without validation data\n`; return { content: [ { type: 'text', text: resultText, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Failed to find cards needing validation: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }; } /** * Get validation statistics and insights */ export function createGetValidationStatsTool(client) { return { type: 'call_tool', name: 'get_validation_stats', description: 'Get overall validation statistics and insights for the database', inputSchema: { type: 'object', properties: { include_recent_only: { type: 'boolean', description: 'Only include cards validated in the last 7 days', default: false, }, }, }, handler: async (params) => { try { const { include_recent_only = false } = params; // Get validation statistics from the API const response = await client.request('/api/admin/validate-schema', { method: 'GET', }); if (!response.success) { return { content: [ { type: 'text', text: `Failed to get validation statistics: ${response.error || 'Unknown error'}`, }, ], isError: true, }; } const { history, summary } = response.data; let resultText = `šŸ“Š Validation Statistics & Insights\n\n`; if (summary) { resultText += `šŸŽÆ Overall Database Health:\n`; resultText += ` Total Cards with Validation: ${summary.total_cards_with_validation}\n`; resultText += ` Valid Cards: ${summary.valid_cards}\n`; resultText += ` Invalid Cards: ${summary.invalid_cards}\n`; resultText += ` Average Completeness: ${summary.average_completeness}%\n`; resultText += ` High Completeness (≄80%): ${summary.high_completeness_cards}\n`; resultText += ` Low Completeness (<50%): ${summary.low_completeness_cards}\n\n`; } if (history && history.length > 0) { resultText += `šŸ“ˆ Recent Validation Activity:\n`; const recentHistory = include_recent_only ? history.filter((h) => new Date(h.created_at) > new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)) : history.slice(0, 10); recentHistory.forEach((h, index) => { resultText += ` ${index + 1}. ${new Date(h.created_at).toLocaleDateString()}\n`; resultText += ` Type: ${h.validation_type || 'Unknown'}\n`; if (h.total_cards) resultText += ` Cards: ${h.total_cards}\n`; if (h.average_completeness) resultText += ` Avg Completeness: ${h.average_completeness}%\n`; }); resultText += '\n'; } resultText += `šŸ’” Recommendations:\n`; resultText += `• Use bulk_validate_cards to check multiple cards at once\n`; resultText += `• Focus on cards with completeness < 50% for maximum impact\n`; resultText += `• Missing required sections (like "impact") should be prioritized\n`; resultText += `• Use get_card_validation for detailed analysis of specific cards\n`; resultText += `• Use find_cards_needing_validation to identify problem cards\n`; return { content: [ { type: 'text', text: resultText, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Failed to get validation stats: ${error instanceof Error ? error.message : 'Unknown error'}`, }, ], isError: true, }; } }, }; } //# sourceMappingURL=validation-operations.js.map