사내 티켓 시스템에서 티켓을 조회하는 파이썬 함수가 있다고 해 봅니다. 이 함수를 MCP 서버로 내놓으면 Claude Code든 VS Code든 직접 만든 Agent든 MCP를 지원하는 앱은 같은 방식으로 이 함수를 도구로 씁니다. 앱마다 따로 연결할 필요가 없습니다.
서버를 만드는 일은 생각보다 간단합니다. 파이썬 SDK가 함수의 이름, 설명, 타입 힌트를 읽어 도구 정의를 자동으로 만들어 줍니다. 아래에서 함수의 어느 부분이 도구 정의의 어느 칸이 되는지 짝을 눌러 보세요.
이 글의 서버 코드와 변환 결과는 Python MCP SDK 2.3.0으로 직접 실행해 확인했습니다. 도구 정의는 설명하기 쉽게 줄여 보였습니다.
MCP 공식 튜토리얼은 파이썬 SDK 2.0 이상을 쓰라고 안내합니다. 서버 클래스는 MCPServer입니다. 서버 객체를 하나 만들고 함수에 @mcp.tool()을 붙이면 등록이 끝납니다. 함수의 타입 힌트와 독스트링(함수 설명 글)이 도구의 입력 모양과 설명이 됩니다.
그래서 설명을 대충 쓰면 모델이 도구를 잘못 고릅니다. 앞서 도구를 설계할 때 배운 대로 이름은 하는 일이 드러나게, 설명은 언제 쓰는지 구체적으로 적습니다.
mcp = MCPServer("ticket-desk") # 1 서버 이름
@mcp.tool() # 2 함수를 도구로 등록
def get_ticket(ticket_id: str) -> dict:
"""티켓 번호로 티켓 한 건을 조회한다.
Args:
ticket_id: 티켓 번호 (예: T-1042)
"""
if ticket_id not in TICKETS:
raise ToolError(f"없는 티켓: {ticket_id}") # 3 예상한 실패
return {"id": ticket_id, **TICKETS[ticket_id]}
@mcp.tool()
def list_tickets(status: Literal["open", "waiting_parts", "closed"] = "open",
owner: str | None = None) -> list[dict]:
"""상태(와 담당자)로 티켓 목록을 조회한다."""
...
if __name__ == "__main__":
mcp.run(transport="stdio") # 4 전송 방식출력은 예시입니다. SDK의 클라이언트로 서버를 띄워 목록과 호출을 확인했고 티켓 데이터는 가짜입니다. 코드는 설명하기 쉽게 줄였습니다.
MCPServer는 도구, 자료, 프롬프트를 모아 두는 상자입니다. 이름은 앱이 서버를 구분할 때 보입니다.@mcp.tool()을 붙인 함수가 도구가 됩니다. 인자의 타입과 기본값이 입력 스키마가 되고 Literal로 보기를 닫아 두면 enum이 됩니다.ToolError로 던집니다. 메시지가 모델에게 그대로 전해지니 모델이 그걸 보고 다음 행동을 정할 수 있습니다.stdio는 앱이 서버를 직접 실행해 쓸 때, streamable-http는 서버를 따로 켜 두고 여러 앱이 쓸 때입니다. 차이는 아래에서 봅니다.티켓이 없는 것은 오류가 아니라 일어날 수 있는 일입니다. 이 실패를 ToolError로 던지면 서버는 도구 호출 결과에 오류 표시(is_error)를 켜고 내 메시지를 담아 돌려줍니다. 모델은 "T-9999는 없으니 번호를 다시 확인해야겠다"고 판단할 수 있습니다.
다른 예외를 그냥 던지면 SDK가 코드 실수로 보고 모델에게 도구 이름만 알립니다. 원인은 개발자가 서버 로그의 추적 기록으로 찾습니다. 잘못된 인자는 SDK가 스키마로 먼저 걸러 오류로 돌려주므로 따로 검사하지 않아도 됩니다.
stdio 전송에서는 앱이 서버를 직접 실행하고 표준 입력과 표준 출력으로 메시지를 주고받습니다. 그래서 서버 코드가 표준 출력에 아무 글이나 쓰면 안 됩니다. 공식 문서도 stdio 서버에서 print()를 쓰지 말고 로그는 표준 오류(stderr)로 보내라고 경고합니다.
Streamable HTTP는 서버를 따로 켜 두고 주소로 접속합니다. 여러 앱이 같은 서버를 쓸 수 있어 원격 서버에 맞습니다. 세 장면을 눌러 보세요.
# Inspector의 CLI 모드로 서버를 직접 시험한다
npx @modelcontextprotocol/inspector --cli python ticket_server.py \
--method tools/list
npx @modelcontextprotocol/inspector --cli python ticket_server.py \
--method tools/call --tool-name get_ticket --tool-arg ticket_id=T-1042출력은 예시입니다. Inspector 2.11.0의 CLI 모드로 목록과 호출을 직접 실행해 확인했습니다. 화면에서 눌러 보는 모드도 있습니다.
tools/list 결과에 도구 두 개와 입력 모양이 나오는지 봅니다. 앱이 모델에게 넘길 정의가 바로 이것입니다.isError가 꺼져 있는지 봅니다. 없는 번호나 빈 인자를 넣어 오류가 어떻게 돌아오는지도 시험해 두면 좋습니다.이렇게 시험한 서버는 어떤 MCP 앱에든 붙을 수 있습니다. 다음 글에서는 남이 만든 서버를 내 Agent에 꽂을 때 조심할 점을 알아보겠습니다.