사내 REST API를 Claude 도구로: Python FastMCP로 MCP 서버 만들기

사내 REST API를 Claude 도구로: Python FastMCP로 MCP 서버 만들기

사내에 이미 잘 만들어둔 REST API가 있는데, Claude에게 “재고 조회해줘”, “이번 주 매출 뽑아줘” 같은 요청을 자연어로 시키고 싶은 경우가 많습니다. 이때 필요한 것이 바로 MCP(Model Context Protocol)이고, Python으로 가장 빠르게 MCP 서버를 만드는 방법이 FastMCP입니다. 이 글에서는 사내 REST API를 Claude 도구로 노출시키는 MCP 서버를 FastMCP로 직접 작성하고, Claude Desktop에 연결해 동작을 확인하는 과정까지 실제 코드로 다룹니다.

MCP와 FastMCP, 정확히 뭘 하는 도구인가

MCP는 Anthropic이 공개한 개방형 프로토콜로, LLM 애플리케이션(호스트)이 외부 데이터 소스나 도구(서버)에 표준화된 방식으로 접근하도록 정의한 규격입니다. 자세한 스펙은 공식 사이트에서 확인할 수 있습니다. 핵심 아이디어는 간단합니다. 개발자가 “도구”를 함수 형태로 정의해두면, Claude 같은 LLM이 대화 맥락에 따라 그 함수를 스스로 호출하고 결과를 받아 답변에 반영합니다.

사내 REST API는 보통 GET /orders/{id}, POST /inventory/search 같은 REST 방식 엔드포인트로 구성되어 있는데, 문제는 Claude가 이런 HTTP 엔드포인트를 직접 호출할 방법이 없다는 점입니다. MCP 서버는 이 API 호출을 함수로 감싸서 “도구”라는 이름표를 붙여주는 중간 계층 역할을 합니다.

FastMCP는 이 MCP 서버를 아주 적은 코드로 만들 수 있게 해주는 Python 프레임워크입니다. 공식 MCP Python SDK(pip install mcp) 안에도 mcp.server.fastmcp.FastMCP 클래스가 포함되어 있어서, 별도 프레임워크 설치 없이도 SDK만으로 FastMCP 스타일 서버를 작성할 수 있습니다. 함수에 @mcp.tool() 데코레이터 하나만 붙이면 함수 시그니처와 독스트링을 읽어 자동으로 도구 스펙(이름, 파라미터, 설명)을 생성해주는 것이 핵심 장점입니다.

개발 환경 준비하기

먼저 가상환경을 만들고 필요한 패키지를 설치합니다. CLI 도구까지 포함된 mcp[cli] 패키지를 설치하면 뒤에서 사용할 mcp dev 명령까지 함께 따라옵니다.

python -m venv .venv
source .venv/bin/activate  # Windows는 .venv\Scripts\activate
pip install "mcp[cli]" httpx

여기서 httpx는 사내 REST API를 실제로 호출할 때 쓸 비동기 HTTP 클라이언트입니다. requests로 대체해도 동작하지만, MCP 도구 함수는 async def로 정의하는 경우가 많아 비동기 클라이언트인 httpx와 궁합이 더 좋습니다.

파이썬 코드로 REST API 서버를 개발하는 화면

사내 API가 사설 네트워크(VPN, 인트라넷)에 있다면, MCP 서버를 실행하는 머신도 같은 네트워크 안에 있어야 한다는 점을 미리 확인해두는 것이 좋습니다. Claude Desktop이 로컬에서 MCP 서버 프로세스를 직접 실행(stdio 방식)하기 때문에, 결국 API 호출 주체는 Claude Desktop이 설치된 PC가 됩니다.

사내 REST API를 감싸는 MCP 도구 코드 작성

아래는 사내 주문 조회 API(GET /orders/{order_id})와 재고 검색 API(POST /inventory/search)를 각각 Claude 도구로 노출하는 예시입니다. 인증은 사내에서 흔히 쓰는 API 키 헤더 방식을 가정했습니다.

import os
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("internal-api-server")

BASE_URL = os.environ.get("INTERNAL_API_BASE", "http://internal-api.local")
API_KEY = os.environ.get("INTERNAL_API_KEY", "")


@mcp.tool()
async def get_order(order_id: str) -> dict:
    """주문 ID로 사내 주문 시스템에서 주문 상세 정보를 조회합니다.

    Args:
        order_id: 조회할 주문의 고유 ID (예: "ORD-2026-0001")
    """
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            f"{BASE_URL}/orders/{order_id}",
            headers={"X-API-Key": API_KEY},
            timeout=10.0,
        )
        resp.raise_for_status()
        return resp.json()


@mcp.tool()
async def search_inventory(keyword: str, limit: int = 10) -> list:
    """상품명 키워드로 사내 재고 시스템을 검색합니다.

    Args:
        keyword: 검색할 상품명 또는 SKU 일부
        limit: 반환할 최대 결과 수 (기본값 10)
    """
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            f"{BASE_URL}/inventory/search",
            json={"keyword": keyword, "limit": limit},
            headers={"X-API-Key": API_KEY},
            timeout=10.0,
        )
        resp.raise_for_status()
        return resp.json()


if __name__ == "__main__":
    mcp.run()

