2026년 AI 개발에서 가장 주목받는 기술 중 하나는 MCP(Model Context Protocol)입니다. Anthropic이 공개한 이 프로토콜은 AI 모델이 외부 도구, 데이터베이스, API와 표준화된 방식으로 소통할 수 있게 해줍니다. 이 글에서는 MCP 서버를 직접 구축하고 Claude와 연동하는 전체 과정을 다룹니다.

MCP란 무엇인가

MCP는 AI 모델과 외부 시스템 사이의 통신을 표준화하는 오픈 프로토콜입니다. 기존에는 각 도구마다 별도의 통합 코드를 작성해야 했지만, MCP를 사용하면 하나의 표준 인터페이스로 다양한 도구를 연결할 수 있습니다. 핵심 구성 요소는 다음과 같습니다.

  • MCP Host: Claude Desktop, Claude Code 등 AI가 실행되는 환경
  • MCP Client: 호스트 내부에서 서버와 1:1 연결을 관리하는 컴포넌트
  • MCP Server: 외부 도구나 데이터를 노출하는 서버 프로세스

MCP 서버 프로젝트 초기화

Node.js 기반 MCP 서버를 만들어 보겠습니다. 먼저 프로젝트를 초기화합니다.

mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod

@modelcontextprotocol/sdk는 MCP 서버/클라이언트 구현을 위한 공식 SDK이고, zod는 입력 스키마 검증에 사용됩니다.

기본 MCP 서버 구현

파일 시스템을 읽는 간단한 MCP 서버를 구현합니다.

// server.mjs
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import fs from 'fs/promises';
import path from 'path';

const server = new McpServer({
  name: 'file-reader',
  version: '1.0.0',
  description: '파일 시스템 읽기 도구를 제공하는 MCP 서버'
});

// 도구 등록: 파일 읽기
server.tool(
  'read_file',
  '지정된 경로의 파일 내용을 읽어 반환합니다',
  {
    filePath: z.string().describe('읽을 파일의 절대 경로'),
    encoding: z.enum(['utf-8', 'ascii', 'base64']).default('utf-8')
      .describe('파일 인코딩')
  },
  async ({ filePath, encoding }) => {
    try {
      const content = await fs.readFile(filePath, encoding);
      return {
        content: [{ type: 'text', text: content }]
      };
    } catch (err) {
      return {
        content: [{ type: 'text', text: `오류: ${err.message}` }],
        isError: true
      };
    }
  }
);

// 도구 등록: 디렉토리 목록
server.tool(
  'list_directory',
  '디렉토리 내 파일 및 폴더 목록을 반환합니다',
  {
    dirPath: z.string().describe('탐색할 디렉토리 경로'),
    recursive: z.boolean().default(false).describe('하위 디렉토리 포함 여부')
  },
  async ({ dirPath, recursive }) => {
    const entries = await fs.readdir(dirPath, { withFileTypes: true });
    const result = [];

    for (const entry of entries) {
      const fullPath = path.join(dirPath, entry.name);
      const stat = await fs.stat(fullPath);
      result.push({
        name: entry.name,
        type: entry.isDirectory() ? 'directory' : 'file',
        size: stat.size,
        modified: stat.mtime.toISOString()
      });
    }

    return {
      content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
    };
  }
);

// 서버 시작
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('MCP 서버가 시작되었습니다.');

리소스(Resource) 노출하기

도구 외에 MCP는 리소스 개념도 지원합니다. 리소스는 AI가 컨텍스트로 참조할 수 있는 데이터입니다.

// 리소스 등록: 프로젝트 설정 파일
server.resource(
  'project-config',
  'config://project',
  async (uri) => {
    const config = await fs.readFile('./config.json', 'utf-8');
    return {
      contents: [{
        uri: uri.href,
        mimeType: 'application/json',
        text: config
      }]
    };
  }
);

// 동적 리소스 템플릿
server.resource(
  'log-file',
  new ResourceTemplate('logs://{date}', { list: undefined }),
  async (uri, { date }) => {
    const logContent = await fs.readFile(`./logs/${date}.log`, 'utf-8');
    return {
      contents: [{
        uri: uri.href,
        mimeType: 'text/plain',
        text: logContent
      }]
    };
  }
);

Claude Desktop에 연결하기

구축한 MCP 서버를 Claude Desktop에 등록합니다. 설정 파일 위치는 OS별로 다릅니다.

# macOS
~/Library/Application Support/Claude/claude_desktop_config.json

# Windows
%APPDATA%\Claude\claude_desktop_config.json

설정 파일에 서버를 추가합니다.

{
  "mcpServers": {
    "file-reader": {
      "command": "node",
      "args": ["/절대경로/my-mcp-server/server.mjs"],
      "env": {
        "NODE_ENV": "production"
      }
    }
  }
}

SSE 전송 방식으로 원격 서버 구축

stdio 대신 HTTP SSE(Server-Sent Events) 전송을 사용하면 원격에서도 MCP 서버에 접속할 수 있습니다.

import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';

const app = express();
const transports = new Map();

app.get('/sse', async (req, res) => {
  const transport = new SSEServerTransport('/messages', res);
  transports.set(transport.sessionId, transport);
  await server.connect(transport);
});

app.post('/messages', async (req, res) => {
  const sessionId = req.query.sessionId;
  const transport = transports.get(sessionId);
  await transport.handlePostMessage(req, res);
});

app.listen(3100, () => {
  console.log('MCP SSE 서버: http://localhost:3100');
});

프롬프트 템플릿 등록

MCP 서버는 자주 사용하는 프롬프트 템플릿도 제공할 수 있습니다.

server.prompt(
  'code-review',
  '코드 리뷰 요청 프롬프트',
  { language: z.string(), code: z.string() },
  ({ language, code }) => ({
    messages: [{
      role: 'user',
      content: {
        type: 'text',
        text: `다음 ${language} 코드를 리뷰해주세요. 버그, 성능, 보안 관점에서 분석하고 개선안을 제시하세요.\n\n${code}`
      }
    }]
  })
);

보안 고려사항

MCP 서버 구축 시 반드시 고려해야 할 보안 사항입니다.

  • 입력 검증: zod 스키마로 모든 입력을 엄격하게 검증
  • 경로 제한: 파일 접근 도구는 허용된 디렉토리만 접근 가능하도록 제한
  • 인증: SSE 서버 사용 시 Bearer 토큰이나 API 키 인증 적용
  • 로깅: 모든 도구 호출을 로깅하여 감사 추적 가능하도록 구성
  • Rate Limiting: 과도한 요청을 방지하는 속도 제한 적용

실전 활용 사례

MCP 서버의 실전 활용 사례는 매우 다양합니다. 데이터베이스 쿼리 도구를 등록하면 AI가 직접 DB를 조회하여 답변할 수 있고, Slack이나 이메일 API를 연결하면 자동 알림 시스템을 구축할 수 있습니다. GitHub API와 연동하면 PR 리뷰, 이슈 관리를 AI가 수행합니다. 핵심은 각 도구의 설명(description)을 명확하게 작성하여 AI가 적절한 시점에 올바른 도구를 선택하도록 하는 것입니다.