LLM 앱 개발 실전 #11 MCP로 도구 연결하기

5 분 소요

6편10편에서 도구를 직접 정의해 Claude에 연결했습니다. 그런데 GitHub, Slack, 데이터베이스처럼 흔히 쓰는 대상이라면, 매번 도구 정의를 손으로 짜는 일이 번거롭습니다. MCP는 이 연결을 표준화한 규약입니다. 이번 글에서는 MCP가 무엇이고 왜 쓰는지, 그리고 Claude를 MCP 서버에 연결하는 방법을 다룹니다.

MCP란 #

MCP(Model Context Protocol)는 LLM이 도구와 데이터에 접근하는 방식을 표준화한 규약입니다. USB를 떠올리면 이해가 쉽습니다. 기기마다 다른 연결 단자를 쓰던 시절에는 기기와 케이블을 일일이 맞춰야 했지만, USB라는 표준이 생기면서 한 가지 방식으로 다 연결할 수 있게 됐습니다. MCP가 도구 연결에서 그런 역할을 합니다.

구조는 두 쪽으로 나뉩니다.

  • MCP 서버 — 어떤 기능이나 데이터를 도구로 노출하는 쪽입니다. GitHub, 파일 시스템, 데이터베이스 같은 대상마다 MCP 서버가 있습니다.
  • MCP 클라이언트 — 그 서버에 연결해 도구를 가져다 쓰는 쪽입니다. 우리 앱이 클라이언트가 됩니다.

핵심은 이렇습니다. 누군가 GitHub용 MCP 서버를 한 번 만들어 두면, 그 서버에 연결하는 모든 앱이 GitHub 도구를 손으로 다시 정의하지 않고 그대로 가져다 씁니다.

왜 MCP를 쓰는가 #

6편 방식으로도 도구는 얼마든지 만들 수 있습니다. 그런데 MCP에는 두 가지 이점이 있습니다.

  • 재사용 — 이미 만들어진 MCP 서버가 많습니다. GitHub, Slack, 파일 시스템, 여러 데이터베이스용 서버가 공개돼 있어, 가져다 연결하기만 하면 됩니다. 도구 정의와 실제 호출 코드를 직접 짤 필요가 없습니다.
  • 표준 — 도구를 한 번 MCP 서버로 만들어 두면, MCP를 지원하는 어떤 앱에서도 쓸 수 있습니다. 우리 앱뿐 아니라 다른 도구와 환경에서도 같은 서버가 동작합니다.

직접 도구를 정의하는 6편 방식과 MCP는 대립하지 않습니다. 우리 앱에만 있는 특수한 기능은 6편처럼 직접 만들고, GitHub 같은 범용 기능은 MCP 서버를 가져다 쓰는 식으로 섞습니다.

Claude를 MCP 서버에 연결하기 #

SDK는 MCP 서버의 도구를 가져와 Claude에 연결하는 도우미를 제공합니다. MCP 서버에 접속해 도구 목록을 받아, 10편에서 본 도구 러너에 그대로 넘기는 방식입니다.

mcp_connect.py
from anthropic import AsyncAnthropic
from anthropic.lib.tools.mcp import async_mcp_tool
from mcp import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters

client = AsyncAnthropic()

async def main():
    # MCP 서버에 접속한다
    async with stdio_client(StdioServerParameters(command="mcp-server")) as (read, write):
        async with ClientSession(read, write) as mcp:
            await mcp.initialize()

            # 서버가 제공하는 도구 목록을 가져온다
            tools = await mcp.list_tools()

            # 가져온 도구를 도구 러너에 넘긴다
            runner = client.beta.messages.tool_runner(
                model="claude-sonnet-4-6",
                max_tokens=1024,
                messages=[{"role": "user", "content": "사용 가능한 도구로 작업을 처리해줘."}],
                tools=[async_mcp_tool(t, mcp) for t in tools.tools],
            )
            async for message in runner:
                print(message)

mcp.list_tools()로 서버가 노출한 도구 목록을 받고, async_mcp_tool로 그 도구들을 Claude가 쓸 수 있는 형태로 바꿔 도구 러너에 넘깁니다. 우리는 도구의 이름이나 입력 스키마를 직접 적지 않았습니다. 서버가 알려 주기 때문입니다. 6편에서 손으로 짰던 tools 정의가, MCP에서는 서버 연결로 대체됩니다.

노트
MCP 클라이언트 기능은 현재 베타이고, mcp 패키지가 필요합니다(pip install anthropic[mcp]). MCP 서버를 직접 실행하지 않고 원격 MCP 서버에 연결하는 방법 등 세부는 빠르게 발전하고 있으므로, 실제 적용 전에 최신 문서를 확인하는 것이 좋습니다. 이 글의 목표는 “도구를 서버 연결로 가져온다"는 발상을 이해하는 데 있습니다.

원격 MCP 서버에 연결하기 #

위 예제는 우리가 직접 실행한 MCP 서버에 연결했습니다. 그런데 인터넷에 이미 떠 있는 원격 MCP 서버에 Claude를 바로 연결할 수도 있습니다. 이때는 호출에 mcp_servers로 서버 주소를 알려 줍니다.

mcp_remote.py
response = client.beta.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    mcp_servers=[
        {"type": "url", "name": "my-tools", "url": "https://example.com/mcp"},
    ],
    betas=["mcp-client-2025-11-20"],
    messages=[{"role": "user", "content": "MCP 도구로 작업을 처리해줘."}],
)

서버 주소만 넘기면 Claude가 그 서버의 도구를 알아서 가져와 씁니다. 로컬에서 서버를 실행할 필요도, 도구 목록을 직접 가져올 필요도 없습니다. 다만 인증이 필요한 서버라면 자격 증명을 함께 전달해야 하고, 그 방식은 서버마다 다릅니다. 이 기능도 베타이므로 적용 전에 최신 문서를 확인합니다.

흔히 걸려 넘어지는 곳 #

  • MCP가 도구를 대체한다고 오해한다 — MCP는 도구를 연결하는 표준일 뿐, 6편의 도구 콜링을 없애는 것이 아닙니다. 내부적으로는 같은 도구 호출 흐름이 돌아갑니다.
  • 신뢰할 수 없는 서버에 연결한다 — MCP 서버는 우리 앱에 도구를 노출합니다. 출처가 불분명한 서버를 연결하면 그 서버가 무슨 일을 하는지 알기 어렵습니다. 신뢰할 수 있는 서버만 씁니다.
  • 인증을 코드에 박는다 — GitHub 같은 서버는 인증이 필요합니다. 토큰을 코드에 직접 적지 말고, 1편에서처럼 환경 변수나 별도의 자격 증명 저장소로 다룹니다.

마무리 #

이번 글에서는 도구 연결의 표준인 MCP를 다뤘습니다.

  • MCP는 LLM이 도구와 데이터에 접근하는 방식을 표준화한 규약으로, 서버는 도구를 노출하고 클라이언트는 그걸 가져다 씁니다.
  • 이미 만들어진 MCP 서버를 연결하면, 도구 정의를 직접 짜지 않고 그대로 재사용할 수 있습니다.
  • 우리 앱만의 특수 기능은 직접 도구로 만들고, 범용 기능은 MCP로 가져오는 식으로 섞습니다.

이제 도구, 검색, 메모리, 에이전트, MCP까지 핵심 조각이 모였습니다. 다음 글인 “LLM 앱 개발 실전 #12 비용, 평가, 관측"에서는 만든 앱을 실제로 운영할 때 필요한 것들, 즉 비용을 줄이고 품질을 측정하고 동작을 들여다보는 방법을 다루겠습니다.

X