Skip to main content
Glama

discover_existing_adrs

Identify and catalog all existing architectural decision records (ADRs) in a specified project directory, optionally including content for enhanced analysis.

Instructions

Discover and catalog existing ADRs in the project

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
adrDirectoryNoDirectory to search for ADRsdocs/adrs
includeContentNoWhether to include ADR content in analysis

Implementation Reference

  • Main MCP tool handler for discovering existing ADRs. Initializes cache infrastructure (.mcp-adr-cache), discovers ADRs using helper, formats MCP response with summary, statistics, and recommendations.
    export async function discoverExistingAdrs(args: { adrDirectory?: string; includeContent?: boolean; projectPath?: string; }): Promise<any> { const { adrDirectory = 'docs/adrs', includeContent = false, projectPath = process.cwd() } = args; try { // INITIALIZE COMPLETE CACHE INFRASTRUCTURE (since this is typically the first command) console.log('🚀 Initializing complete cache infrastructure...'); // 1. TodoJsonManager removed - use mcp-shrimp-task-manager for task management console.warn( '⚠️ TodoJsonManager is deprecated and was removed in memory-centric transformation' ); // Skip todo initialization - TodoJsonManager removed console.log('✅ Initialized todo-data.json and cache directory'); // 2. ProjectHealthScoring removed - use relationship-based importance instead console.warn( '⚠️ ProjectHealthScoring is deprecated and was removed in memory-centric transformation' ); // Skip health scoring initialization - ProjectHealthScoring removed console.log('✅ Initialized project-health-scores.json'); // 3. Initialize KnowledgeGraphManager (creates knowledge-graph-snapshots.json and todo-sync-state.json) // Set PROJECT_PATH temporarily for proper initialization const originalConfig = process.env['PROJECT_PATH']; process.env['PROJECT_PATH'] = projectPath; const { KnowledgeGraphManager } = await import('../utils/knowledge-graph-manager.js'); const kgManager = new KnowledgeGraphManager(); await kgManager.loadKnowledgeGraph(); // Creates knowledge-graph-snapshots.json and todo-sync-state.json console.log('✅ Initialized knowledge-graph-snapshots.json and todo-sync-state.json'); // Restore original config if (originalConfig !== undefined) { process.env['PROJECT_PATH'] = originalConfig; } else { delete process.env['PROJECT_PATH']; } console.log('🎯 Complete cache infrastructure ready!'); // Use the new ADR discovery utility const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js'); const discoveryResult = await discoverAdrsInDirectory(adrDirectory, projectPath, { includeContent, includeTimeline: false, }); // Format the results for MCP response return { content: [ { type: 'text', text: `# 🎯 Complete ADR Discovery & Cache Infrastructure Initialized ## Cache Infrastructure Status ✅ **todo-data.json** - JSON-first TODO system initialized ✅ **project-health-scores.json** - Multi-component project health scoring ✅ **knowledge-graph-snapshots.json** - Knowledge graph system & intent tracking ✅ **todo-sync-state.json** - TODO synchronization state ✅ **Cache Directory** - Complete infrastructure ready at \`.mcp-adr-cache/\` ## ADR Discovery Results ### Discovery Summary - **Directory**: ${discoveryResult.directory} - **Total ADRs Found**: ${discoveryResult.totalAdrs} - **Include Content**: ${includeContent ? 'Yes' : 'No (metadata only)'} ## Discovered ADRs ${ discoveryResult.adrs.length > 0 ? discoveryResult.adrs .map( adr => ` ### ${adr.title} - **File**: ${adr.filename} - **Status**: ${adr.status} - **Date**: ${adr.date || 'Not specified'} - **Path**: ${adr.path} ${adr.metadata?.number ? `- **Number**: ${adr.metadata.number}` : ''} ${adr.metadata?.category ? `- **Category**: ${adr.metadata.category}` : ''} ${adr.metadata?.tags?.length ? `- **Tags**: ${adr.metadata.tags.join(', ')}` : ''} ${ includeContent && adr.content ? ` #### Content Preview \`\`\`markdown ${adr.content.slice(0, 500)}${adr.content.length > 500 ? '...' : ''} \`\`\` ` : '' } ` ) .join('\n') : 'No ADRs found in the specified directory.' } ## Summary Statistics ### By Status ${ Object.entries(discoveryResult.summary.byStatus) .map(([status, count]) => `- **${status}**: ${count}`) .join('\n') || 'No status information available' } ### By Category ${ Object.entries(discoveryResult.summary.byCategory) .map(([category, count]) => `- **${category}**: ${count}`) .join('\n') || 'No category information available' } ## Recommendations ${discoveryResult.recommendations.map(rec => `- ${rec}`).join('\n')} ## Next Steps Based on the discovered ADRs, you can: 1. **Analyze for Missing Decisions**: Use the \`suggest_adrs\` tool with the discovered ADR titles 2. **Generate Implementation TODOs**: Use the \`generate_adr_todo\` tool 3. **Create New ADRs**: Use the \`generate_adr_from_decision\` tool for new decisions ### Example Commands To suggest new ADRs based on discovered ones: \`\`\`json { "tool": "suggest_adrs", "args": { "existingAdrs": ${JSON.stringify(discoveryResult.adrs.map(adr => adr.title))}, "analysisType": "comprehensive" } } \`\`\` To generate a todo list from discovered ADRs: \`\`\`json { "tool": "generate_adr_todo", "args": { "adrDirectory": "${adrDirectory}", "scope": "all" } } \`\`\` ## Raw Discovery Data For programmatic use, the raw discovery data is: \`\`\`json ${JSON.stringify(discoveryResult, null, 2)} \`\`\` `, }, ], }; } catch (error) { throw new McpAdrError( `Failed to discover ADRs: ${error instanceof Error ? error.message : String(error)}`, 'DISCOVERY_ERROR' ); } }
  • Core helper function that scans ADR directory, identifies ADR files, extracts metadata (title, status, date, etc.), generates summaries and recommendations. Called by the handler.
    export async function discoverAdrsInDirectory( adrDirectory: string, projectPath: string, options: AdrDiscoveryOptions = {} ): Promise<AdrDiscoveryResult> { const { includeContent = false, includeTimeline = true, // Default to true for smart extraction timelineOptions = {}, generateActions = false, thresholdProfile, autoDetectContext = true, } = options; try { const fs = await import('fs/promises'); const path = await import('path'); // Try multiple strategies to resolve the correct ADR directory path // This handles cases where: // 1. projectPath is already in a subdirectory (e.g., /project/docs) // 2. adrDirectory is relative (e.g., "docs/adrs") // 3. adrDirectory is already absolute let fullAdrPath: string; let dirExists = false; // Strategy 1: Try resolving as-is (standard behavior) fullAdrPath = path.resolve(projectPath, adrDirectory); try { const stat = await fs.stat(fullAdrPath); dirExists = stat.isDirectory(); } catch { dirExists = false; } // Strategy 2: If that fails and adrDirectory contains the same path segment as projectPath, // the projectPath might be pointing to a subdirectory. Try stripping the common suffix. if (!dirExists && adrDirectory.includes(path.sep)) { const projectPathParts = projectPath.split(path.sep); const adrDirParts = adrDirectory.split(path.sep); // Check if projectPath ends with the first part of adrDirectory // e.g., projectPath ends with "docs" and adrDirectory starts with "docs/adrs" const lastProjectPart = projectPathParts[projectPathParts.length - 1]; if (lastProjectPart && lastProjectPart === adrDirParts[0]) { // Remove the redundant first part from adrDirectory const adjustedAdrDir = adrDirParts.slice(1).join(path.sep); fullAdrPath = path.resolve(projectPath, adjustedAdrDir); try { const stat = await fs.stat(fullAdrPath); dirExists = stat.isDirectory(); } catch { dirExists = false; } } } // Strategy 3: If still not found, try checking one level up from projectPath if (!dirExists) { const parentPath = path.dirname(projectPath); fullAdrPath = path.resolve(parentPath, adrDirectory); try { const stat = await fs.stat(fullAdrPath); dirExists = stat.isDirectory(); } catch { dirExists = false; } } if (!dirExists) { return { directory: adrDirectory, totalAdrs: 0, adrs: [], summary: { byStatus: {}, byCategory: {} }, recommendations: [ `ADR directory '${adrDirectory}' does not exist (tried multiple path resolutions)`, `Attempted paths: ${path.resolve(projectPath, adrDirectory)}, ${path.resolve(path.dirname(projectPath), adrDirectory)}`, 'Consider creating the directory and adding your first ADR', 'Use the generate_adr_from_decision tool to create new ADRs', ], }; } // Read directory contents const entries = await fs.readdir(fullAdrPath, { withFileTypes: true }); const markdownFiles = entries .filter(entry => entry.isFile() && entry.name.endsWith('.md')) .map(entry => entry.name); // Process each markdown file to check if it's an ADR const discoveredAdrs: DiscoveredAdr[] = []; for (const filename of markdownFiles) { const filePath = path.join(fullAdrPath, filename); try { // Read the file content to check if it's an ADR const content = await fs.readFile(filePath, 'utf-8'); // Check if this looks like an ADR const isAdr = isLikelyAdr(content, filename); if (isAdr) { const adr = parseAdrMetadata(content, filename, path.join(adrDirectory, filename)); if (includeContent) { adr.content = content; } // Extract timeline if requested if (includeTimeline) { try { const { extractBasicTimeline } = await import('./adr-timeline-extractor.js'); adr.timeline = await extractBasicTimeline(filePath, content, timelineOptions); } catch (error) { console.warn(`[Timeline] Failed to extract timeline for ${filename}:`, error); // Continue without timeline data } } discoveredAdrs.push(adr); } } catch (error) { console.error(`[WARN] Failed to read file ${filename}:`, error); } } // Generate summary const summary = generateAdrSummary(discoveredAdrs); // Generate recommendations const recommendations = generateRecommendations(discoveredAdrs, adrDirectory); // Generate action items if requested let actionQueue: AdrWorkQueue | undefined; if (generateActions && discoveredAdrs.some((adr) => adr.timeline)) { try { const { detectProjectContext, selectThresholdProfile } = await import( './adr-context-detector.js' ); const { generateActionItems } = await import('./adr-action-analyzer.js'); // Detect project context or use manual profile let selectedProfile; if (autoDetectContext && !thresholdProfile) { const context = await detectProjectContext(projectPath, discoveredAdrs); selectedProfile = selectThresholdProfile(context); } else { const { THRESHOLD_PROFILES } = await import('./adr-context-detector.js'); selectedProfile = THRESHOLD_PROFILES[thresholdProfile || 'mature'] || THRESHOLD_PROFILES['mature']; } // Generate action items actionQueue = await generateActionItems(discoveredAdrs, selectedProfile!, { useAdrTypeModifiers: true, projectPath, }); // Add action summary to summary object (summary as any).totalActionsRequired = actionQueue.summary.totalActions; (summary as any).criticalActions = actionQueue.summary.criticalCount; } catch (error) { console.warn('[Actions] Failed to generate action items:', error); // Continue without action items } } return { directory: adrDirectory, totalAdrs: discoveredAdrs.length, adrs: discoveredAdrs, summary, recommendations, ...(actionQueue ? { actionQueue } : {}), }; } catch (error) { throw new McpAdrError( `Failed to discover ADRs: ${error instanceof Error ? error.message : String(error)}`, 'DISCOVERY_ERROR' ); }
  • Type definitions for ADR discovery results and individual discovered ADRs, used by the helper and handler for structured output.
    export interface DiscoveredAdr { /** Filename of the ADR file */ filename: string; /** Title extracted from the ADR */ title: string; /** Status of the decision (proposed, accepted, deprecated, etc.) */ status: string; /** Date when the decision was made */ date: string | undefined; /** Full file path to the ADR */ path: string; /** Full content of the ADR file (optional) */ content?: string; /** Context section of the ADR */ context?: string; /** Decision section of the ADR */ decision?: string; /** Consequences section of the ADR */ consequences?: string; /** Additional metadata about the ADR */ metadata?: { /** ADR number or identifier */ number?: string; /** Category or domain of the decision */ category?: string; /** Tags for categorization */ tags?: string[]; }; /** Timeline information (creation, updates, staleness) */ timeline?: BasicTimeline; } /** * Options for ADR discovery */ export interface AdrDiscoveryOptions { /** Include full ADR content */ includeContent?: boolean; /** Include timeline analysis */ includeTimeline?: boolean; /** Timeline extraction options */ timelineOptions?: TimelineExtractionOptions; /** Generate action items */ generateActions?: boolean; /** Threshold profile for action generation */ thresholdProfile?: string; /** Auto-detect project context */ autoDetectContext?: boolean; } /** * Result of ADR discovery operation */ export interface AdrDiscoveryResult { /** Directory where ADRs were discovered */ directory: string; /** Total number of ADRs found */ totalAdrs: number; /** Array of discovered ADRs */ adrs: DiscoveredAdr[]; /** Summary statistics of discovered ADRs */ summary: { /** Count of ADRs by status */ byStatus: Record<string, number>; /** Count of ADRs by category */ byCategory: Record<string, number>; /** Total actions required (if generateActions=true) */ totalActionsRequired?: number; /** Critical actions (if generateActions=true) */ criticalActions?: number; }; /** Recommendations for improving ADR management */ recommendations: string[]; /** Action queue (if generateActions=true) */ actionQueue?: AdrWorkQueue; }
  • Tool name and description listed in server context generator for documentation/reference.
    name: 'discover_existing_adrs', description: 'Discover and index existing ADRs in project', },

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

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