LLM 엔지니어링10장 · MCP 와 Prompt·Context Engineering4 / 22← 이전목차다음 →MetaContents

10장. MCP 와 Prompt·Context Engineering

MCP 의 구조

아파트의 관리사무소를 떠올려 봅시다. 주민(사용자)은 관리사무소(호스트)에 부탁하고, 관리사무소의 직원(클라이언트)은 경비실, 주차 관제실, 택배 보관함 같은 각 시설(서버)에 연락합니다. 시설마다 할 수 있는 일(도구), 볼 수 있는 기록(리소스), 정해 둔 안내문(프롬프트)이 있습니다.

호스트 안의 클라이언트가 MCP 서버에 접속하고, 서버는 도구, 리소스, 프롬프트를 내어 주는 구조도

1번 호스트는 사용자와 LLM 을 잇는 AI 앱입니다. 2번 클라이언트는 호스트 안에서 서버 하나와 한 줄로 연결됩니다. 3번 서버는 4번 도구(부르면 일을 한다), 5번 리소스(주소로 읽는 자료), 6번 프롬프트(정해 둔 지시문 틀)를 내어 줍니다. 여기서 관리사무소가 호스트, 직원이 클라이언트, 시설이 서버입니다.

서버가 내어 주는 세 가지

이름하는 일누가 쓰기로 정하나예
도구(tools)부르면 일을 하고 결과를 준다모델이 고른다재고 찾기, 출고 예약
리소스(resources)주소로 읽는 자료앱(호스트)이 고른다stock://reservations 예약 목록
프롬프트(prompts)정해 둔 지시문 틀사용자가 고른다"재고 보고서 쓰기" 틀

서버 만들기의 생김새

파이썬 mcp 패키지에서는 함수 위에 @mcp.tool() 을 붙이면 도구가 됩니다. 타입 힌트와 독스트링에서 도구 설명과 JSON 스키마가 만들어집니다. 아래는 생김새만 보이는 코드로, 실제로 띄우는 것은 실습 1에서 합니다.

Python
code = '''
from mcp.server.mcpserver import MCPServer

mcp = MCPServer("솔내 재고")

@mcp.tool()
def get_stock(code: str) -> dict:
    """품목 코드의 창고별 재고를 돌려준다."""
    ...

@mcp.resource("stock://warehouses")
def warehouses() -> str:
    """창고 목록"""
    return "본사 창고, 물류 센터"

if __name__ == "__main__":
    mcp.run()          # 기본은 stdio 로 클라이언트와 대화한다
'''
print(code.count("@mcp."), "개의 장식(데코레이터)")

2 개의 장식(데코레이터) 가 나옵니다. 도구 하나와 리소스 하나입니다. 9장에서 손으로 적던 JSON 스키마를 서버가 함수에서 만들어 줍니다.

코드에 나오는 함수

코드쉬운 뜻
code = '''…'''따옴표 셋으로 여러 줄의 글을 통째로 담는다. 여기서는 서버 코드를 실행하지 않고 글로만 들고 있다
MCPServer("솔내 재고")MCP 서버 객체를 만든다. 괄호 안은 클라이언트에게 알려 줄 서버 이름이다
@mcp.tool()데코레이터(decorator, 장식)다. 바로 아래 함수를 이 서버의 도구로 등록한다
@mcp.resource("stock://warehouses")아래 함수를 이 주소로 읽는 리소스로 등록한다
code: str, -> dict타입 힌트(type hint)다. 인자는 글자, 돌려주는 값은 사전이라고 적어 둔다. 서버가 이것으로 인자 스키마를 만든다
"""…"""독스트링(docstring, 문서 글)이다. 서버가 이 글을 도구 설명으로 보낸다
if __name__ == "__main__":이 파일을 직접 실행했을 때만 아래 줄을 돌린다. 다른 파일이 불러 쓸 때는 돌지 않는다
mcp.run()서버를 띄워 클라이언트의 메시지를 기다린다
code.count("@mcp.")글 안에 @mcp. 이 몇 번 나오는지 센다

이 코드가 뜻하는 것

관리사무소의 시설 등록 대장으로 읽어 봅니다. 시설이 문을 열면 대장에 이름과 하는 일을 적습니다. 직원은 대장만 보고 어느 시설에 무엇을 부탁할지 압니다. 여기서 등록 대장이 서버, 대장에 적는 일이 @mcp.tool(), 시설의 이름과 하는 일이 함수 이름과 독스트링입니다.

줄하는 일쉽게 말하면
mcp = MCPServer("솔내 재고")서버를 하나 만든다등록 대장을 새로 편다
@mcp.tool() 과 def get_stock(...)함수를 도구로 등록한다대장에 "재고 찾기" 시설을 적는다
@mcp.resource(...) 와 def warehouses()함수를 리소스로 등록한다게시판에 창고 목록을 붙여 둔다
mcp.run()서버를 띄운다사무소 문을 열고 연락을 기다린다
print(code.count("@mcp."), ...)장식의 수를 센다대장에 적힌 줄 수 세기

결과 2 개의 장식(데코레이터) 는 이 서버가 내어 줄 것이 두 가지(도구 하나, 리소스 하나)라는 뜻입니다. 이 블록은 서버를 띄우지 않고 글자만 셉니다. 실제로 띄우고 접속하는 것은 실습 1에서 합니다.

작게 줄여서 눈으로 보기

@mcp.tool() 이 속에서 하는 일을 아주 작게 흉내 내 봅니다. 아래 tool 은 설명을 위해 직접 만든 것이고, mcp 패키지의 실제 코드는 훨씬 많은 일을 합니다.

Python
import inspect

TOOLS = {}                                   # 도구 이름 → 설명과 인자

def tool(fn):
    """함수를 받아 이름, 설명, 인자 타입을 적어 두고 함수는 그대로 돌려준다"""
    params = inspect.signature(fn).parameters
    TOOLS[fn.__name__] = {"설명": fn.__doc__,
                          "인자": {k: p.annotation.__name__ for k, p in params.items()}}
    return fn

@tool
def get_stock(code: str, limit: int) -> dict:
    """품목 코드의 창고별 재고를 돌려준다."""
    return {}

print(TOOLS)
출력
{'get_stock': {'설명': '품목 코드의 창고별 재고를 돌려준다.', '인자': {'code': 'str', 'limit': 'int'}}}
결과읽는 법
'get_stock'함수 이름(fn.__name__)이 그대로 도구 이름이 되었다
'설명': '품목 코드의 …'독스트링(fn.__doc__)이 도구 설명이 되었다
'인자': {'code': 'str', 'limit': 'int'}타입 힌트에서 인자 이름과 종류를 읽었다. 9장에서 손으로 적던 스키마의 재료다

inspect.signature(fn) 은 함수의 서명(signature), 곧 인자 목록을 읽어 오는 표준 라이브러리 함수입니다. p.annotation 은 인자에 붙인 타입 힌트입니다. 데코레이터는 "함수를 받아 무언가를 적어 두고 함수를 돌려주는 함수"라서, @tool 한 줄만 붙여도 함수가 목록에 올라갑니다.

주의할 점

  • 도구는 모델이, 리소스는 앱이, 프롬프트는 사용자가 고른다는 구분은 규격의 뜻입니다. 실제로 어떻게 보여 줄지는 호스트 앱마다 다릅니다.
  • 한 호스트가 여러 서버에 접속할 수 있습니다. 서버마다 클라이언트가 하나씩 생깁니다.
← 이전 절10장 · 도구를 잇는 표준, MCP다음 절 →10장 · MCP 서버와 클라이언트의 대화