Skip to main content
Glama
adrs-discovered-resource.ts7.17 kB
/** * ADRs Discovered Resource - ADR discovery and summary * URI Pattern: adr://adrs/discovered */ import { URLSearchParams } from 'url'; import * as path from 'path'; import { McpAdrError } from '../types/index.js'; import { resourceCache, generateETag } from './resource-cache.js'; import { ResourceGenerationResult } from './index.js'; import { resourceRouter } from './resource-router.js'; export interface AdrsDiscoveredData { directory: string; totalAdrs: number; adrs: Array<{ filename: string; title: string; status: string; date?: string; path: string; number?: string; category?: string; tags?: string[]; }>; summary: { byStatus: Record<string, number>; byCategory: Record<string, number>; }; recommendations: string[]; timestamp: string; } /** * Generate ADRs discovered resource with discovery results and summary. * * Discovers and catalogs all Architectural Decision Records (ADRs) in a project: * - Scans configured ADR directory for decision records * - Extracts metadata (title, status, date, category) * - Generates summary statistics by status and category * - Provides recommendations for ADR management * * **URI Pattern:** `adr://adrs/discovered` * * **Query Parameters:** * - `projectPath`: Override project root path (default: process.cwd()) * - `adrDirectory`: Override ADR directory (default: docs/adrs) * - `includeContent`: Include full ADR content (default: false) * - `includeTimeline`: Include timeline analysis (default: false) * * @param params - URL path parameters (none for this resource) * @param searchParams - URL query parameters for customization * * @returns Promise resolving to resource generation result containing: * - data: ADR discovery results with summaries * - contentType: "application/json" * - lastModified: ISO timestamp of generation * - cacheKey: Unique identifier "adrs-discovered" or "adrs-discovered:{projectPath}" * - ttl: Cache duration (120 seconds / 2 minutes) * - etag: Entity tag for cache validation * * @throws {McpAdrError} When: * - RESOURCE_NOT_FOUND: ADR directory not found or inaccessible * - RESOURCE_GENERATION_ERROR: ADR discovery or parsing fails * * @example * ```typescript * // Discover ADRs in default location * const discovered = await generateAdrsDiscoveredResource( * {}, * new URLSearchParams() * ); * * console.log(`Found ${discovered.data.totalAdrs} ADRs`); * console.log(`Accepted: ${discovered.data.summary.byStatus.accepted}`); * console.log(`Proposed: ${discovered.data.summary.byStatus.proposed}`); * * // Discover ADRs in custom location * const custom = await generateAdrsDiscoveredResource( * {}, * new URLSearchParams('projectPath=/custom/path&adrDirectory=decisions') * ); * * // Expected output structure: * { * data: { * directory: "/project/docs/adrs", * totalAdrs: 25, * adrs: [ * { * filename: "001-use-postgresql.md", * title: "Use PostgreSQL for primary database", * status: "accepted", * date: "2025-01-15", * path: "/project/docs/adrs/001-use-postgresql.md", * number: "001", * category: "database", * tags: ["postgresql", "database"] * }, * ... * ], * summary: { * byStatus: { * accepted: 20, * proposed: 3, * deprecated: 2 * }, * byCategory: { * database: 5, * api: 8, * infrastructure: 7, * security: 5 * } * }, * recommendations: [ * "Consider reviewing deprecated ADRs", * "3 proposed ADRs need decisions" * ], * timestamp: "2025-12-16T04:30:00.000Z" * }, * contentType: "application/json", * cacheKey: "adrs-discovered", * ttl: 120 * } * ``` * * @since v2.2.0 * @see {@link discoverAdrsInDirectory} for ADR discovery logic */ export async function generateAdrsDiscoveredResource( _params: Record<string, string>, searchParams: URLSearchParams ): Promise<ResourceGenerationResult> { const projectPath = searchParams.get('projectPath') || process.cwd(); const adrDirectoryParam = searchParams.get('adrDirectory') || process.env['ADR_DIRECTORY'] || 'docs/adrs'; const includeContent = searchParams.get('includeContent') === 'true'; const includeTimeline = searchParams.get('includeTimeline') === 'true'; const cacheKey = projectPath === process.cwd() ? 'adrs-discovered' : `adrs-discovered:${projectPath}`; // Check cache const cached = await resourceCache.get<ResourceGenerationResult>(cacheKey); if (cached) { return cached; } try { // Dynamically import ADR discovery to avoid circular dependencies const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js'); // Resolve ADR directory const adrDirectory = path.resolve(projectPath, adrDirectoryParam); // Discover ADRs const discoveryResult = await discoverAdrsInDirectory(adrDirectory, projectPath, { includeContent, includeTimeline, }); // Format ADR list (exclude content for lighter response) const adrs = discoveryResult.adrs.map(adr => { const formatted: { filename: string; title: string; status: string; date?: string; path: string; number?: string; category?: string; tags?: string[]; } = { filename: adr.filename, title: adr.title, status: adr.status, path: adr.path, }; if (adr.date !== undefined) formatted.date = adr.date; if (adr.metadata?.number !== undefined) formatted.number = adr.metadata.number; if (adr.metadata?.category !== undefined) formatted.category = adr.metadata.category; if (adr.metadata?.tags !== undefined) formatted.tags = adr.metadata.tags; return formatted; }); const discoveredData: AdrsDiscoveredData = { directory: discoveryResult.directory, totalAdrs: discoveryResult.totalAdrs, adrs, summary: discoveryResult.summary, recommendations: discoveryResult.recommendations, timestamp: new Date().toISOString(), }; const result: ResourceGenerationResult = { data: discoveredData, contentType: 'application/json', lastModified: new Date().toISOString(), cacheKey, ttl: 120, // 2 minutes cache (ADRs change occasionally) etag: generateETag(discoveredData), }; // Cache result resourceCache.set(cacheKey, result, result.ttl); return result; } catch (error) { // Check if it's a directory not found error if (error instanceof Error && error.message.includes('not found')) { throw new McpAdrError( `ADR directory not found: ${adrDirectoryParam}`, 'RESOURCE_NOT_FOUND' ); } throw new McpAdrError( `Failed to discover ADRs: ${error instanceof Error ? error.message : String(error)}`, 'RESOURCE_GENERATION_ERROR' ); } } // Register route resourceRouter.register('/adrs/discovered', generateAdrsDiscoveredResource, 'Discovered ADRs with summary');

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