Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
perform-research-tool.tsโ€ข12.7 kB
/** * Perform Research Tool * * New MCP tool that uses the research orchestrator to answer research questions * using cascading sources: project files โ†’ knowledge graph โ†’ environment โ†’ web search */ import { McpAdrError } from '../types/index.js'; import type { ToolContext } from '../types/tool-context.js'; import { ResearchOrchestrator } from '../utils/research-orchestrator.js'; import { ToolContextManager, type ToolContextDocument } from '../utils/context-document-manager.js'; import * as path from 'path'; /** * Perform research using the orchestrated multi-source approach * * @description Executes comprehensive research using cascading data sources: * project files โ†’ knowledge graph โ†’ environment analysis โ†’ web search. * Returns structured research results with confidence scoring and source attribution. * * @param {Object} args - Research configuration parameters * @param {string} args.question - The research question to investigate * @param {string} [args.projectPath] - Path to project root (defaults to cwd) * @param {string} [args.adrDirectory] - ADR directory relative to project (defaults to 'docs/adrs') * @param {number} [args.confidenceThreshold] - Minimum confidence for results (0-1, defaults to 0.6) * @param {boolean} [args.performWebSearch] - Enable web search as fallback (defaults to true) * * @returns {Promise<any>} Research results with answer, confidence, and sources * * @throws {McpAdrError} When question is empty or research orchestration fails * * @example * ```typescript * // Basic research question * const result = await performResearch({ * question: 'What authentication methods are used in this project?' * }); * * console.log(result.answer); // Research findings * console.log(result.confidence); // 0.85 * console.log(result.sources); // ['project-files', 'knowledge-graph'] * ``` * * @example * ```typescript * // Advanced research with custom settings * const result = await performResearch({ * question: 'How does the deployment pipeline work?', * projectPath: '/path/to/project', * confidenceThreshold: 0.8, * performWebSearch: false * }); * ``` * * @since 2.0.0 * @category Research * @category Tools * @mcp-tool */ export async function performResearch( args: { question: string; projectPath?: string; adrDirectory?: string; confidenceThreshold?: number; performWebSearch?: boolean; }, context?: ToolContext ): Promise<any> { const { question, projectPath = process.cwd(), adrDirectory = 'docs/adrs', confidenceThreshold = 0.6, performWebSearch = true, } = args; if (!question || question.trim().length === 0) { throw new McpAdrError('Research question is required', 'INVALID_INPUT'); } try { context?.info(`๐Ÿ” Starting research: ${question}`); context?.report_progress(0, 100); // Create research orchestrator const orchestrator = new ResearchOrchestrator(projectPath, adrDirectory); orchestrator.setConfidenceThreshold(confidenceThreshold); context?.info('๐Ÿ“ Searching project files...'); context?.report_progress(25, 100); // Perform research (orchestrator handles: files โ†’ knowledge graph โ†’ environment โ†’ web) context?.info('๐Ÿ“Š Querying knowledge graph and environment resources...'); context?.report_progress(50, 100); const research = await orchestrator.answerResearchQuestion(question); context?.info('๐ŸŒ Analyzing results and preparing response...'); context?.report_progress(75, 100); // Format response let response = `# Research Results: ${question} ## Summary ${research.answer || 'No conclusive answer found from available sources.'} ## Confidence Score: ${(research.confidence * 100).toFixed(1)}% ## Sources Consulted `; // Add source details if (research.sources.length === 0) { response += '\n*No relevant sources found*\n'; } else { for (const source of research.sources) { response += `\n### ${formatSourceName(source.type)} - **Confidence**: ${(source.confidence * 100).toFixed(1)}% - **Timestamp**: ${source.timestamp} `; // Add source-specific details if (source.type === 'project_files') { const files = source.data.files || []; response += `- **Files Found**: ${files.length}\n`; if (files.length > 0) { response += '\n**Relevant Files**:\n'; files.slice(0, 10).forEach((file: string) => { const relevance = source.data.relevance?.[file]; response += `- \`${file}\`${relevance ? ` (relevance: ${(relevance * 100).toFixed(0)}%)` : ''}\n`; }); if (files.length > 10) { response += `\n*... and ${files.length - 10} more files*\n`; } } } if (source.type === 'knowledge_graph') { const nodes = source.data.nodes || []; response += `- **Related ADRs**: ${nodes.length}\n`; } if (source.type === 'environment') { const capabilities = source.data.capabilities || []; response += `- **Available Capabilities**: ${capabilities.join(', ')}\n`; if (source.data.data?.length > 0) { response += '\n**Environment Data**:\n'; source.data.data.forEach((cap: any) => { response += `- **${cap.capability}**: ${cap.found ? 'โœ… Data found' : 'โŒ No data'}\n`; }); } } } } // Web search recommendation if (research.needsWebSearch && performWebSearch) { response += ` ## ๐ŸŒ Web Search Recommended Confidence is below threshold (${(confidenceThreshold * 100).toFixed(0)}%). Consider performing a web search for additional information: **Suggested search queries**: ${generateSearchQueries(question) .map(q => `- "${q}"`) .join('\n')} `; } // Metadata response += ` ## Research Metadata - **Duration**: ${research.metadata.duration}ms - **Sources Queried**: ${research.metadata.sourcesQueried.join(', ')} - **Files Analyzed**: ${research.metadata.filesAnalyzed} - **Overall Confidence**: ${(research.confidence * 100).toFixed(1)}% ## Next Steps `; if (research.confidence >= 0.8) { response += `โœ… High confidence answer. You can proceed with this information. `; } else if (research.confidence >= 0.6) { response += `โš ๏ธ Moderate confidence. Consider validating findings with additional sources. `; } else { response += `โŒ Low confidence. Web search or manual research recommended. `; } // Recommendations based on sources if (research.sources.some(s => s.type === 'project_files')) { response += ` ### Recommended Actions 1. Review the identified project files for detailed implementation information 2. Check for any related configuration files or documentation 3. Consider creating or updating ADRs to document findings `; } if (research.sources.some(s => s.type === 'environment')) { response += ` ### Environment Insights - Live environment data is available for verification - Consider running environment analysis tools for more details - Check environment configuration against ADR requirements `; } context?.info('โœ… Research complete!'); context?.report_progress(100, 100); // Save research context for future sessions try { const contextManager = new ToolContextManager(projectPath); await contextManager.initialize(); const contextDoc: ToolContextDocument = { metadata: { toolName: 'perform_research', toolVersion: '2.0.0', generated: new Date().toISOString(), projectPath, projectName: path.basename(projectPath), status: research.confidence >= confidenceThreshold ? 'success' : 'partial', confidence: research.confidence * 100, }, quickReference: `Research: "${question}" - ${(research.confidence * 100).toFixed(0)}% confidence. Sources: ${research.sources.map(s => formatSourceName(s.type)).join(', ')}`, executionSummary: { status: `Research completed with ${(research.confidence * 100).toFixed(0)}% confidence`, confidence: research.confidence * 100, keyFindings: [ `Question: ${question}`, `Confidence: ${(research.confidence * 100).toFixed(1)}%`, `Sources consulted: ${research.metadata.sourcesQueried.join(', ')}`, `Files analyzed: ${research.metadata.filesAnalyzed}`, `Duration: ${research.metadata.duration}ms`, ], }, detectedContext: { question, answer: research.answer, confidence: research.confidence, sources: research.sources.map(s => ({ type: s.type, confidence: s.confidence, timestamp: s.timestamp, dataType: s.data ? Object.keys(s.data).join(', ') : 'none', })), needsWebSearch: research.needsWebSearch, }, keyDecisions: [ { decision: `Research approach: ${research.metadata.sourcesQueried.join(' โ†’ ')}`, rationale: `Cascading research strategy from local project files to external sources`, alternatives: ['Direct web search', 'Manual code review'], }, ], learnings: { successes: research.confidence >= 0.8 ? ['High confidence research results obtained', 'Sufficient local context available'] : research.confidence >= 0.6 ? ['Moderate confidence results', 'Some local context found'] : [], failures: research.confidence < 0.6 ? [ 'Low confidence - insufficient local data', 'May need web search or manual research', ] : [], recommendations: research.confidence >= 0.8 ? ['Results can be used with confidence', 'Consider documenting findings in ADR'] : research.confidence >= 0.6 ? [ 'Validate findings with additional sources', 'Consider cross-referencing with documentation', ] : ['Perform web search for additional context', 'Manual research recommended'], environmentSpecific: [], }, relatedDocuments: { adrs: [], configs: [], otherContexts: [], }, rawData: { research: { answer: research.answer, confidence: research.confidence, sources: research.sources, needsWebSearch: research.needsWebSearch, metadata: research.metadata, }, }, }; await contextManager.saveContext('research', contextDoc); context?.info('๐Ÿ’พ Research context saved for future reference'); } catch (contextError) { // Don't fail the research if context saving fails context?.info(`โš ๏ธ Failed to save research context: ${contextError}`); } return { content: [ { type: 'text', text: response, }, ], }; } catch (error) { throw new McpAdrError( `Failed to perform research: ${error instanceof Error ? error.message : String(error)}`, 'RESEARCH_ERROR' ); } } /** * Format source name for display */ function formatSourceName(sourceType: string): string { const names: Record<string, string> = { project_files: '๐Ÿ“ Project Files', knowledge_graph: '๐Ÿง  Knowledge Graph', environment: '๐Ÿ”ง Environment Resources', web_search: '๐ŸŒ Web Search', }; return names[sourceType] || sourceType; } /** * Generate web search queries based on research question */ function generateSearchQueries(question: string): string[] { const queries: string[] = [question]; // Add variations const questionLower = question.toLowerCase(); if (questionLower.includes('what')) { queries.push(question.replace(/^what/i, 'how to')); } if (questionLower.includes('how')) { queries.push(question.replace(/^how/i, 'best practices for')); } // Add context-specific queries if (questionLower.includes('kubernetes') || questionLower.includes('k8s')) { queries.push(`${question} kubernetes best practices`); } if (questionLower.includes('docker')) { queries.push(`${question} docker production`); } if (questionLower.includes('openshift')) { queries.push(`${question} openshift documentation`); } return queries.slice(0, 3); // Limit to top 3 }

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/tosin2013/mcp-adr-analysis-server'

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