안녕하세요, AI API 통합을 6년째 담당하고 있는 시니어 엔지니어입니다. 오늘은 제가 직접 실무에서 사용하면서 가장 큰 생산성 향상을 가져다준 기술을 하나 소개하려고 합니다. 바로 MCP(Model Context Protocol) 서버를 직접 개발해서 사내 API를 Claude Code가 호출할 수 있게 만드는 방법입니다. 이 글 끝까지 따라오시면 외부에 노출되면 안 되는 사내 시스템도 Claude Code가 안전하게 다룰 수 있게 됩니다.

저는 최근 한 금융사 프로젝트에서 이 방식으로 사내 거래 조회 API를 Claude Code에 연결했습니다. 개발자가 SQL을 직접 짜지 않고도 "어제 서울 지역 VIP 고객 거래 내역 보여줘"라고 채팅창에 입력하기만 하면 결과가 나오는 시스템을 만들었죠. 처음에는 정말 신기했지만, 막상 코드를 보니 30줄짜리 파이썬 파일 두 개로 충분했습니다.

본 튜토리얼에서는 모델 호출 게이트웨이로 HolySheep AI를 사용합니다. HolySheep AI는 단일 API 키로 GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash, DeepSeek V3.2까지 모든 주요 모델을 호출할 수 있고, 해외 신용카드 없이도 로컬 결제 방식으로 가입 즉시 무료 크레딧을 받아 실습할 수 있습니다.

MCP가 무엇인가요?

MCP는 Model Context Protocol의 약자로, 앤트로픽이 2024년 말에 오픈소스로 공개한 표준 통신 규약입니다. 쉽게 말하면 "AI 비서가 우리 회사 시스템에 안전하게 접근하는 방법"이라고 생각하시면 됩니다.

기존에는 ChatGPT나 Claude 같은 AI가 우리 회사 데이터베이스를 조회하려면 매번 커스텀 코드를 작성해야 했습니다. MCP는 이런 반복 작업을 표준화해서, 한 번 규격에 맞게 서버를 만들어두면 어떤 AI 클라이언트든 동일한 방식으로 연결할 수 있게 해줍니다.

왜 MCP 서버를 직접 만들어야 하나요?

Claude Code는 기본적으로 로컬 파일 읽기, 터미널 명령 실행, Git 작업만 가능합니다. 사내 API는 다음과 같은 이유로 직접 노출이 어렵습니다.

MCP 서버는 이런 중간 다리 역할을 합니다. AI는 "고객 조회 도구 호출"이라고만 요청하고, 실제 데이터는 서버가 사내 시스템에서 가져와 마스킹해서 돌려주는 구조입니다. 저는 이 분리 구조가 보안 팀과 AI 도입 팀 사이의 갈등을 해결하는 핵심이라고 생각합니다.

실습 준비물

아래 항목만 준비하시면 됩니다. 모두 무료입니다.

Step 1: 개발 환경 세팅

먼저 작업 폴더를 만들고 MCP 개발에 필요한 패키지를 설치합니다. 저는 uv를 추천하지만 pip도 동일하게 작동합니다.

# 작업 폴더 생성 및 이동
mkdir mcp-internal-api
cd mcp-internal-api

Python 가상환경 만들기

python3 -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate

MCP SDK와 HTTP 클라이언트 설치

pip install mcp httpx pydantic

설치가 끝나면 폴더 구조는 이렇게 됩니다.

mcp-internal-api/
├── venv/
├── server.py          # MCP 서버 메인 코드
├── internal_api.py    # 사내 API 호출 모듈
├── config.json        # 인증 정보 저장
└── README.md

Step 2: 사내 API 호출 모듈 작성

먼저 사내 API를 호출하는 파이썬 모듈을 작성합니다. 테스트용으로 가짜 API를 만들어두겠습니다. 실제 사내 API 엔드포인트가 있다면 base_url만 바꾸시면 됩니다.

# internal_api.py
import httpx
import os
from typing import Optional

INTERNAL_API_BASE = os.getenv("INTERNAL_API_BASE", "https://internal.example.com")
API_TOKEN = os.getenv("INTERNAL_API_TOKEN", "test-token-1234")

class InternalAPIClient:
    def __init__(self):
        self.headers = {
            "Authorization": f"Bearer {API_TOKEN}",
            "X-Internal-Client": "mcp-server"
        }

    def get_employee(self, employee_id: str) -> dict:
        """사내 직원 정보 조회"""
        with httpx.Client(timeout=10.0) as client:
            response = client.get(
                f"{INTERNAL_API_BASE}/hr/employees/{employee_id}",
                headers=self.headers
            )
            response.raise_for_status()
            data = response.json()
            # PII 마스킹: 전화번호 뒷자리 가리기
            if "phone" in data:
                data["phone"] = data["phone"][:-4] + "****"
            return data

    def list_recent_orders(self, region: Optional[str] = None, limit: int = 10) -> list:
        """최근 주문 내역 조회 (지역 필터 가능)"""
        params = {"limit": limit}
        if region:
            params["region"] = region
        with httpx.Client(timeout=15.0) as client:
            response = client.get(
                f"{INTERNAL_API_BASE}/sales/orders",
                headers=self.headers,
                params=params
            )
            response.raise_for_status()
            return response.json()

