mcp-adr-analysis-server
Version:
MCP server for analyzing Architectural Decision Records and project architecture
200 lines • 6.59 kB
JavaScript
/**
* Pattern by Name Resource - Individual pattern analysis
* URI Pattern: adr://pattern/{name}
*/
import { McpAdrError } from '../types/index.js';
import { resourceCache, generateETag } from './resource-cache.js';
import { resourceRouter } from './resource-router.js';
/**
* Find ADRs that reference this pattern
*/
async function findReferencingAdrs(patternName) {
const referencingAdrs = [];
try {
const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js');
const path = await import('path');
const adrDirectory = path.resolve(process.cwd(), process.env['ADR_DIRECTORY'] || 'docs/adrs');
const result = await discoverAdrsInDirectory(adrDirectory, process.cwd(), {
includeContent: true,
includeTimeline: false,
});
// Find ADRs that mention this pattern
for (const adr of result.adrs) {
const content = `${adr.title} ${adr.context || ''} ${adr.decision || ''}`.toLowerCase();
if (content.includes(patternName.toLowerCase())) {
referencingAdrs.push({
id: adr.filename.replace(/\.md$/, ''),
title: adr.title || 'Untitled',
status: adr.status || 'unknown',
});
}
}
}
catch {
// No ADRs found
}
return referencingAdrs;
}
/**
* Find related technologies (placeholder)
*/
function findRelatedTechnologies(_name) {
// Placeholder implementation
return ['TypeScript', 'Node.js'];
}
/**
* Get pattern relationships (placeholder)
*/
function getPatternRelationships(_name) {
// Placeholder implementation
return {
relatedPatterns: [],
alternativePatterns: [],
complementaryPatterns: [],
};
}
/**
* Assess pattern quality
*/
function assessPatternQuality(pattern, adrsReferencing) {
// Determine complexity based on pattern type
let complexity = 'medium';
if (pattern.category === 'creational')
complexity = 'low';
else if (pattern.category === 'structural')
complexity = 'medium';
else if (pattern.category === 'behavioral')
complexity = 'high';
// Maintainability based on usage
const acceptedAdrs = adrsReferencing.filter(a => a.status === 'accepted').length;
const maintainability = acceptedAdrs >= 3 ? 90 : acceptedAdrs >= 1 ? 70 : 50;
// Testability (placeholder - would require actual analysis)
const testability = complexity === 'low' ? 90 : complexity === 'medium' ? 70 : 50;
// Documentation based on ADRs
const documentation = adrsReferencing.length > 0 ? 80 : 40;
return {
complexity,
maintainability,
testability,
documentation,
};
}
/**
* Generate pattern examples
*/
function generatePatternExamples(patternName) {
// Placeholder - would extract from ADRs or code
return [
{
title: `${patternName} Implementation`,
description: `Example usage of ${patternName} pattern in the codebase`,
codeReference: 'See relevant ADRs for implementation details',
},
];
}
/**
* Get pattern metadata
*/
function getPatternMetadata(pattern) {
return {
firstUsed: pattern.introducedDate || new Date().toISOString(),
lastUpdated: new Date().toISOString(),
maturity: pattern.maturity || 'proven',
references: pattern.references || [],
};
}
/**
* Get pattern intent and applicability
*/
function getPatternDetails(pattern) {
return {
intent: pattern.intent || `Design pattern for ${pattern.name}`,
applicability: pattern.applicability || ['General purpose'],
structure: {
participants: pattern.participants || [],
collaborations: pattern.collaborations || [],
},
};
}
/**
* Get known patterns (placeholder)
*/
function getKnownPatterns() {
return [
{
name: 'MVC',
category: 'architectural',
description: 'Model-View-Controller pattern',
},
{
name: 'Repository',
category: 'data',
description: 'Repository pattern for data access',
},
{
name: 'Singleton',
category: 'creational',
description: 'Singleton pattern for single instance',
},
];
}
/**
* Generate pattern by name resource
*/
export async function generatePatternByNameResource(params, _searchParams) {
const name = params['name'];
if (!name) {
throw new McpAdrError('Missing required parameter: name', 'INVALID_PARAMS');
}
const cacheKey = `pattern:${name}`;
// Check cache
const cached = await resourceCache.get(cacheKey);
if (cached) {
return cached;
}
// Find pattern
const allPatterns = getKnownPatterns();
const pattern = allPatterns.find((p) => p.name.toLowerCase() === name.toLowerCase());
if (!pattern) {
throw new McpAdrError(`Pattern not found: ${name}`, 'RESOURCE_NOT_FOUND');
}
// Gather pattern details
const adrsReferencing = await findReferencingAdrs(name);
const technologiesUsed = findRelatedTechnologies(name);
const relationships = getPatternRelationships(name);
const quality = assessPatternQuality(pattern, adrsReferencing);
const patternInfo = getPatternDetails(pattern);
const examples = generatePatternExamples(name);
const metadata = getPatternMetadata(pattern);
const patternDetails = {
name: pattern.name,
category: pattern.category || 'uncategorized',
description: pattern.description || `Design pattern: ${pattern.name}`,
intent: patternInfo.intent,
applicability: patternInfo.applicability,
structure: patternInfo.structure,
usage: {
adrsReferencing,
technologiesUsed,
implementationCount: adrsReferencing.length,
},
relationships,
quality,
examples,
metadata,
};
const result = {
data: patternDetails,
contentType: 'application/json',
lastModified: new Date().toISOString(),
cacheKey,
ttl: 300, // 5 minutes cache
etag: generateETag(patternDetails),
};
// Cache result
resourceCache.set(cacheKey, result, result.ttl);
return result;
}
// Register route
resourceRouter.register('/pattern/{name}', generatePatternByNameResource, 'Individual pattern analysis by name');
//# sourceMappingURL=pattern-by-name-resource.js.map