TL;DR
MCP (Model Context Protocol) 서버로 실제 개발에 유용한 프로젝트분석 도구를 만듭니다. Day 1에서 MCP서버 개념을, Day 2에서 Resource와 Prompt를 배웠다면, 이제 AI개발 현장에서 바로 사용할 수 있는 실전 Tool을 구현합니다.
이 글에서 배우는 것:
- AI개발 실전 – MCP개발로 파일시스템 접근 Tool 구현
- MCP서버로 프로젝트분석 자동화하는 AI개발 워크플로우
- 디렉토리 구조 분석, 의존성 분석, 코드 통계 Tool을 TypeScript로 개발
- Claude Code AI도구에서 Model Context Protocol 기반 Tool 활용법
- AI개발 현장에서 필수인 보안 고려사항
전체 코드: my-first-mcp – MCP개발 튜토리얼 프로젝트분석 확장
시리즈: Day 1: Model Context Protocol 개념 | Day 2: Resource와 Prompt | Day 3: 실전 프로젝트분석 | Day 4: npm 배포 (예정)
왜 프로젝트분석 MCP서버인가?
AI개발에서 컨텍스트의 중요성
Claude Code 같은 AI도구로 개발할 때 가장 중요한 것은 컨텍스트입니다. AI가 프로젝트 구조를 이해하고, 어떤 의존성을 사용하는지, 코드베이스가 얼마나 큰지 파악해야 더 정확한 도움을 줄 수 있습니다.
MCP서버로 프로젝트분석 도구를 만들면:
┌─────────────────────────────────────────────────────────────┐ │ 프로젝트분석 MCP서버 │ ├─────────────────────────────────────────────────────────────┤ │ Tool 1: analyze_structure │ │ → 디렉토리 구조를 트리 형태로 분석 │ │ │ │ Tool 2: analyze_dependencies │ │ → package.json 의존성 분석 │ │ │ │ Tool 3: count_lines │ │ → 코드 라인 수 통계 │ ├─────────────────────────────────────────────────────────────┤ │ Claude Code가 이 정보로 더 정확한 분석과 제안 제공 │ └─────────────────────────────────────────────────────────────┘
Model Context Protocol을 사용하면 AI도구가 파일시스템에 안전하게 접근할 수 있습니다. MCP개발을 통해 AI개발 워크플로우를 자동화할 수 있습니다.
프로젝트 확장 준비
기존 my-first-mcp 프로젝트 확장
Day 1에서 만든 MCP서버에 프로젝트분석 기능을 추가합니다. Claude Code AI도구와 연동할 수 있도록 기존 TypeScript 프로젝트 구조를 유지하면서 새로운 Tool들을 추가합니다. Model Context Protocol 기반 AI개발의 핵심은 확장 가능한 설계입니다.
# 기존 프로젝트로 이동 cd my-first-mcp # 파일시스템 관련 의존성 추가 npm install glob # 타입 정의 추가 npm install -D @types/node
파일 구조 확장
MCP개발에서는 코드를 모듈화하는 것이 좋습니다:
my-first-mcp/ ├── src/ │ ├── index.ts # 메인 서버 │ ├── tools/ │ │ ├── time.ts # 시간 관련 Tool │ │ ├── calculator.ts # 계산 Tool │ │ └── project-analyzer.ts # 프로젝트분석 Tool (새로 추가) │ └── utils/ │ └── file-system.ts # 파일시스템 유틸리티 ├── package.json └── tsconfig.json
Tool 1: 프로젝트 구조 분석
analyze_structure Tool 구현
MCP서버의 첫 번째 프로젝트분석 Tool입니다. 디렉토리 구조를 트리 형태로 분석하여 AI도구가 프로젝트 전체 구조를 파악할 수 있게 합니다.
import { z } from "zod";
import * as fs from "fs";
import * as path from "path";
// 프로젝트 구조 분석 Tool - MCP개발 핵심 기능
server.tool(
"analyze_structure",
"프로젝트 디렉토리 구조를 분석합니다",
{
targetPath: z.string().describe("분석할 디렉토리 경로"),
maxDepth: z.number().optional().default(3).describe("최대 탐색 깊이"),
includeHidden: z.boolean().optional().default(false).describe("숨김 파일 포함 여부"),
},
async ({ targetPath, maxDepth, includeHidden }) => {
// 경로 보안 검증 - 파일시스템 접근 시 필수
const resolvedPath = path.resolve(targetPath);
if (!fs.existsSync(resolvedPath)) {
return {
content: [{ type: "text", text: `오류: 경로를 찾을 수 없습니다 - ${resolvedPath}` }],
isError: true,
};
}
const structure = buildDirectoryTree(resolvedPath, maxDepth, includeHidden);
return {
content: [{
type: "text",
text: `## 프로젝트 구조 분석 결과\n\n경로: ${resolvedPath}\n\n\`\`\`\n${structure}\n\`\`\``,
}],
};
}
);
디렉토리 트리 생성 함수
파일시스템을 순회하며 트리 구조를 생성하는 유틸리티 함수입니다. MCP개발에서 자주 사용되는 패턴입니다.
// 파일시스템 트리 생성 - AI개발에서 프로젝트분석에 필수
function buildDirectoryTree(
dirPath: string,
maxDepth: number,
includeHidden: boolean,
currentDepth: number = 0,
prefix: string = ""
): string {
if (currentDepth >= maxDepth) {
return prefix + "...\n";
}
let result = "";
const items = fs.readdirSync(dirPath);
// 숨김 파일 필터링
const filteredItems = includeHidden
? items
: items.filter(item => !item.startsWith("."));
// node_modules, dist 등 제외 (MCP서버 성능 최적화)
const excludeDirs = ["node_modules", "dist", ".git", "coverage"];
const finalItems = filteredItems.filter(item => !excludeDirs.includes(item));
finalItems.forEach((item, index) => {
const itemPath = path.join(dirPath, item);
const isLast = index === finalItems.length - 1;
const connector = isLast ? "└── " : "├── ";
const newPrefix = prefix + (isLast ? " " : "│ ");
const stats = fs.statSync(itemPath);
if (stats.isDirectory()) {
result += prefix + connector + item + "/\n";
result += buildDirectoryTree(itemPath, maxDepth, includeHidden, currentDepth + 1, newPrefix);
} else {
result += prefix + connector + item + "\n";
}
});
return result;
}
실행 예시
Claude Code AI도구에서 MCP서버의 프로젝트분석 Tool을 호출한 결과:
User: "이 프로젝트 구조를 분석해줘" Claude Code: analyze_structure Tool 호출 → 경로: /Users/dev/my-project ## 프로젝트 구조 분석 결과 경로: /Users/dev/my-project ├── src/ │ ├── index.ts │ ├── components/ │ │ ├── Button.tsx │ │ └── Card.tsx │ └── utils/ │ └── helpers.ts ├── tests/ │ └── index.test.ts ├── package.json ├── tsconfig.json └── README.md
Tool 2: 의존성 분석
analyze_dependencies Tool 구현
package.json을 분석하여 프로젝트 의존성 정보를 제공합니다. MCP서버가 TypeScript 프로젝트의 기술 스택을 파악하는 데 유용합니다.
// package.json 의존성 분석 - Model Context Protocol 활용
server.tool(
"analyze_dependencies",
"package.json의 의존성을 분석합니다",
{
targetPath: z.string().describe("package.json이 있는 디렉토리 경로"),
includeDevDeps: z.boolean().optional().default(true).describe("devDependencies 포함 여부"),
},
async ({ targetPath, includeDevDeps }) => {
const packageJsonPath = path.join(path.resolve(targetPath), "package.json");
if (!fs.existsSync(packageJsonPath)) {
return {
content: [{ type: "text", text: `오류: package.json을 찾을 수 없습니다 - ${packageJsonPath}` }],
isError: true,
};
}
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
const result = formatDependencyAnalysis(packageJson, includeDevDeps);
return {
content: [{ type: "text", text: result }],
};
}
);
의존성 포맷팅 함수
MCP개발에서 AI도구가 쉽게 파싱할 수 있는 형태로 데이터를 포맷팅합니다:
// 의존성 분석 결과 포맷팅 - AI개발 친화적 출력
function formatDependencyAnalysis(packageJson: any, includeDevDeps: boolean): string {
let result = `## 의존성 분석 결과\n\n`;
// 프로젝트 기본 정보
result += `### 프로젝트 정보\n`;
result += `- **이름**: ${packageJson.name || "N/A"}\n`;
result += `- **버전**: ${packageJson.version || "N/A"}\n`;
result += `- **설명**: ${packageJson.description || "N/A"}\n\n`;
// 프로덕션 의존성
const deps = packageJson.dependencies || {};
const depCount = Object.keys(deps).length;
result += `### 프로덕션 의존성 (${depCount}개)\n`;
if (depCount > 0) {
result += "| 패키지 | 버전 |\n|--------|------|\n";
Object.entries(deps).forEach(([name, version]) => {
result += `| ${name} | ${version} |\n`;
});
} else {
result += "프로덕션 의존성 없음\n";
}
result += "\n";
// 개발 의존성
if (includeDevDeps) {
const devDeps = packageJson.devDependencies || {};
const devDepCount = Object.keys(devDeps).length;
result += `### 개발 의존성 (${devDepCount}개)\n`;
if (devDepCount > 0) {
result += "| 패키지 | 버전 |\n|--------|------|\n";
Object.entries(devDeps).forEach(([name, version]) => {
result += `| ${name} | ${version} |\n`;
});
} else {
result += "개발 의존성 없음\n";
}
result += "\n";
}
// 스크립트
const scripts = packageJson.scripts || {};
const scriptCount = Object.keys(scripts).length;
result += `### NPM 스크립트 (${scriptCount}개)\n`;
if (scriptCount > 0) {
Object.entries(scripts).forEach(([name, command]) => {
result += `- \`npm run ${name}\`: ${command}\n`;
});
}
return result;
}
Tool 3: 코드 통계
count_lines Tool 구현
프로젝트의 코드 라인 수를 분석합니다. MCP서버로 코드베이스 규모를 파악하면 AI도구가 더 적절한 분석을 제공할 수 있습니다.
import { glob } from "glob";
// 코드 라인 수 통계 - MCP개발 프로젝트분석 Tool
server.tool(
"count_lines",
"프로젝트의 코드 라인 수를 분석합니다",
{
targetPath: z.string().describe("분석할 디렉토리 경로"),
extensions: z.array(z.string()).optional()
.default(["ts", "tsx", "js", "jsx"])
.describe("분석할 파일 확장자"),
},
async ({ targetPath, extensions }) => {
const resolvedPath = path.resolve(targetPath);
if (!fs.existsSync(resolvedPath)) {
return {
content: [{ type: "text", text: `오류: 경로를 찾을 수 없습니다` }],
isError: true,
};
}
const stats = await analyzeCodeLines(resolvedPath, extensions);
return {
content: [{ type: "text", text: formatCodeStats(stats) }],
};
}
);
코드 라인 분석 함수
파일시스템을 순회하며 코드, 주석, 빈 줄을 분류합니다:
interface CodeStats {
totalFiles: number;
totalLines: number;
codeLines: number;
commentLines: number;
blankLines: number;
byExtension: Record<string, { files: number; lines: number }>;
}
// 파일시스템 순회하며 코드 통계 수집 - TypeScript로 구현
async function analyzeCodeLines(
dirPath: string,
extensions: string[]
): Promise<CodeStats> {
const pattern = `**/*.{${extensions.join(",")}}`;
const files = await glob(pattern, {
cwd: dirPath,
ignore: ["**/node_modules/**", "**/dist/**", "**/coverage/**"],
});
const stats: CodeStats = {
totalFiles: 0,
totalLines: 0,
codeLines: 0,
commentLines: 0,
blankLines: 0,
byExtension: {},
};
for (const file of files) {
const filePath = path.join(dirPath, file);
const content = fs.readFileSync(filePath, "utf-8");
const lines = content.split("\n");
const ext = path.extname(file).slice(1);
if (!stats.byExtension[ext]) {
stats.byExtension[ext] = { files: 0, lines: 0 };
}
stats.totalFiles++;
stats.byExtension[ext].files++;
let inBlockComment = false;
for (const line of lines) {
const trimmed = line.trim();
stats.totalLines++;
stats.byExtension[ext].lines++;
if (trimmed === "") {
stats.blankLines++;
} else if (inBlockComment) {
stats.commentLines++;
if (trimmed.includes("*/")) {
inBlockComment = false;
}
} else if (trimmed.startsWith("/*")) {
stats.commentLines++;
if (!trimmed.includes("*/")) {
inBlockComment = true;
}
} else if (trimmed.startsWith("//")) {
stats.commentLines++;
} else {
stats.codeLines++;
}
}
}
return stats;
}
// 코드 통계 포맷팅 - AI개발 친화적 출력
function formatCodeStats(stats: CodeStats): string {
let result = `## 코드 통계 분석 결과\n\n`;
result += `### 전체 요약\n`;
result += `- **총 파일 수**: ${stats.totalFiles}개\n`;
result += `- **총 라인 수**: ${stats.totalLines.toLocaleString()}줄\n`;
result += `- **코드 라인**: ${stats.codeLines.toLocaleString()}줄 (${((stats.codeLines / stats.totalLines) * 100).toFixed(1)}%)\n`;
result += `- **주석 라인**: ${stats.commentLines.toLocaleString()}줄 (${((stats.commentLines / stats.totalLines) * 100).toFixed(1)}%)\n`;
result += `- **빈 줄**: ${stats.blankLines.toLocaleString()}줄 (${((stats.blankLines / stats.totalLines) * 100).toFixed(1)}%)\n\n`;
result += `### 확장자별 통계\n`;
result += "| 확장자 | 파일 수 | 라인 수 |\n|--------|---------|----------|\n";
Object.entries(stats.byExtension)
.sort((a, b) => b[1].lines - a[1].lines)
.forEach(([ext, data]) => {
result += `| .${ext} | ${data.files}개 | ${data.lines.toLocaleString()}줄 |\n`;
});
return result;
}
파일시스템 보안 고려사항
MCP개발에서 보안의 중요성
MCP서버가 파일시스템에 접근할 때는 보안이 매우 중요합니다. Model Context Protocol 기반 서버를 개발할 때 반드시 고려해야 할 사항들입니다.
// 경로 보안 검증 유틸리티 - MCP서버 필수 보안
function validatePath(inputPath: string, allowedBase?: string): string {
// 경로 정규화
const resolved = path.resolve(inputPath);
// 경로 이탈 방지 (Path Traversal 공격 차단)
if (allowedBase) {
const normalizedBase = path.resolve(allowedBase);
if (!resolved.startsWith(normalizedBase)) {
throw new Error(`보안 오류: 허용된 경로 범위를 벗어났습니다`);
}
}
// 심볼릭 링크 확인
if (fs.existsSync(resolved) && fs.lstatSync(resolved).isSymbolicLink()) {
const realPath = fs.realpathSync(resolved);
if (allowedBase && !realPath.startsWith(path.resolve(allowedBase))) {
throw new Error(`보안 오류: 심볼릭 링크가 허용된 범위를 벗어납니다`);
}
}
return resolved;
}
민감 파일 필터링
AI도구에 노출되면 안 되는 파일들을 필터링합니다:
// 민감 파일 패턴 - MCP개발 보안 설정
const SENSITIVE_PATTERNS = [
/\.env$/, // 환경 변수
/\.env\..+$/, // .env.local, .env.production 등
/credentials/i, // 인증 정보
/secrets?/i, // 시크릿 파일
/\.pem$/, // 인증서
/\.key$/, // 키 파일
/password/i, // 패스워드 관련
];
function isSensitiveFile(filename: string): boolean {
return SENSITIVE_PATTERNS.some(pattern => pattern.test(filename));
}
// 파일시스템 접근 시 민감 파일 제외
function filterSensitiveFiles(files: string[]): string[] {
return files.filter(file => !isSensitiveFile(file));
}
통합 테스트
MCP Inspector로 테스트
프로젝트분석 Tool들을 MCP Inspector로 테스트합니다:
# 빌드 npm run build # Inspector 실행 npm run inspect # 브라우저에서 http://localhost:6274 열기
테스트 시나리오
1. Tools 탭 클릭 2. analyze_structure 선택 - targetPath: "." (현재 디렉토리) - maxDepth: 3 - includeHidden: false 3. Run Tool 클릭 4. 디렉토리 트리 결과 확인 5. analyze_dependencies 선택 - targetPath: "." - includeDevDeps: true 6. Run Tool 클릭 7. 의존성 목록 확인 8. count_lines 선택 - targetPath: "." - extensions: ["ts", "js"] 9. Run Tool 클릭 10. 코드 통계 확인
Claude Code에서 활용
MCP서버를 Claude Code AI도구에 연결한 후:
User: "이 프로젝트를 분석해줘" Claude Code: 1. analyze_structure로 전체 구조 파악 2. analyze_dependencies로 기술 스택 확인 3. count_lines로 코드베이스 규모 분석 → 종합적인 프로젝트 분석 보고서 제공
전체 코드 통합
src/tools/project-analyzer.ts
Model Context Protocol 기반 프로젝트분석 Tool 전체 코드:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import * as fs from "fs";
import * as path from "path";
import { glob } from "glob";
// 프로젝트분석 Tool 등록 함수 - MCP개발 모듈화
export function registerProjectAnalyzerTools(server: McpServer) {
// Tool 1: 구조 분석
server.tool(
"analyze_structure",
"프로젝트 디렉토리 구조를 분석합니다",
{
targetPath: z.string().describe("분석할 디렉토리 경로"),
maxDepth: z.number().optional().default(3),
includeHidden: z.boolean().optional().default(false),
},
async ({ targetPath, maxDepth, includeHidden }) => {
const resolved = path.resolve(targetPath);
if (!fs.existsSync(resolved)) {
return { content: [{ type: "text", text: "경로를 찾을 수 없습니다" }], isError: true };
}
const tree = buildDirectoryTree(resolved, maxDepth!, includeHidden!);
return { content: [{ type: "text", text: `## 프로젝트 구조\n\n\`\`\`\n${tree}\`\`\`` }] };
}
);
// Tool 2: 의존성 분석
server.tool(
"analyze_dependencies",
"package.json 의존성을 분석합니다",
{
targetPath: z.string().describe("package.json이 있는 경로"),
includeDevDeps: z.boolean().optional().default(true),
},
async ({ targetPath, includeDevDeps }) => {
const pkgPath = path.join(path.resolve(targetPath), "package.json");
if (!fs.existsSync(pkgPath)) {
return { content: [{ type: "text", text: "package.json을 찾을 수 없습니다" }], isError: true };
}
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
return { content: [{ type: "text", text: formatDependencyAnalysis(pkg, includeDevDeps!) }] };
}
);
// Tool 3: 코드 통계
server.tool(
"count_lines",
"코드 라인 수를 분석합니다",
{
targetPath: z.string().describe("분석할 디렉토리"),
extensions: z.array(z.string()).optional().default(["ts", "tsx", "js", "jsx"]),
},
async ({ targetPath, extensions }) => {
const resolved = path.resolve(targetPath);
if (!fs.existsSync(resolved)) {
return { content: [{ type: "text", text: "경로를 찾을 수 없습니다" }], isError: true };
}
const stats = await analyzeCodeLines(resolved, extensions!);
return { content: [{ type: "text", text: formatCodeStats(stats) }] };
}
);
}
// ... 헬퍼 함수들 (위에서 정의한 것들)
src/index.ts 업데이트
MCP서버 메인 파일에 프로젝트분석 Tool을 등록합니다:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { registerProjectAnalyzerTools } from "./tools/project-analyzer.js";
const server = new McpServer({
name: "my-first-mcp",
version: "1.0.0",
});
// 기존 Tool들 (Day 1에서 구현)
// ...
// 프로젝트분석 Tool 등록 (Day 3)
registerProjectAnalyzerTools(server);
// Resource와 Prompt (Day 2에서 구현)
// ...
// 서버 시작
const transport = new StdioServerTransport();
server.connect(transport);
console.error("MCP 서버 시작됨!");
요약
Day 3에서 배운 MCP개발 핵심 내용:
-
실전 프로젝트분석 Tool 구현
analyze_structure: 디렉토리 구조 분석으로 AI도구에 컨텍스트 제공analyze_dependencies: package.json 분석으로 기술 스택 파악count_lines: 코드 통계로 프로젝트 규모 분석
-
파일시스템 접근 패턴
- Node.js fs 모듈로 TypeScript 기반 파일 처리
- glob 패턴 매칭으로 효율적인 파일 탐색
- MCP서버 성능 최적화 (불필요한 디렉토리 제외)
-
보안 고려사항
- 경로 이탈 방지 (Path Traversal 공격 차단)
- 민감 파일 필터링
- Model Context Protocol 기반 안전한 파일시스템 접근
MCP개발 핵심 패턴
// 파일시스템 Tool 패턴 - Model Context Protocol
server.tool(
"tool_name",
"설명",
{ targetPath: z.string(), ...options },
async ({ targetPath, ...options }) => {
// 1. 경로 검증
const resolved = path.resolve(targetPath);
// 2. 파일시스템 접근
// 3. 결과 포맷팅
return { content: [{ type: "text", text: result }] };
}
);
다음 편 예고
Day 4: MCP 서버 npm 배포하기
Day 4에서는 MCP개발 결과물을 세상에 공유합니다:
- npm 패키지 구조화 및 배포 설정
- TypeScript 빌드 최적화
- README.md 작성 가이드
- npx로 바로 실행 가능한 MCP서버 만들기
- 사용자 설치 및 설정 가이드
참고 자료
공식 문서
시리즈 링크
- Day 1: Model Context Protocol 개념과 첫 서버
- Day 2: Resource와 Prompt 구현
- Day 4: npm 배포 (예정)
- Day 5: 고급 패턴 (예정)
Leave A Comment