Skip to main content
Glama

Claude MCP Server Integration

by mokemoke0821
file-analyzer.ts10.7 kB
import fs from 'fs-extra'; import path from 'path'; import dayjs from 'dayjs'; import mime from 'mime-types'; import { FileSystemStats, FileInfo, DirectoryInfo } from '../types/index.js'; import { listDirectoryContents, formatFileSize } from '../utils/file-system.js'; /** * ディレクトリ内のファイル統計を収集 */ export async function analyzeDirectory( dirPath: string, options: { recursive?: boolean; includeHidden?: boolean; maxDepth?: number; } = {} ): Promise<FileSystemStats> { const { recursive = true, includeHidden = false, maxDepth = Infinity, } = options; try { // ディレクトリ内のファイルとフォルダを取得 const contents = await listDirectoryContents( dirPath, recursive, includeHidden, maxDepth ); // 統計情報を初期化 const stats: FileSystemStats = { total: { files: 0, directories: 0, size: 0, }, byType: {}, byDate: {}, bySize: { small: 0, // < 1MB medium: 0, // 1MB - 100MB large: 0, // 100MB - 1GB huge: 0, // > 1GB }, }; // ファイルとディレクトリを処理 for (const item of contents) { if (item.isDirectory) { // ディレクトリの場合 stats.total.directories++; } else { // ファイルの場合 stats.total.files++; stats.total.size += item.size; // タイプ別の統計 const fileInfo = item as FileInfo; const extension = fileInfo.extension.toLowerCase() || 'unknown'; if (!stats.byType[extension]) { stats.byType[extension] = { count: 0, size: 0, }; } stats.byType[extension].count++; stats.byType[extension].size += item.size; // 日付別の統計 const createdDate = dayjs(item.created).format('YYYY-MM-DD'); const modifiedDate = dayjs(item.modified).format('YYYY-MM-DD'); if (!stats.byDate[createdDate]) { stats.byDate[createdDate] = { created: 0, modified: 0, }; } if (!stats.byDate[modifiedDate]) { stats.byDate[modifiedDate] = { created: 0, modified: 0, }; } stats.byDate[createdDate].created++; stats.byDate[modifiedDate].modified++; // サイズ別の統計 const sizeInMB = item.size / (1024 * 1024); if (sizeInMB < 1) { stats.bySize.small++; } else if (sizeInMB < 100) { stats.bySize.medium++; } else if (sizeInMB < 1024) { stats.bySize.large++; } else { stats.bySize.huge++; } } } return stats; } catch (error) { throw new Error(`ディレクトリ分析に失敗しました: ${(error as Error).message}`); } } /** * ファイル統計情報をテキスト形式で整形 */ export function formatFileStats(stats: FileSystemStats): string { let output = '=== ファイルシステム統計 ===\n\n'; // 合計統計 output += '--- 合計 ---\n'; output += `ファイル数: ${stats.total.files}\n`; output += `ディレクトリ数: ${stats.total.directories}\n`; output += `合計サイズ: ${formatFileSize(stats.total.size)}\n\n`; // サイズ別統計 output += '--- サイズ別ファイル数 ---\n'; output += `小 (< 1MB): ${stats.bySize.small}\n`; output += `中 (1MB - 100MB): ${stats.bySize.medium}\n`; output += `大 (100MB - 1GB): ${stats.bySize.large}\n`; output += `超大 (> 1GB): ${stats.bySize.huge}\n\n`; // タイプ別統計 output += '--- ファイルタイプ別統計 ---\n'; // タイプを数で降順にソート const sortedTypes = Object.entries(stats.byType) .sort(([, a], [, b]) => b.count - a.count); for (const [extension, data] of sortedTypes) { const extName = extension === 'unknown' ? '拡張子なし' : extension; output += `${extName}: ${data.count} ファイル (${formatFileSize(data.size)})\n`; } output += '\n'; // 日付別統計 output += '--- 最近の活動 ---\n'; // 日付を降順にソート(最新順) const recentDates = Object.entries(stats.byDate) .sort(([dateA], [dateB]) => dateB.localeCompare(dateA)) .slice(0, 10); // 最新の10日分 for (const [date, data] of recentDates) { output += `${date}: 作成 ${data.created} ファイル, 更新 ${data.modified} ファイル\n`; } return output; } /** * ファイル統計情報をHTML形式で整形 */ export function formatFileStatsHtml(stats: FileSystemStats, dirPath: string): string { // タイプ別データを準備 const typeData = Object.entries(stats.byType) .sort(([, a], [, b]) => b.count - a.count) .map(([type, data]) => { return { type: type === 'unknown' ? '拡張子なし' : type, count: data.count, size: formatFileSize(data.size), percentage: ((data.count / stats.total.files) * 100).toFixed(1), }; }); // 日付別データを準備 const dateData = Object.entries(stats.byDate) .sort(([dateA], [dateB]) => dateB.localeCompare(dateA)) .slice(0, 30) // 最新の30日分 .map(([date, data]) => { return { date, created: data.created, modified: data.modified, }; }); const html = ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>ファイル統計: ${dirPath}</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; padding: 20px; background-color: #f8f9fa; color: #333; max-width: 1200px; margin: 0 auto; } h1, h2, h3 { color: #495057; } .stats-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 20px; margin-bottom: 30px; } .stats-card { background-color: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 20px; } .stats-summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px; margin-bottom: 30px; } .summary-item { background-color: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); padding: 15px; text-align: center; } .summary-item h3 { margin-top: 0; color: #6c757d; font-size: 0.9rem; text-transform: uppercase; } .summary-item p { margin-bottom: 0; font-size: 1.5rem; font-weight: bold; color: #495057; } .size-label { font-size: 0.8rem; color: #6c757d; } table { width: 100%; border-collapse: collapse; } th, td { padding: 10px; text-align: left; border-bottom: 1px solid #dee2e6; } th { background-color: #e9ecef; font-weight: bold; } .bar-chart { height: 15px; background-color: #4dabf7; border-radius: 2px; } .chart-container { margin-top: 40px; } </style> </head> <body> <h1>ファイル統計: ${dirPath}</h1> <div class="stats-summary"> <div class="summary-item"> <h3>ファイル</h3> <p>${stats.total.files}</p> </div> <div class="summary-item"> <h3>ディレクトリ</h3> <p>${stats.total.directories}</p> </div> <div class="summary-item"> <h3>合計サイズ</h3> <p>${formatFileSize(stats.total.size)}</p> </div> <div class="summary-item"> <h3>ファイルタイプ</h3> <p>${Object.keys(stats.byType).length}</p> </div> </div> <div class="stats-container"> <div class="stats-card"> <h2>サイズ別ファイル数</h2> <table> <tr> <th>サイズ範囲</th> <th>ファイル数</th> <th>割合</th> </tr> <tr> <td>小 (<1MB)</td> <td>${stats.bySize.small}</td> <td>${((stats.bySize.small / stats.total.files) * 100).toFixed(1)}%</td> </tr> <tr> <td>中 (1MB-100MB)</td> <td>${stats.bySize.medium}</td> <td>${((stats.bySize.medium / stats.total.files) * 100).toFixed(1)}%</td> </tr> <tr> <td>大 (100MB-1GB)</td> <td>${stats.bySize.large}</td> <td>${((stats.bySize.large / stats.total.files) * 100).toFixed(1)}%</td> </tr> <tr> <td>超大 (>1GB)</td> <td>${stats.bySize.huge}</td> <td>${((stats.bySize.huge / stats.total.files) * 100).toFixed(1)}%</td> </tr> </table> </div> <div class="stats-card"> <h2>最近の活動</h2> <table> <tr> <th>日付</th> <th>作成</th> <th>更新</th> </tr> ${dateData.map(data => ` <tr> <td>${data.date}</td> <td>${data.created}</td> <td>${data.modified}</td> </tr> `).join('')} </table> </div> </div> <div class="stats-card chart-container"> <h2>ファイルタイプ分布</h2> <table> <tr> <th>タイプ</th> <th>ファイル数</th> <th>サイズ</th> <th>分布</th> </tr> ${typeData.map(data => ` <tr> <td>${data.type}</td> <td>${data.count}</td> <td>${data.size}</td> <td> <div class="bar-chart" style="width: ${data.percentage}%"></div> <span class="size-label">${data.percentage}%</span> </td> </tr> `).join('')} </table> </div> </body> </html> `; return html; }

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/mokemoke0821/claude-mcp-integration'

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