generate_system_flow
Auto-generate a Mermaid flowchart diagram of system module, class, and function connections. Understand project structure without reading every file by selecting scope: full, modules-only, or feature-focused.
Instructions
Auto-generate a Mermaid flowchart diagram showing how modules, classes, and functions connect in the system. Returns a Mermaid diagram string that AI can read to understand the full system flow without reading every file.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project | No | Project name or path | |
| scope | No | Scope of the diagram: 'full' shows all entities, 'modules-only' shows only module relationships (recommended for large projects), 'feature' requires the 'feature' param | |
| feature | No | Feature keyword to focus the diagram on (e.g. 'auth', 'crawl', 'payment'). Only used when scope='feature' | |
| maxNodes | No | Maximum nodes in diagram (default: 60). Reduce for large projects |
Implementation Reference
- index.ts:383-512 (handler)The main handler function for the 'generate_system_flow' tool. It loads analysis data, filters by scope (full/modules-only/feature), applies maxNodes limit, generates a Mermaid flowchart diagram string, and returns the result as JSON.
// Tool 6: Generate System Flow — Auto-generate Mermaid diagram from code analysis server.tool( "generate_system_flow", "Auto-generate a Mermaid flowchart diagram showing how modules, classes, and functions connect in the system. Returns a Mermaid diagram string that AI can read to understand the full system flow without reading every file.", { project: z.string().optional().describe("Project name or path"), scope: z.enum(["full", "modules-only", "feature"]).optional().describe("Scope of the diagram: 'full' shows all entities, 'modules-only' shows only module relationships (recommended for large projects), 'feature' requires the 'feature' param"), feature: z.string().optional().describe("Feature keyword to focus the diagram on (e.g. 'auth', 'crawl', 'payment'). Only used when scope='feature'"), maxNodes: z.number().optional().describe("Maximum nodes in diagram (default: 60). Reduce for large projects"), }, async ({ project, scope, feature, maxNodes }) => { const loaded = loadAnalysis(project); if (!loaded) { return { content: [{ type: "text" as const, text: "No analysis data found. Run 'CodeAtlas: Analyze Project' first." }] }; } const max = maxNodes || 60; const diagramScope = scope || "modules-only"; let nodes = loaded.analysis.graph.nodes; let links = loaded.analysis.graph.links; const nodeMap = new Map(nodes.map((n) => [n.id, n])); // Filter by scope if (diagramScope === "modules-only") { nodes = nodes.filter((n) => n.type === "module" && n.filePath); const nodeIds = new Set(nodes.map((n) => n.id)); links = links.filter((l) => nodeIds.has(l.source) && nodeIds.has(l.target) && l.type === "import"); } else if (diagramScope === "feature" && feature) { const q = feature.toLowerCase(); // Find nodes matching the feature keyword const matchingNodes = new Set<string>(); nodes.forEach((n) => { if (n.label.toLowerCase().includes(q) || (n.filePath && n.filePath.toLowerCase().includes(q))) { matchingNodes.add(n.id); } }); // Expand to include connected nodes (1 hop) links.forEach((l) => { if (matchingNodes.has(l.source)) matchingNodes.add(l.target); if (matchingNodes.has(l.target)) matchingNodes.add(l.source); }); nodes = nodes.filter((n) => matchingNodes.has(n.id)); const nodeIds = new Set(nodes.map((n) => n.id)); links = links.filter((l) => nodeIds.has(l.source) && nodeIds.has(l.target)); } // Truncate if too many nodes if (nodes.length > max) { // Prioritize: modules > classes > functions > variables const priorityOrder = ["module", "class", "function", "variable"]; nodes.sort((a, b) => { const ia = priorityOrder.indexOf(a.type); const ib = priorityOrder.indexOf(b.type); return (ia === -1 ? 99 : ia) - (ib === -1 ? 99 : ib); }); nodes = nodes.slice(0, max); } const truncatedNodeIds = new Set(nodes.map((n) => n.id)); links = links.filter((l) => truncatedNodeIds.has(l.source) && truncatedNodeIds.has(l.target)); // Remove duplicate links const linkSet = new Set<string>(); links = links.filter((l) => { const key = `${l.source}|${l.target}|${l.type}`; if (linkSet.has(key)) return false; linkSet.add(key); return true; }); // Build Mermaid diagram const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9_]/g, "_").substring(0, 40); const nodeIdMap = new Map<string, string>(); let counter = 0; const getMermaidId = (nodeId: string) => { if (!nodeIdMap.has(nodeId)) { nodeIdMap.set(nodeId, `n${counter++}`); } return nodeIdMap.get(nodeId)!; }; const lines: string[] = ["graph TD"]; // Add node declarations for (const node of nodes) { const mid = getMermaidId(node.id); const label = node.label.replace(/"/g, "'"); const typeIcon = node.type === "module" ? "📄" : node.type === "class" ? "🏗️" : node.type === "function" ? "⚡" : "📦"; if (node.type === "module") { lines.push(` ${mid}["${typeIcon} ${label}"]`); } else if (node.type === "class") { lines.push(` ${mid}[["${typeIcon} ${label}"]]`); } else { lines.push(` ${mid}("${typeIcon} ${label}")`); } } // Add link declarations const arrowMap: Record<string, string> = { import: "-->", call: "-.->", contains: "-->", implements: "-.->|implements|" }; const labelMap: Record<string, string> = { import: "imports", call: "calls", contains: "contains", implements: "implements" }; for (const link of links) { const src = getMermaidId(link.source); const tgt = getMermaidId(link.target); if (src && tgt) { const arrow = arrowMap[link.type] || "-->"; if (link.type === "contains") { lines.push(` ${src} ${arrow} ${tgt}`); } else { lines.push(` ${src} ${arrow}|${labelMap[link.type] || link.type}| ${tgt}`); } } } const mermaid = lines.join("\n"); const result = { project: loaded.projectName, scope: diagramScope, feature: feature || null, nodeCount: nodes.length, linkCount: links.length, truncated: loaded.analysis.graph.nodes.length > max, mermaidDiagram: mermaid, summary: `System flow for ${loaded.projectName}: ${nodes.filter((n) => n.type === "module").length} modules, ${nodes.filter((n) => n.type === "class").length} classes, ${nodes.filter((n) => n.type === "function").length} functions connected by ${links.length} relationships.`, }; return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] }; } ); - index.ts:387-392 (schema)Input schema for generate_system_flow: accepts optional 'project' (string), 'scope' (enum of full/modules-only/feature), 'feature' (string), and 'maxNodes' (number, default 60).
{ project: z.string().optional().describe("Project name or path"), scope: z.enum(["full", "modules-only", "feature"]).optional().describe("Scope of the diagram: 'full' shows all entities, 'modules-only' shows only module relationships (recommended for large projects), 'feature' requires the 'feature' param"), feature: z.string().optional().describe("Feature keyword to focus the diagram on (e.g. 'auth', 'crawl', 'payment'). Only used when scope='feature'"), maxNodes: z.number().optional().describe("Maximum nodes in diagram (default: 60). Reduce for large projects"), }, - index.ts:384-385 (registration)Registration of the tool with the MCP server via server.tool('generate_system_flow', ...).
server.tool( "generate_system_flow", - index.ts:104-124 (helper)The loadAnalysis helper function used by generate_system_flow to read analysis data from .codeatlas/analysis.json.
function loadAnalysis(projectDir?: string): { analysis: AnalysisResult; projectName: string; projectDir: string } | null { const projects = discoverProjects(); if (projects.length === 0) return null; let target = projects[0]; // default: most recently modified if (projectDir) { const match = projects.find( (p) => p.dir === projectDir || p.name.toLowerCase() === projectDir.toLowerCase() ); if (match) target = match; } try { const data = fs.readFileSync(target.analysisPath, "utf-8"); return { analysis: JSON.parse(data), projectName: target.name, projectDir: target.dir }; } catch (e) { console.error(`[CodeAtlas] ERROR: Failed to parse analysis file at ${target.analysisPath}: ${e}`); return null; } } - index.ts:26-33 (helper)The AnalysisResult interface defining the shape of graph data (nodes and links) consumed by generate_system_flow.
interface AnalysisResult { graph: { nodes: GraphNode[]; links: GraphLink[] }; insights: any[]; stats?: { files: number; functions: number; classes: number; dependencies: number; circularDeps: number }; entityCounts?: { modules: number; functions: number; classes: number; dependencies: number; circularDeps: number }; totalFilesAnalyzed?: number; totalFilesSkipped?: number; }