UNPKG

mcp-ai-agent-guidelines

Version:

A comprehensive Model Context Protocol server providing advanced tools, resources, and prompts for implementing AI agent best practices

349 lines (327 loc) 13.2 kB
// ADR Generator - Automated Architecture Decision Record generation import { z } from "zod"; import { logger } from "../shared/logger.js"; import { constraintManager } from "./constraint-manager.js"; const _ADRRequestSchema = z.object({ sessionState: z.any(), // DesignSessionState title: z.string(), context: z.string(), decision: z.string(), consequences: z.string().optional(), alternatives: z.array(z.string()).optional().default([]), status: z .enum(["proposed", "accepted", "deprecated", "superseded"]) .optional() .default("proposed"), metadata: z.record(z.unknown()).optional().default({}), }); class ADRGeneratorImpl { adrCounter = 1; async generateADR(request) { const { sessionState, title, context, decision, consequences, alternatives, status, metadata, } = request; // Generate ADR number const adrNumber = String(this.adrCounter++).padStart(4, "0"); const timestamp = new Date().toISOString(); // Get template specification const _adrFormat = constraintManager.getOutputFormatSpec("adr"); // Generate comprehensive ADR content const markdown = this.generateADRMarkdown({ number: adrNumber, title, context, decision, consequences: consequences || this.generateConsequences(decision, sessionState), alternatives: alternatives || [], status: status || "proposed", timestamp, sessionId: sessionState.config.sessionId, metadata, }); // Create artifact const artifact = { id: `adr-${adrNumber}`, name: `ADR-${adrNumber}: ${title}`, type: "adr", content: markdown, format: "markdown", timestamp, metadata: { adrNumber, status, sessionId: sessionState.config.sessionId, ...metadata, }, }; // Generate recommendations const recommendations = this.generateADRRecommendations(request); // Find related decisions const relatedDecisions = this.findRelatedDecisions(sessionState, decision); return { artifact, markdown, recommendations, relatedDecisions, }; } generateADRMarkdown(adr) { return `# ADR-${adr.number}: ${adr.title} **Date**: ${new Date(adr.timestamp).toLocaleDateString()} **Status**: ${adr.status.toUpperCase()} **Session**: ${adr.sessionId} ## Context ${adr.context} ## Decision ${adr.decision} ## Consequences ${adr.consequences} ${adr.alternatives.length > 0 ? `## Alternatives Considered ${adr.alternatives .map((alt, index) => `### Alternative ${index + 1} ${alt}`) .join("\n\n")}` : ""} ## Implementation Notes - **Priority**: High - **Effort**: TBD - **Risk Level**: Medium - **Dependencies**: See related ADRs ## Validation Criteria - [ ] Technical feasibility confirmed - [ ] Security implications assessed - [ ] Performance impact evaluated - [ ] Documentation updated - [ ] Team consensus achieved ## Related Decisions - See session artifacts for related design decisions - Review constraint validation results ## Review History | Date | Reviewer | Action | Notes | |------|----------|--------|-------| | ${new Date(adr.timestamp).toLocaleDateString()} | Design Assistant | Created | Initial ADR generation | --- *Generated by MCP Design Assistant ADR Generator* *ADR Format: ${constraintManager.getOutputFormatSpec("adr")?.template || "Standard"}* ${adr.metadata && Object.keys(adr.metadata).length > 0 ? ` ## Metadata \`\`\`json ${JSON.stringify(adr.metadata, null, 2)} \`\`\` ` : ""}`; } generateConsequences(decision, _sessionState) { const consequences = []; // Analyze decision for potential consequences const decisionLower = decision.toLowerCase(); // Technical consequences if (decisionLower.includes("microservice") || decisionLower.includes("distributed")) { consequences.push("**Technical**: Increased system complexity and network latency"); consequences.push("**Operational**: Requires advanced monitoring and deployment strategies"); } if (decisionLower.includes("monolith") || decisionLower.includes("single")) { consequences.push("**Technical**: Simpler deployment but potential scaling limitations"); consequences.push("**Development**: Easier local development and testing"); } if (decisionLower.includes("cloud") || decisionLower.includes("aws") || decisionLower.includes("azure")) { consequences.push("**Cost**: Variable operational costs based on usage"); consequences.push("**Vendor**: Potential vendor lock-in considerations"); } if (decisionLower.includes("database") || decisionLower.includes("storage")) { consequences.push("**Data**: Data consistency and backup strategies required"); consequences.push("**Performance**: Query optimization and indexing needed"); } // Business consequences if (decisionLower.includes("compliance") || decisionLower.includes("security")) { consequences.push("**Compliance**: Additional audit and certification requirements"); consequences.push("**Risk**: Enhanced security posture but implementation overhead"); } // Default consequences if none detected if (consequences.length === 0) { consequences.push("**Positive**: Addresses identified requirements and constraints"); consequences.push("**Negative**: Requires implementation effort and potential technical debt"); consequences.push("**Neutral**: Should be monitored for long-term impact"); } return consequences.join("\n\n"); } generateADRRecommendations(request) { const recommendations = []; const { decision } = request; // Check against constraints const validation = constraintManager.validateConstraints(decision); if (!validation.passed) { recommendations.push("Address constraint violations before accepting this ADR"); recommendations.push(...validation.recommendations.slice(0, 2)); } // General ADR recommendations recommendations.push("Review ADR with relevant stakeholders before implementation"); recommendations.push("Update related documentation and architecture diagrams"); recommendations.push("Plan implementation timeline and resource allocation"); // Context-specific recommendations if (decision.toLowerCase().includes("new technology")) { recommendations.push("Conduct proof-of-concept before full implementation"); recommendations.push("Plan team training and knowledge transfer"); } if (decision.toLowerCase().includes("security") || decision.toLowerCase().includes("auth")) { recommendations.push("Involve security team in implementation planning"); recommendations.push("Plan security testing and penetration testing"); } return recommendations; } findRelatedDecisions(sessionState, decision) { const related = []; // Look through session artifacts for related decisions const existingADRs = sessionState.artifacts.filter((a) => a.type === "adr"); const decisionKeywords = this.extractKeywords(decision); for (const adr of existingADRs) { const adrKeywords = this.extractKeywords(adr.content); const overlap = decisionKeywords.filter((k) => adrKeywords.includes(k)); if (overlap.length > 0) { related.push(`${adr.name} (${overlap.length} shared concepts)`); } } // Add general relationships if (decision.toLowerCase().includes("database")) { related.push("Data architecture decisions"); related.push("Security and privacy decisions"); } if (decision.toLowerCase().includes("api")) { related.push("Interface design decisions"); related.push("Authentication and authorization decisions"); } return related; } extractKeywords(text) { const keywords = []; const techTerms = [ "api", "database", "microservice", "monolith", "cloud", "aws", "azure", "gcp", "security", "auth", "oauth", "jwt", "ssl", "https", "encryption", "docker", "kubernetes", "container", "serverless", "lambda", "react", "angular", "vue", "node", "python", "java", "typescript", "postgresql", "mysql", "mongodb", "redis", "elasticsearch", "rest", "graphql", "grpc", "websocket", "webhook", ]; const textLower = text.toLowerCase(); for (const term of techTerms) { if (textLower.includes(term)) { keywords.push(term); } } return keywords; } async generateSessionADRs(sessionState) { const adrs = []; // Generate ADRs for major decisions made during each phase for (const [phaseId, phase] of Object.entries(sessionState.phases)) { if (phase.status === "completed" && phase.artifacts.length > 0) { // Look for decision-worthy content in phase artifacts const decisions = this.extractDecisionsFromPhase(phase); for (const decision of decisions) { try { const adrResult = await this.generateADR({ sessionState, title: `${phase.name} - ${decision.title}`, context: `Decision made during ${phase.name} phase of design session ${sessionState.config.sessionId}`, decision: decision.content, status: "accepted", metadata: { phaseId, autoGenerated: true, }, }); adrs.push(adrResult.artifact); } catch (error) { logger.warn("Failed to generate ADR for phase", { phaseId, error: error instanceof Error ? error.message : String(error), }); } } } } return adrs; } extractDecisionsFromPhase(phase) { const decisions = []; // Look for decision patterns in phase content for (const artifact of phase.artifacts) { const content = artifact.content; // Look for "decided", "chosen", "selected" patterns const decisionPatterns = [ /decided to (.*?)(?:\n|\.)/gi, /chosen (.*?)(?:\n|\.)/gi, /selected (.*?)(?:\n|\.)/gi, /will use (.*?)(?:\n|\.)/gi, /approach: (.*?)(?:\n|\.)/gi, ]; for (const pattern of decisionPatterns) { const matches = Array.from(content.matchAll(pattern)); for (const match of matches) { const matchResult = match; if (matchResult?.[1] && typeof matchResult[1] === "string" && matchResult[1].trim().length > 10) { decisions.push({ title: matchResult[1].trim().slice(0, 50) + (matchResult[1].length > 50 ? "..." : ""), content: matchResult[1].trim(), }); } } } } // Ensure we have at least one decision per completed phase if (decisions.length === 0 && phase.status === "completed") { decisions.push({ title: `${phase.name} Completion`, content: `Completed ${phase.name} phase with ${phase.coverage.toFixed(1)}% coverage`, }); } return decisions; } } // Export singleton instance export const adrGenerator = new ADRGeneratorImpl(); // Module Implementation Status Sentinel export const IMPLEMENTATION_STATUS = "IMPLEMENTED"; //# sourceMappingURL=adr-generator.js.map