저는 보안상 PII 마스킹을 서버 단에서 처리하는 방식을 강력 추천합니다. AI가 절대 원본 데이터를 받지 못하게 하는 게 핵심입니다. 위 코드에서 전화번호 뒷자리를 마스킹한 부분이 바로 그 예시입니다.

Step 3: MCP 서버 메인 코드 작성

이제 진짜 MCP 서버를 만듭니다. mcp 라이브러리가 제공하는 FastMCP 클래스를 쓰면 30줄도 안 되는 코드로 완전한 서버를 만들 수 있습니다.

# server.py
import httpx
from mcp.server.fastmcp import FastMCP
from internal_api import InternalAPIClient

MCP 서버 인스턴스 생성

mcp = FastMCP("Internal HR & Sales Server")

사내 API 클라이언트 초기화

api_client = InternalAPIClient() @mcp.tool() def get_employee_info(employee_id: str) -> str: """ 사내 직원 정보를 조회합니다. 입력: 직원 ID (예: 'E12345') 반환: 이름, 부서, 이메일, 마스킹된 전화번호 """ try: data = api_client.get_employee(employee_id) return ( f"이름: {data.get('name')}\n" f"부서: {data.get('department')}\n" f"이메일: {data.get('email')}\n" f"전화번호: {data.get('phone')}" ) except httpx.HTTPStatusError as e: return f"조회 실패: HTTP {e.response.status_code}" except Exception as e: return f"오류 발생: {str(e)}" @mcp.tool() def search_recent_orders(region: str = "", limit: int = 10) -> str: """ 최근 주문 내역을 조회합니다. 입력: 지역(선택, 예: '서울'), 최대 개수(기본 10) 반환: 주문 목록 요약 """ region_filter = region if region else None orders = api_client.list_recent_orders(region=region_filter, limit=limit) if not orders: return "조회된 주문이 없습니다." lines = [f"총 {len(orders)}건의 주문을 찾았습니다."] for o in orders[:20]: lines.append( f"- [{o.get('order_id')}] {o.get('customer')} / " f"{o.get('region')} / {o.get('amount'):,}원" ) return "\n".join(lines) @mcp.resource("company://policies/data-handling") def data_handling_policy() -> str: """데이터 처리 정책 문서""" return ( "1. 모든 PII는 마스킹 후 반환합니다.\n" "2. 일 조회 한도는 100건입니다.\n" "3. 조회 로그는 90일 보관됩니다." ) if __name__ == "__main__": # stdio 방식으로 실행 (Claude Code와 표준 입출력으로 통신) mcp.run()

Step 4: Claude Code에 서버 등록하기

서버가 준비되었으니 Claude Code가 이 서버를 인식하도록 설정 파일을 작성합니다. Claude Code 설치 폴더 아래에 있는 .mcp.json 파일을 만들면 됩니다.

# ~/.claude/.mcp.json (Mac/Linux)

%USERPROFILE%\.claude\.mcp.json (Windows)

{ "mcpServers": { "internal-hr": { "command": "python", "args": ["C:/projects/mcp-internal-api/server.py"], "env": { "INTERNAL_API_BASE": "https://internal.example.com", "INTERNAL_API_TOKEN": "your-real-token-here" } } } }

저는 처음에 path를 잘못 적어서 한참 헤맸습니다. 절대 경로로 적어야 하고, 백슬래시 두 개 쓰거나 슬래시로 바꿔야 합니다. 윈도우 사용자분들이 가장 많이 겪는 실수입니다.

설정 후 Claude Code를 재시작하면 좌측 하단에 해머 모양 아이콘이 생기며 등록된 도구 목록이 표시됩니다. "internal-hr" 서버 아래에 get_employee_info, search_recent_orders 두 개가 보이면 성공입니다.

Step 5: HolySheep AI로 모델 비용 최적화하기

Claude Code는 기본적으로 Anthropic API를 직접 호출합니다. 하지만 팀 단위로 사용하면 비용이 빠르게 늘어나죠. 저는 Claude Code의 추론 모델 호출만 직접 API로 두고, 분류·요약·임베딩 같은 경량 작업은 HolySheep AI 게이트웨이로 라우팅해서 비용을 60% 이상 절감했습니다.

HolySheep AI 게이트웨이 호출 예시 (Python):