Skip to main content
Glama

Memory Bank MCP Server

by t3ta
mermaid-generator.ts13.2 kB
import * as path from 'path'; import { ProjectStructure, DirectoryStructure, ClassInfo, InterfaceInfo, DiagramOptions } from './types'; /** * mermaidダイアグラムを生成するクラス */ export class MermaidGenerator { /** * ディレクトリ構造図を生成 */ public generateDirectoryDiagram(structure: ProjectStructure, options: DiagramOptions = { type: 'directory', detailLevel: 'standard' }): string { const direction = options.direction || 'TB'; const lines: string[] = []; // ダイアグラムヘッダー lines.push(`graph ${direction}`); if (options.title) { lines.push(` %% ${options.title}`); lines.push(''); } // スタイル定義 lines.push(' %% スタイル定義'); lines.push(' classDef package fill:#f9f,stroke:#333,stroke-width:1px;'); lines.push(' classDef directory fill:#bbf,stroke:#333,stroke-width:1px;'); lines.push(' classDef file fill:#f9f9f9,stroke:#333,stroke-width:1px;'); lines.push(''); // ルートディレクトリの処理 for (const rootDir of structure.rootDirectories) { this.processDirectoryForMermaid(rootDir, lines, '', options); } // クラス定義の適用 lines.push(''); lines.push(' %% クラス定義の適用'); lines.push(' class root directory;'); // パッケージに対してpackageクラスを適用 structure.packagePaths.forEach((packagePath) => { const safePackageName = this.sanitizeId(path.basename(packagePath)); lines.push(` class ${safePackageName} package;`); }); return lines.join('\n'); } /** * クラス図を生成 */ public generateClassDiagram(structure: ProjectStructure, options: DiagramOptions = { type: 'class', detailLevel: 'standard' }): string { const lines: string[] = []; // ダイアグラムヘッダー lines.push('classDiagram'); if (options.title) { lines.push(` %% ${options.title}`); lines.push(''); } // パッケージ毎のクラスとインターフェースを収集 const classesAndInterfaces = new Map<string, { classes: ClassInfo[], interfaces: InterfaceInfo[] }>(); for (const rootDir of structure.rootDirectories) { this.collectClassesAndInterfaces(rootDir, classesAndInterfaces); } // クラスとインターフェースの定義を生成 classesAndInterfaces.forEach((data, _packageName) => { if (data.classes.length > 0 || data.interfaces.length > 0) { lines.push(` %% パッケージ: ${_packageName}`); lines.push(''); // インターフェース定義 for (const interfaceInfo of data.interfaces) { this.generateInterfaceDefinition(interfaceInfo, lines, options); } // クラス定義 for (const classInfo of data.classes) { this.generateClassDefinition(classInfo, lines, options); } // 継承・実装関係の定義 for (const classInfo of data.classes) { if (classInfo.extends) { lines.push(` ${this.sanitizeId(classInfo.name)} --|> ${this.sanitizeId(classInfo.extends)}: extends`); } if (classInfo.implements) { for (const implementedInterface of classInfo.implements) { lines.push(` ${this.sanitizeId(classInfo.name)} ..|> ${this.sanitizeId(implementedInterface)}: implements`); } } } // インターフェースの継承関係 for (const interfaceInfo of data.interfaces) { if (interfaceInfo.extends) { for (const extendedInterface of interfaceInfo.extends) { lines.push(` ${this.sanitizeId(interfaceInfo.name)} --|> ${this.sanitizeId(extendedInterface)}: extends`); } } } lines.push(''); } }); return lines.join('\n'); } /** * 依存関係図を生成 */ public generateDependencyDiagram(structure: ProjectStructure, options: DiagramOptions = { type: 'dependency', detailLevel: 'standard' }): string { const direction = options.direction || 'TB'; const lines: string[] = []; // ダイアグラムヘッダー lines.push(`graph ${direction}`); if (options.title) { lines.push(` %% ${options.title}`); lines.push(''); } // パッケージの依存関係を収集 const packageDependencies = new Map<string, Set<string>>(); // パッケージ定義 structure.packagePaths.forEach((packagePath, _packageName) => { const safePackageName = this.sanitizeId(_packageName); lines.push(` ${safePackageName}["${_packageName}"]`); packageDependencies.set(_packageName, new Set<string>()); }); lines.push(''); // パッケージの依存関係を解析 structure.packagePaths.forEach((packagePath, _packageName) => { const packageJsonPath = path.join(packagePath, 'package.json'); try { const packageJson = require(packageJsonPath); const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies }; for (const dep in dependencies) { // 同じプロジェクト内のパッケージへの依存関係のみ表示 if (structure.packagePaths.has(dep)) { const deps = packageDependencies.get(_packageName); if (deps) { deps.add(dep); } } } } catch { // package.jsonが読めない場合は無視 } }); // 依存関係の矢印を追加 packageDependencies.forEach((deps, _packageName) => { const safePackageName = this.sanitizeId(_packageName); deps.forEach(dep => { const safeDepName = this.sanitizeId(dep); lines.push(` ${safePackageName} --> ${safeDepName}`); }); }); // スタイルを設定 lines.push(''); lines.push(' %% スタイル定義'); lines.push(' classDef package fill:#f9f,stroke:#333,stroke-width:1px;'); // クラス定義の適用 lines.push(''); lines.push(' %% クラス定義の適用'); structure.packagePaths.forEach((packagePath, _packageName) => { const safePackageName = this.sanitizeId(_packageName); lines.push(` class ${safePackageName} package;`); }); return lines.join('\n'); } /** * ディレクトリをmermaid用に処理 */ private processDirectoryForMermaid(dir: DirectoryStructure, lines: string[], parentId: string, options: DiagramOptions): string { const dirId = parentId ? `${parentId}_${this.sanitizeId(dir.name)}` : this.sanitizeId(dir.name); // ディレクトリノードの定義 if (dir.isPackage) { const packageName = dir.packageInfo?.name || dir.name; lines.push(` ${dirId}["📦 ${packageName}"]`); } else { lines.push(` ${dirId}["📁 ${dir.name}"]`); } // サブディレクトリの処理 if (dir.subdirectories && dir.subdirectories.length > 0) { for (const subdir of dir.subdirectories) { const subdirId = this.processDirectoryForMermaid(subdir, lines, dirId, options); lines.push(` ${dirId} --> ${subdirId}`); } } // 詳細レベルが 'standard' または 'full' の場合はファイルも表示 if (options.detailLevel === 'standard' || options.detailLevel === 'full') { for (const file of dir.files) { const fileId = `${dirId}_${this.sanitizeId(file.name)}`; lines.push(` ${fileId}["📄 ${file.name}"]`); lines.push(` ${dirId} --> ${fileId}`); // ファイルクラスを適用 lines.push(` class ${fileId} file;`); } } // ディレクトリクラスを適用 if (dir.isPackage) { lines.push(` class ${dirId} package;`); } else { lines.push(` class ${dirId} directory;`); } return dirId; } /** * クラスとインターフェースを収集 */ private collectClassesAndInterfaces( dir: DirectoryStructure, result: Map<string, { classes: ClassInfo[], interfaces: InterfaceInfo[] }> ): void { const packageName = dir.isPackage ? (dir.packageInfo?.name || dir.name) : ''; if (dir.isPackage) { // 新しいパッケージを追加 if (!result.has(packageName)) { result.set(packageName, { classes: [], interfaces: [] }); } } // ファイル内のクラスとインターフェースを収集 if (dir.files && dir.files.length > 0) { for (const file of dir.files) { if (file.classes && file.classes.length > 0) { for (const classInfo of file.classes) { if (classInfo.isExported && dir.isPackage) { const packageData = result.get(packageName); if (packageData) { packageData.classes.push(classInfo); } } } } if (file.interfaces && file.interfaces.length > 0) { for (const interfaceInfo of file.interfaces) { if (interfaceInfo.isExported && dir.isPackage) { const packageData = result.get(packageName); if (packageData) { packageData.interfaces.push(interfaceInfo); } } } } } } // サブディレクトリを再帰的に処理 if (dir.subdirectories && dir.subdirectories.length > 0) { for (const subdir of dir.subdirectories) { this.collectClassesAndInterfaces(subdir, result); } } } /** * クラス定義を生成 */ private generateClassDefinition(classInfo: ClassInfo, lines: string[], options: DiagramOptions): void { lines.push(` class ${this.sanitizeId(classInfo.name)} {`); // 詳細レベルに応じてメンバーを表示 if (options.detailLevel === 'standard' || options.detailLevel === 'full') { // プロパティを表示 for (const prop of classInfo.properties) { const visibilitySymbol = this.getVisibilitySymbol(prop.visibility); const staticPrefix = prop.isStatic ? '$' : ''; lines.push(` ${visibilitySymbol}${staticPrefix}${prop.name}: ${this.simplifyType(prop.type)}`); } // メソッドを表示 for (const method of classInfo.methods) { const visibilitySymbol = this.getVisibilitySymbol(method.visibility); const staticPrefix = method.isStatic ? '$' : ''; if (options.detailLevel === 'full') { // 詳細レベルが 'full' の場合はパラメータも表示 const params = method.parameters.map(param => `${param.name}: ${this.simplifyType(param.type)}`).join(', '); lines.push(` ${visibilitySymbol}${staticPrefix}${method.name}(${params}): ${this.simplifyType(method.returnType)}`); } else { // 詳細レベルが 'standard' の場合はメソッド名のみ表示 lines.push(` ${visibilitySymbol}${staticPrefix}${method.name}()`); } } } lines.push(' }'); } /** * インターフェース定義を生成 */ private generateInterfaceDefinition(interfaceInfo: InterfaceInfo, lines: string[], options: DiagramOptions): void { lines.push(` class ${this.sanitizeId(interfaceInfo.name)} {`); lines.push(' <<interface>>'); // 詳細レベルに応じてメンバーを表示 if (options.detailLevel === 'standard' || options.detailLevel === 'full') { // プロパティを表示 for (const prop of interfaceInfo.properties) { lines.push(` +${prop.name}: ${this.simplifyType(prop.type)}`); } // メソッドを表示 for (const method of interfaceInfo.methods) { if (options.detailLevel === 'full') { // 詳細レベルが 'full' の場合はパラメータも表示 const params = method.parameters.map(param => `${param.name}: ${this.simplifyType(param.type)}`).join(', '); lines.push(` +${method.name}(${params}): ${this.simplifyType(method.returnType)}`); } else { // 詳細レベルが 'standard' の場合はメソッド名のみ表示 lines.push(` +${method.name}()`); } } } lines.push(' }'); } /** * 可視性記号を取得 */ private getVisibilitySymbol(visibility: string): string { switch (visibility) { case 'public': return '+'; case 'protected': return '#'; case 'private': return '-'; default: return '+'; } } /** * 型名を簡略化 */ private simplifyType(type: string): string { // 長すぎる型は簡略化 if (type.length > 30) { return type.substring(0, 27) + '...'; } return type; } /** * IDを安全な形式に変換 */ private sanitizeId(id: string): string { // mermaidでIDとして使える形式に変換 return id.replace(/[^a-zA-Z0-9]/g, '_'); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/t3ta/memory-bank-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server