여기서 중요한 부분은 함수 독스트링입니다. Claude는 이 독스트링을 읽고 “이 도구가 언제, 어떤 인자로 쓰여야 하는지” 판단하기 때문에, 파라미터 설명을 구체적으로 적어둘수록 Claude가 엉뚱한 값을 넣어 호출하는 실수가 줄어듭니다. 타입 힌트(str, int, dict)도 함께 읽혀 자동으로 JSON 스키마로 변환되므로, 타입을 정확히 명시하는 습관이 실제 동작 안정성에 직결됩니다.

로컬 테스트와 Claude Desktop 연동

코드를 server.py로 저장했다면, Claude에 연결하기 전에 MCP Inspector로 먼저 동작을 확인하는 것이 좋습니다. mcp[cli]를 설치했다면 다음 명령으로 브라우저 기반 테스트 UI가 열립니다.

mcp dev server.py

Inspector 화면에서 get_order, search_inventory 도구가 목록에 나타나고, 파라미터를 입력해 직접 호출한 뒤 사내 API가 반환한 JSON이 그대로 표시되는지 확인합니다. 이 단계에서 인증 헤더 누락이나 타임아웃 문제 같은 실수를 미리 잡아낼 수 있습니다.

정상 동작이 확인되면 Claude Desktop 설정 파일(claude_desktop_config.json, macOS 기준 ~/Library/Application Support/Claude/, Windows 기준 %APPDATA%\Claude\)에 서버를 등록합니다.

AI 어시스턴트가 사내 시스템과 연동되는 자동화 개념도

{
  "mcpServers": {
    "internal-api-server": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"],
      "env": {
        "INTERNAL_API_BASE": "http://internal-api.local",
        "INTERNAL_API_KEY": "여기에_실제_키"
      }
    }
  }
}

저장 후 Claude Desktop을 재시작하면 대화창 하단 도구 아이콘에 get_order, search_inventory가 나타나고, “ORD-2026-0001 주문 상태 알려줘”처럼 자연어로 요청하면 Claude가 알아서 get_order 도구를 호출해 사내 API 응답을 요약해줍니다.

CLI 스크립트 대비 MCP 서버가 유리한 지점

사내 API를 다루는 방법이 MCP만 있는 것은 아닙니다. 이미 사내 배포 스크립트나 CLI 도구가 있다면 Claude에게 그 CLI를 셸 명령으로 실행시키는 방식도 가능합니다. 두 방식의 차이를 실무 관점에서 정리하면 다음과 같습니다.

비교 항목 CLI 스크립트 직접 실행 FastMCP 기반 MCP 서버
초기 구축 비용 낮음 (기존 스크립트 그대로 사용) 중간 (도구 함수 재작성 필요)
파라미터 검증 스크립트 내부 로직에 의존 타입 힌트 기반 자동 스키마 검증
여러 클라이언트 재사용 어려움 (호스트마다 재설정) 표준 프로토콜이라 재사용 용이
도구 단위 권한 제어 세밀하게 구현 가능 서버 단위가 기본, 세부 ACL은 별도 구현 필요
여러 도구 조합 호출 스크립트 조합 로직 필요 Claude가 문맥에 따라 자동 선택

정리하면, 일회성 스크립트 하나만 노출할 것이라면 CLI 실행으로도 충분하지만, 사내 API 엔드포인트가 여러 개이고 여러 팀·여러 클라이언트(Claude Desktop, Claude Code 등)에서 재사용해야 한다면 MCP 서버로 표준화해두는 쪽이 유지보수에 유리합니다.

자주 묻는 질문(FAQ)

Q1. FastMCP와 MCP 공식 Python SDK 중 무엇을 써야 하나요?
간단한 사내 API 래핑이라면 pip install "mcp[cli]"로 설치되는 공식 SDK의 FastMCP 클래스만으로 충분합니다. 인증 흐름이나 프록시 기능처럼 더 고급 기능이 필요해지면 별도 프로젝트로 개발되는 FastMCP(jlowin/fastmcp)를 추가로 검토해볼 수 있습니다.

Q2. 사내 API 인증 정보는 안전하게 관리되나요?
위 예시처럼 API 키를 claude_desktop_config.jsonenv 필드에 넣으면 로컬 프로세스 환경변수로만 전달되고 Claude 서버로 전송되지 않습니다. 다만 설정 파일 자체가 평문으로 저장되므로, 사내 정책상 민감한 키라면 OS 자격 증명 저장소를 읽어오는 코드로 대체하는 것이 안전합니다.

Q3. MCP 서버가 응답하지 않거나 도구가 안 보이면 어떻게 확인하나요?
가장 먼저 mcp dev server.py로 Inspector에서 단독 실행이 되는지 확인합니다. 여기서 정상 동작한다면 claude_desktop_config.json의 경로 오타나 command에 지정한 Python 인터프리터가 필요한 패키지(mcp, httpx)를 실제로 갖고 있는지(가상환경 경로 지정 여부)를 다시 점검하는 것이 일반적인 해결 순서입니다.

사내 REST API 한두 개를 MCP 도구로 옮겨보면 생각보다 코드량이 많지 않다는 것을 바로 체감할 수 있습니다. 이번 글의 get_order 예시를 실제 사내 엔드포인트 주소로만 바꿔서 mcp dev로 먼저 돌려보고, 정상 동작이 확인되면 Claude Desktop 설정에 등록하는 순서로 진행해보시길 권합니다.

Leave a Comment