Skip to main content
Glama
dh1789

My First MCP

by dh1789

count_lines

Analyze code line counts in projects by classifying lines as code, comments, or blanks. Specify directory path and file extensions to measure project size and structure.

Instructions

프로젝트의 코드 라인 수를 분석합니다 (코드/주석/빈줄 분류).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYes분석할 디렉토리 경로
extensionsNo분석할 확장자 목록 (예: ["ts", "js"]). 기본값: 모든 지원 확장자

Implementation Reference

  • src/index.ts:525-564 (registration)
    Registers the 'count_lines' tool with the MCP server, defines input schema using Zod, and provides a thin handler that formats the response from the core countLines function.
    server.tool( "count_lines", "프로젝트의 코드 라인 수를 분석합니다 (코드/주석/빈줄 분류).", { path: z.string().describe("분석할 디렉토리 경로"), extensions: z .array(z.string()) .optional() .describe("분석할 확장자 목록 (예: [\"ts\", \"js\"]). 기본값: 모든 지원 확장자"), }, async ({ path: targetPath, extensions }) => { const result = countLines(targetPath, { extensions }); if (!result.success) { return { content: [{ type: "text", text: `오류: ${result.error}` }], isError: true, }; } let text = `📊 코드 라인 통계\n\n`; text += `📁 총 파일: ${result.totalFiles}개\n`; text += `📝 총 라인: ${result.totalLines}줄\n`; text += ` - 코드: ${result.codeLines}줄\n`; text += ` - 주석: ${result.commentLines}줄\n`; text += ` - 빈줄: ${result.blankLines}줄\n`; if (result.byExtension && Object.keys(result.byExtension).length > 0) { text += `\n📈 확장자별 통계:\n`; Object.entries(result.byExtension) .sort((a, b) => b[1].lines - a[1].lines) .forEach(([ext, stats]) => { text += ` .${ext}: ${stats.files}개 파일, ${stats.lines}줄 (코드: ${stats.codeLines}, 주석: ${stats.commentLines})\n`; }); } return { content: [{ type: "text", text }], }; }
  • Zod input schema for the count_lines tool: path (directory) and optional extensions array.
    { path: z.string().describe("분석할 디렉토리 경로"), extensions: z .array(z.string()) .optional() .describe("분석할 확장자 목록 (예: [\"ts\", \"js\"]). 기본값: 모든 지원 확장자"), },
  • Main handler function implementing the core logic of counting lines in project files, initializing stats and invoking recursive traversal.
    export function countLines( targetPath: string, options: LineCountOptions = {} ): LineCountResult { const { extensions } = options; try { if (!fs.existsSync(targetPath)) { return { success: false, error: `경로를 찾을 수 없습니다: ${targetPath}`, }; } const stats: LineCountResult = { success: true, totalLines: 0, totalFiles: 0, codeLines: 0, commentLines: 0, blankLines: 0, byExtension: {}, }; countLinesRecursive(targetPath, extensions, stats); return stats; } catch (err) { return { success: false, error: err instanceof Error ? err.message : String(err), }; } }
  • Recursive directory traversal helper that processes files, applies filters, reads content, analyzes lines, and updates statistics.
    function countLinesRecursive( dirPath: string, extensions: string[] | undefined, stats: LineCountResult ): void { const entries = fs.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory()) { // 숨김 디렉토리와 node_modules 제외 if (!entry.name.startsWith(".") && entry.name !== "node_modules") { countLinesRecursive(fullPath, extensions, stats); } } else if (entry.isFile()) { const ext = path.extname(entry.name).slice(1); // .ts -> ts // 확장자 필터 if (extensions && extensions.length > 0 && !extensions.includes(ext)) { continue; } // 숨김 파일 제외 if (entry.name.startsWith(".")) { continue; } // 지원하는 텍스트 파일만 분석 const textExtensions = ["ts", "js", "tsx", "jsx", "json", "md", "txt", "css", "scss", "html", "py", "go", "rs", "java", "c", "cpp", "h"]; if (!textExtensions.includes(ext)) { continue; } try { const content = fs.readFileSync(fullPath, "utf-8"); const fileStats = analyzeFileLines(content, ext); stats.totalFiles! += 1; stats.totalLines! += fileStats.total; stats.codeLines! += fileStats.code; stats.commentLines! += fileStats.comment; stats.blankLines! += fileStats.blank; // 확장자별 통계 if (!stats.byExtension![ext]) { stats.byExtension![ext] = { files: 0, lines: 0, codeLines: 0, commentLines: 0, blankLines: 0, }; } stats.byExtension![ext].files += 1; stats.byExtension![ext].lines += fileStats.total; stats.byExtension![ext].codeLines += fileStats.code; stats.byExtension![ext].commentLines += fileStats.comment; stats.byExtension![ext].blankLines += fileStats.blank; } catch { // 바이너리 파일 등 읽기 실패 시 무시 } } } }
  • Per-file line analyzer that classifies lines into code, comments (single/multi-line), and blanks using language-specific patterns.
    function analyzeFileLines( content: string, ext: string ): { total: number; code: number; comment: number; blank: number } { const lines = content.split("\n"); let code = 0; let comment = 0; let blank = 0; let inMultiLineComment = false; // 주석 패턴 (언어별) const singleLineComment = getSingleLineCommentPattern(ext); const multiLineStart = getMultiLineCommentStart(ext); const multiLineEnd = getMultiLineCommentEnd(ext); for (const line of lines) { const trimmed = line.trim(); if (trimmed === "") { blank++; continue; } // 멀티라인 주석 처리 if (inMultiLineComment) { comment++; if (multiLineEnd && trimmed.includes(multiLineEnd)) { inMultiLineComment = false; } continue; } if (multiLineStart && trimmed.startsWith(multiLineStart)) { comment++; if (!multiLineEnd || !trimmed.includes(multiLineEnd)) { inMultiLineComment = true; } continue; } // 단일 라인 주석 if (singleLineComment && trimmed.startsWith(singleLineComment)) { comment++; continue; } code++; } return { total: lines.length, code, comment, blank }; }

Latest Blog Posts

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/dh1789/my-first-mcp'

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