mcp-ai-agent-guidelines
Version:
A comprehensive Model Context Protocol server providing advanced tools, resources, and prompts for implementing AI agent best practices
181 lines • 7.39 kB
JavaScript
/**
* Mermaid Diagram Generator - Main Orchestrator
* Refactored to use Strategy pattern with handler registry.
*/
import { ClassHandler, ERHandler, FlowchartHandler, GanttHandler, GitGraphHandler, JourneyHandler, MindmapHandler, PieHandler, QuadrantHandler, SequenceHandler, StateHandler, TimelineHandler, } from "./handlers/index.js";
import { MermaidDiagramSchema } from "./types.js";
import { prepareAccessibilityComments } from "./utils/accessibility.utils.js";
import { fallbackDiagram, repairDiagram } from "./utils/repair.utils.js";
import { validateDiagram } from "./validator.js";
/**
* Handler registry - Maps diagram types to their handlers.
* Uses Strategy pattern for O(1) handler lookup.
*/
const HANDLER_REGISTRY = {};
HANDLER_REGISTRY.flowchart = new FlowchartHandler();
HANDLER_REGISTRY.sequence = new SequenceHandler();
HANDLER_REGISTRY.class = new ClassHandler();
HANDLER_REGISTRY.state = new StateHandler();
HANDLER_REGISTRY.gantt = new GanttHandler();
HANDLER_REGISTRY.pie = new PieHandler();
HANDLER_REGISTRY.er = new ERHandler();
HANDLER_REGISTRY.journey = new JourneyHandler();
HANDLER_REGISTRY.quadrant = new QuadrantHandler();
HANDLER_REGISTRY["git-graph"] = new GitGraphHandler();
HANDLER_REGISTRY.mindmap = new MindmapHandler();
HANDLER_REGISTRY.timeline = new TimelineHandler();
/**
* Normalize legacy diagram type names.
* @param args - Raw input arguments
* @returns Normalized arguments
*/
function normalizeLegacyTypes(args) {
if (args && typeof args === "object" && args !== null) {
const obj = args;
// Handle legacy diagram type names
const legacyMappings = {
erDiagram: "er",
graph: "flowchart",
userJourney: "journey",
gitgraph: "git-graph",
gitGraph: "git-graph",
};
const currentType = obj.diagramType;
if (currentType && legacyMappings[currentType]) {
return { ...obj, diagramType: legacyMappings[currentType] };
}
}
return args;
}
/**
* Generate Mermaid diagram using appropriate handler.
* @param input - Validated diagram input
* @returns Generated diagram code
*/
function generateDiagram(input) {
const handler = HANDLER_REGISTRY[input.diagramType];
if (!handler) {
throw new Error(`Unknown diagram type: ${input.diagramType}`);
}
// Pass direction as advanced feature if provided (for flowchart)
const advancedFeatures = {
...input.advancedFeatures,
direction: input.direction,
};
return handler.generate(input.description, input.theme, advancedFeatures);
}
/**
* Format output response.
* @param input - Diagram input
* @param diagram - Generated diagram code
* @param validation - Validation result
* @param repaired - Whether diagram was repaired
* @returns Formatted MCP response
*/
function formatResponse(input, diagram, validation, repaired) {
const validityNote = validation.valid
? validation.skipped
? `ℹ️ Validation skipped (mermaid not available). Diagram generated.`
: `✅ Diagram validated successfully${repaired ? " (after auto-repair)" : ""}.`
: `❌ Diagram invalid even after attempts: ${validation.error}`;
const feedback = validation.valid
? ""
: [
"### Feedback Loop",
"- Try simplifying node labels (avoid punctuation that Mermaid may misparse)",
"- Ensure a single diagram header (e.g., 'flowchart TD')",
"- Replace complex punctuation with plain words",
"- If describing a pipeline, try a simpler 5-step flow and add branches gradually",
].join("\n");
return {
content: [
{
type: "text",
text: [
"## Generated Mermaid Diagram",
"",
"### Description",
input.description,
"",
"### Diagram Code",
"```mermaid",
diagram,
"```",
"",
"### Accessibility",
input.accTitle || input.accDescr
? [
input.accTitle ? `- Title: ${input.accTitle}` : undefined,
input.accDescr ? `- Description: ${input.accDescr}` : undefined,
]
.filter(Boolean)
.join("\n")
: "- You can provide accTitle and accDescr to improve screen reader context.",
"",
"### Validation",
validityNote,
feedback,
"",
"### Generation Settings",
`Type: ${input.diagramType}`,
`Strict: ${input.strict}`,
`Repair: ${input.repair}`,
"",
"### Usage Instructions",
"1. Copy the Mermaid code above",
"2. Paste it into any Mermaid-enabled Markdown renderer or the Live Editor",
"3. Adjust styling, layout, or relationships as needed",
"",
"### Notes",
"Repair heuristics: classDef style tokens normalized, ensures colon syntax, fallback to minimal diagram if unrecoverable.",
].join("\n"),
},
],
};
}
/**
* Main entry point for Mermaid diagram generation.
* @param args - Raw input arguments (will be validated)
* @returns MCP response with generated diagram
*/
export async function mermaidDiagramGenerator(args) {
// Normalize legacy type names
const normalized = normalizeLegacyTypes(args);
// Validate input
const input = MermaidDiagramSchema.parse(normalized);
// Generate diagram using appropriate handler
let diagram = generateDiagram(input);
// Prepend accessibility comments if provided
const accComments = prepareAccessibilityComments(input.accTitle, input.accDescr);
if (accComments) {
diagram = `${accComments}\n${diagram}`;
}
// Validate diagram
let validation = await validateDiagram(diagram);
let repaired = false;
// Attempt repair if validation fails
if (!validation.valid && input.repair) {
const attempt = repairDiagram(diagram);
if (attempt !== diagram) {
diagram = attempt;
validation = await validateDiagram(diagram);
repaired = validation.valid;
}
}
// Use fallback if still invalid and strict mode is enabled
if (!validation.valid && input.strict) {
diagram = fallbackDiagram();
validation = await validateDiagram(diagram);
}
return formatResponse(input, diagram, validation, repaired);
}
// Re-export types and utilities for testing
export { MermaidDiagramSchema } from "./types.js";
// NOTE: __setMermaidModuleProvider is now exported from src/tools/test-utils/mermaid.ts for test-only usage
// Export internals for testing
export { generateDiagram };
// Export helpers for unit testing (coverage targets)
export { normalizeLegacyTypes, formatResponse };
// Export handler registry for testing (internal)
export { HANDLER_REGISTRY };
//# sourceMappingURL=index.js.map