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

10장. MCP 와 Prompt·Context Engineering

실습 1 — MCP 서버 만들기

가상 회사 "솔내기술"의 사무용품 재고(stock.csv)를 내어 주는 MCP 서버를 파일로 만들고, 클라이언트로 접속해 도구 목록을 받아 봅니다. pip install mcp 로 설치해야 합니다(교안을 만든 PC 는 2.3.0). 서버 파일은 out_stock_server.py 로 data 폴더에 저장됩니다.

1단계. 서버 파일 쓰기

Python
from pathlib import Path

SERVER = r'''
import os
import pandas as pd
from mcp.server.mcpserver import MCPServer

HERE = os.path.dirname(os.path.abspath(__file__))
STOCK = pd.read_csv(os.path.join(HERE, "stock.csv"))       # 서버 파일 옆의 재고 표
mcp = MCPServer("솔내 재고")

@mcp.tool()
def get_stock(code: str) -> dict:
    """품목 코드(예: P-101)의 창고별 재고 수량과 최소수량을 돌려준다."""
    rows = STOCK[STOCK["코드"] == code]
    if rows.empty:
        return {"오류": f"{code} 라는 품목 코드는 없습니다."}
    return {"품목": rows["품목"].iloc[0], "창고별": rows[["창고", "수량", "최소수량"]].to_dict("records")}

@mcp.tool()
def list_items(category: str = "") -> dict:
    """분류(용지, 토너, 필기구, 전자, 탕비)의 품목 코드와 이름 목록을 돌려준다. 비우면 모든 품목."""
    rows = STOCK if not category else STOCK[STOCK["분류"] == category]
    return {"품목": rows[["코드", "품목"]].drop_duplicates().to_dict("records")}

@mcp.resource("stock://warehouses")
def warehouses() -> str:
    """창고 목록"""
    return ", ".join(STOCK["창고"].unique())

if __name__ == "__main__":
    mcp.run()          # stdio 로 클라이언트와 대화한다
'''
Path("out_stock_server.py").write_text(SERVER, encoding="utf-8")
print("서버 파일:", Path("out_stock_server.py").stat().st_size, "바이트")
출력
서버 파일: 1263 바이트

2단계. 클라이언트로 접속해 도구 목록 받기

Python
import asyncio, json, os, sys
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

# sys.executable: 지금 쓰는 파이썬(.venv)으로 서버를 띄운다
PARAMS = StdioServerParameters(command=sys.executable, args=["out_stock_server.py"])

async def show_tools():
    # errlog: 서버가 표준 오류에 남기는 기록을 버린다(출력을 깔끔하게)
    async with stdio_client(PARAMS, errlog=open(os.devnull, "w")) as (read, write):
        async with ClientSession(read, write) as session:
            init = await session.initialize()                     # 인사: 서버 이름과 판을 받는다
            print("서버:", init.server_info.name)
            for t in (await session.list_tools()).tools:          # tools/list
                print("도구:", t.name, "|", t.description)
                print("  스키마:", json.dumps(t.input_schema["properties"], ensure_ascii=False))

asyncio.run(show_tools())
출력
서버: 솔내 재고
도구: get_stock | 품목 코드(예: P-101)의 창고별 재고 수량과 최소수량을 돌려준다.
  스키마: {"code": {"title": "Code", "type": "string"}}
도구: list_items | 분류(용지, 토너, 필기구, 전자, 탕비)의 품목 코드와 이름 목록을 돌려준다. 비우면 모든 품목.
  스키마: {"category": {"default": "", "title": "Category", "type": "string"}}

이 실습이 뜻하는 것

이 실습은 가게에 주문 창구를 하나 내는 일과 같습니다. 창구 담당자가 무엇을 주문할 수 있는지 적은 메뉴판과 응대 규칙을 안내문으로 써 두고(1단계), 손님이 창구에 와서 인사한 뒤 "여기서 무엇을 할 수 있나요?" 하고 메뉴판을 받아 봅니다(2단계). 여기서 창구가 MCP(Model Context Protocol, 모델에 맥락을 건네는 규약) 서버, 손님이 클라이언트, 메뉴판의 항목이 도구, 항목마다 적힌 "무엇을 넣어야 하는지"가 입력 스키마입니다. 비유와 다른 점은, 이 창구에는 사람이 아니라 프로그램이 찾아오고, 손님이 올 때마다 창구(서버 프로그램)가 새로 열렸다 닫힌다는 것입니다.

단계출력뜻
1. 서버 파일 쓰기서버 파일: 1263 바이트서버 코드가 out_stock_server.py 라는 1,263바이트짜리 파일로 저장되었습니다. 아직 실행한 것은 아니고 파일만 생겼습니다
2. 인사서버: 솔내 재고클라이언트가 서버를 띄워 접속했고, 서버가 자기 이름을 알려 주었습니다. 연결이 잘 되었다는 뜻입니다
2. 도구 목록get_stock, list_items 와 설명서버가 내어 주는 도구는 둘입니다. 설명 글은 코드의 함수 설명(독스트링)이 그대로 옮겨진 것입니다
2. 스키마"type": "string", "default": ""get_stock 에는 글자로 된 품목 코드를 꼭 넣어야 하고, list_items 의 분류는 비워도 됩니다. 손으로 쓰지 않았는데 서버가 만들어 주었습니다

한 줄로 줄이면, 재고 표를 MCP 서버로 감싸 두면 어떤 클라이언트든 접속해서 "무슨 도구가 있고 무엇을 넣어야 하는지"를 스스로 알아낼 수 있다는 것을 보인 실습입니다. 아직 도구를 실제로 부르지는 않았습니다. 도구를 불러 재고 숫자를 받는 것은 실습 2, LLM(Large Language Model, 대규모 언어 모델)이 도구를 고르게 하는 것은 실습 3에서 합니다.

코드 설명

  • 서버 코드를 글로 적어 파일로 저장하는 것은 교안에서 한 파일로 보이기 위해서입니다. 실제로는 out_stock_server.py 를 편집기에서 직접 만들어도 됩니다.
  • 서버 이름 "솔내 재고"와 도구 둘(get_stock, list_items)이 왔습니다. 도구 설명은 함수의 독스트링이 그대로 쓰였습니다.
  • 스키마는 타입 힌트에서 만들어졌습니다. code: str 은 {"type": "string"}, 기본값이 있는 category: str = "" 는 "default": "" 가 붙어 필수가 아닌 인자가 됩니다. 9장에서 손으로 적던 JSON 스키마를 서버가 대신 만든 것입니다.
  • asyncio.run() 은 async 함수를 실행합니다. MCP 클라이언트는 서버와 주고받는 동안 기다림이 많아 비동기(async)로 짜여 있습니다. async with 로 연결을 열면 블록을 빠져나갈 때 서버 프로그램도 닫힙니다.
← 이전 절10장 · 실습 지도다음 절 →10장 · 실습 2 — 클라이언트로 도구와 리소스 쓰기