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 };
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions analyzing and classifying lines (code/comments/blank), which implies a read-only operation, but doesn't specify output format, performance considerations, or any side effects. For a tool with no annotation coverage, this leaves key behavioral traits undocumented, such as whether it's resource-intensive or how results are returned.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose without any fluff. It's front-loaded with the core functionality and includes helpful parenthetical detail. Every word earns its place, making it highly concise and well-structured for quick understanding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of analyzing code lines with classification, the lack of annotations and output schema means the description is incomplete. It doesn't explain what the return values look like (e.g., a breakdown of counts), potential errors, or usage constraints. For a tool with no structured output information, the description should provide more context to be fully helpful.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, with clear descriptions for both parameters ('path' and 'extensions'). The description adds no additional parameter semantics beyond what the schema provides, such as examples of supported extensions or path format details. Since the schema does the heavy lifting, the baseline score of 3 is appropriate, as the description doesn't compensate but also doesn't detract.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: '프로젝트의 코드 라인 수를 분석합니다 (코드/주석/빈줄 분류)' translates to 'Analyzes the number of lines of code in a project (classifying code/comments/blank lines).' This specifies the verb (analyzes), resource (project code lines), and scope (with classification). However, it doesn't explicitly distinguish this tool from potential siblings like 'analyze_structure' or 'calculate,' which might have overlapping functionality, preventing a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention any prerequisites, exclusions, or specific contexts for usage. Given the sibling tools include 'analyze_structure' and 'calculate,' which could potentially handle similar analyses, the lack of differentiation is a significant gap in usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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