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
JavaScript
// 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