CPN 한국어 자습서 · 러닝패스 2 / 4 — Building with the Claude API
MCP
Defining tools with MCP
mcp_server.py에 도구 둘을 추가합니다. 예전엔 도구마다 커다란 JSON 스키마를 손으로 써야 했지만, 공식 MCP Python SDK를 쓰면 서버는 한 줄, 도구는 데코레이터 한 줄로 끝납니다. 뒤에서 SDK가 JSON 스키마를 자동 생성합니다. read_doc_contents(읽기)와 edit_document(찾아 치환)를 정의합니다.
Stephen Grider · Anthropic 기술 스태프
CLI 챗봇을 위한 MCP 서버를 만들기 시작합니다. 보다시피 CLI 자체는 이미 동작하고 Claude와 대화도 되지만, 아직 MCP 서버 쪽 기능은 붙어 있지 않습니다. 그래서 지금은 도구 두 개를 가진 MCP 서버를 더합니다 — 하나는 문서를 읽는 도구, 하나는 문서 내용을 수정하는 도구입니다.
서버 구현은 루트 프로젝트 디렉터리의 mcp_server.py 파일에 넣습니다. 여기에는 기본 MCP 서버를 미리 조금 세팅해 두었고, 메모리에만 존재하는 문서 모음(docs)을 정의해 두었습니다. 그리고 우리가 함께 완성할 작업을 TODO 항목으로 적어 두었습니다. 지금은 처음 두 항목, 즉 도구 두 개를 작성하는 것에만 집중합니다.
과거에 도구를 작성해 봤을 때는, 아 문법이 참 많았죠. 그 커다란 JSON 스키마들 말입니다. 그런데 좋은 소식이 있습니다. 이 프로젝트에서는 공식 MCP Python SDK를 씁니다. 우리가 가져다 쓰는 mcp 패키지가 바로 그것입니다. 이 패키지는 단 한 줄의 코드로 MCP 서버를 만들어 줍니다. 그리고 도구 정의도 아주 쉽게 해 줍니다.
도구를 정의하려면 함수 위에 데코레이터만 붙이면 됩니다. 예를 들어 이름이 add_ints이고 설명과 인자 두 개를 받는 도구를 이렇게 만들 수 있습니다. 이런 도구 정의를 한 번 써 두면, 뒤에서 MCP가 우리 대신 도구 JSON 스키마를 생성해 주고, 우리는 그걸 그대로 Claude에 넘기면 됩니다. 보다시피 도구 정의 같은 기본 작업이 한결 쉬워집니다.
우리의 첫 작업은 도구 두 개를 구현하는 것입니다. 먼저 문서를 읽는 도구부터 시작합니다. 목표는 단순합니다 — 문서 이름(ID)을 받아 그 내용을 돌려주는 것. 모든 문서는 이미 docs 딕셔너리 안에 있습니다. 키는 문서의 ID(이름), 값은 문서 내용입니다. 그러니 도구는 정말 단순합니다 — 문자열 하나를 받아 docs에서 알맞은 값을 찾아 돌려줍니다.
구현하려면 첫 TODO 바로 아래에 @mcp.tool로 새 도구를 정의합니다. 이름은 read_doc_contents, 설명은 "Read the contents of a document and return it as a string."로 둡니다. 본래는 Claude가 언제 이 도구를 써야 할지 명확히 알도록 설명을 아주 충실히 적는 게 좋지만, 여기서는 시간을 아끼려고 간단한 설명만 둡니다. 그다음 실제 도구 함수 read_document를 정의합니다.
이 함수는 문자열 doc_id 인자를 받는데, 이를 Field로 지정하고 설명 "Id of the document to read"를 붙입니다. 그러려면 파일 맨 위에서 pydantic의 Field 클래스를 import해야 합니다. 함수 본문에서는 먼저 Claude가 없는 문서를 요청한 경우를 처리합니다 — doc_id가 docs에 없으면, f-string으로 "Doc with id {doc_id} not found" 메시지를 담아 ValueError를 raise합니다. 그 검사를 통과하면 실제 문서 docs[doc_id]를 반환합니다.
이게 도구를 정의하는 전부입니다. 도구 이름, 설명, 기대 인자와 그 타입, 인자 설명까지 명시했습니다. 이 데코레이터와 Field 타입들을 MCP Python SDK가 한데 모아 우리 대신 JSON 스키마를 생성합니다. 첫 도구를 구현했으니 그 TODO를 지우고, 다른 도구 — 문서 수정 도구를 구현합니다.
같은 과정을 반복합니다. @mcp.tool에 이름 edit_document, 설명 "Edit a document by replacing a string in the documents content with a new string"를 줍니다. 함수 edit_document는 인자를 셋 받습니다 — 문서 ID, 찾을 옛 문자열, 그리고 그것을 대체할 새 문자열입니다. doc_id, old_str, new_str을 각각 Field와 설명으로 지정합니다. 문서 수정은 아주 단순한 찾아 치환(find and replace)입니다.
여기서도 Claude가 실제로 존재하는 문서를 요청하는지 확인합니다 — doc_id가 docs에 없으면 같은 식으로 ValueError를 raise합니다. 올바른 문서를 찾으면 docs[doc_id] = docs[doc_id].replace(old_str, new_str)로 치환합니다. 이렇게 도구 구현 두 개를 정말 빠르게 끝냈습니다. 거듭 말하지만, 이 MCP Python SDK로 도구를 정의하는 것이 스키마 정의를 손으로 쓰는 것보다 훨씬 쉽습니다. 좋은 출발입니다 — MCP 서버를 만들고 도구 두 개를 구현했습니다.
이 장에서 배우는 것What you'll learn
약 7분공식 MCP Python SDK — 서버는 FastMCP(...) 한 줄
도구 정의 = 함수에 @mcp.tool 데코레이터 한 줄
SDK가 뒤에서 도구 JSON 스키마를 자동 생성
인자는 pydantic Field로 타입·설명 명시
read_doc_contents — docs에서 찾아 반환, 없으면 ValueError
edit_document — old_str→new_str 찾아 치환
mcp = FastMCP("DocumentMCP") 한 줄로 서버를 만든다.name·description을 받는다.예전엔 도구마다 커다란 JSON 스키마를 손으로 써야 했습니다. 공식 MCP Python SDK는 서버를 한 줄로 만들고, 도구는 함수 위 데코레이터 한 줄로 정의하게 해 줍니다.
데코레이터 방식은 함수·타입·Field를 SDK가 모아 같은 JSON 스키마를 자동 생성합니다 — 결과는 같고, 손으로 쓸 일이 없습니다.
SDK가 보여 주는 도구 정의의 모양은 이렇습니다 — 이름·설명과 인자 두 개를 받는 도구. 한 번 써 두면 뒤에서 MCP가 JSON 스키마를 대신 생성해, 그대로 Claude에 넘길 수 있습니다.
# SDK가 보여 주는 도구 정의의 모양 — 함수 위 데코레이터 하나 @mcp.tool( name="add_ints", description="Add two integers together", ) def tool_fn( a=Field(description="First number to add"), b=Field(description="Second number to add"), ) -> int: return a + b
구현은 루트의 mcp_server.py에 들어갑니다. 기본 서버는 FastMCP(...) 한 줄로 만들어져 있고, 메모리에만 존재하는 docs 딕셔너리가 정의돼 있습니다 — 키는 문서 ID(이름), 값은 내용입니다.
from mcp.server.fastmcp import FastMCP from pydantic import Field mcp = FastMCP("DocumentMCP", log_level="ERROR") # 서버를 한 줄로 생성 docs = { "deposition.md": "This deposition covers the testimony of Angela Smith, P.E.", "report.pdf": "The report details the state of a 20m condenser tower.", "financials.docx": "These financials outline the project's budget and expenditures.", "outlook.pdf": "This document presents the projected future performance of the system.", "plan.md": "The plan outlines the steps for the project's implementation.", "spec.txt": "These specifications define the technical requirements for the equipment.", } # TODO: Write a tool to read a doc # TODO: Write a tool to edit a doc
적어 둔 TODO 중 처음 두 개 — 문서를 읽는 도구와 수정하는 도구 — 에만 집중합니다.
첫 TODO 아래에 @mcp.tool로 읽기 도구를 정의합니다. 이름 read_doc_contents, 인자 doc_id를 pydantic Field로 지정합니다. 없는 문서를 요청하면 ValueError를 던지고, 아니면 docs[doc_id]를 반환합니다.
@mcp.tool( name="read_doc_contents", description="Read the contents of a document and return it as a string.", ) def read_document( doc_id: str = Field(description="Id of the document to read"), ): if doc_id not in docs: raise ValueError(f"Doc with id {doc_id} not found") return docs[doc_id]
같은 방식을 반복해 수정 도구를 만듭니다. edit_document는 인자를 셋 — doc_id, old_str, new_str — 받고, 문서에서 옛 문자열을 새 문자열로 찾아 치환합니다. (Field 설명까지 모아 SDK가 JSON 스키마를 자동 생성합니다.)
@mcp.tool( name="edit_document", description="Edit a document by replacing a string in the documents content with a new string", ) def edit_document( doc_id: str = Field(description="Id of the document that will be edited"), old_str: str = Field( description="The text to replace. Must match exactly, including whitespace" ), new_str: str = Field( description="The new text to insert in place of the old text" ), ): if doc_id not in docs: raise ValueError(f"Doc with id {doc_id} not found") docs[doc_id] = docs[doc_id].replace(old_str, new_str)
ValueError로 안전하게 처리.FastMCP(...) 한 줄, 도구는 @mcp.tool 한 줄.read_doc_contents — docs에서 찾아 반환, 없으면 ValueError.edit_document — old_str를 new_str로 찾아 치환.Q1Python SDK로 도구를 가장 쉽게 정의하는 방법은?
데코레이터·타입·Field를 SDK가 모아 JSON 스키마를 자동 생성합니다. 손으로 쓸 필요가 없습니다.
Q2도구 인자의 타입과 설명은 무엇으로 지정하나요?
doc_id: str = Field(description=...) 형태로 타입과 설명을 함께 지정합니다.
Q3read_document가 없는 문서 ID를 받으면?
doc_id not in docs면 ValueError를 던져, 존재하지 않는 문서 요청을 안전하게 처리합니다.
도구를 작성했지만 아직 동작을 확인하지 못했습니다. 다음은 SDK가 제공하는 인메모리 브라우저 디버거로 서버를 띄워, 도구를 손으로 실행해 봅니다. → 서버 인스펙터
전 코스는 계속 무료입니다. 등록하면 이 코스의 남은 76개 레슨을 끝까지 읽을 수 있습니다.
이미 등록하셨다면 그때 쓰신 이메일을 넣어 주세요.