Python MCP 도구 작성
함수에 @mcp.tool()을 적용하면 함수 이름, Docstring, 타입 힌트가 각각 도구 이름, 설명, 입력 스키마가 된다.
함수와 도구의 대응
MCP 도구는 이름, 설명, 입력 스키마(JSON Schema)로 정의한다. 이 세 항목을 직접 작성하면 코드와 불일치하기 쉽다. 공식 Python SDK는 함수 정의에서 이 세 항목을 추출한다.
서버 코드
pip install "mcp[cli]"
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
mcp = MCPServer("Stock")
STOCK = {"main": {"bolt-m8": 420}, "spare": {"bolt-m8": 35}}
@mcp.tool()
def get_stock(part_id: str, warehouse: str = "main") -> int:
"""부품의 현재 재고 수량을 돌려준다."""
if warehouse not in STOCK:
# 모델이 읽고 인자를 고칠 수 있게 오류로 알린다
raise ToolError(f"없는 창고: {warehouse}")
return STOCK[warehouse].get(part_id, 0)
if __name__ == "__main__":
mcp.run() # 인자를 주지 않으면 stdio로 뜬다
참고
SDK 1.x에서는 이 클래스가 FastMCP였으며 mcp.server.fastmcp에서 import했다. 2.x에는 해당 경로가 없다.
함수에서 추출되는 정보
- 함수 이름
get_stock이 도구 이름이 된다. - docstring이 모델이 읽는 설명이 된다.
- 타입 힌트가 입력 스키마가 된다. 기본값이 없는
part_id만required에 포함된다. - 타입이 일치하지 않는 인자는 함수 실행 전에 SDK가 거부한다.
실패를 문자열로 반환하면 성공한 결과로 처리된다. ToolError를 발생시키면 결과에 오류 표시가 설정되고, 작성한 메시지가 모델에 전달된다.
실행 방법
# Inspector로 띄워 도구 목록과 호출을 확인한다 (npx 필요)
mcp dev server.py
python server.py로 직접 실행하면 서버는 아무것도 출력하지 않고 입력을 대기한다. stdio 서버는 클라이언트가 자식 프로세스로 실행하여 사용하기 때문이다. mcp.run()을 if __name__ 아래에 두면 mcp dev가 파일을 import할 때 서버가 실행되지 않는다.