도구 하나 — 설명문이 코드보다 중요하다

5 분
목표MCP 서버 강좌 2편. 도구를 하나 등록한다. 이름·설명·입력 스키마·핸들러 네 조각인데, 그중 셋은 우리가 아니라 부르는 쪽이 읽는다. 그래서 설명문을 잘 쓰는 게 핸들러를 잘 쓰는 것보다 값이 크다.

1편의 서버는 아무것도 못 했다. 도구를 하나 붙인다.

네 조각

server.addTool(
  name: 'desk.count',
  description: 'How many people are waiting at the desk right now',
  inputSchema: const {'type': 'object', 'properties': {}},
  handler: (args) async =>
      CallToolResult(content: [TextContent(text: '{"waiting":3}')]),
);

그리고 기능을 켠다고 선언한다.

capabilities: ServerCapabilities(tools: ToolsCapability(listChanged: true))

셋은 우리가 안 읽는다

네 조각 중 handler 만 우리 코드가 읽는다. 나머지 셋은 전부 부르는 쪽이 읽는다.

tools/list 를 물으면 이렇게 나간다.

{
 "tools": [
  {
   "name": "desk.count",
   "description": "How many people are waiting at the desk right now",
   "inputSchema": {
    "type": "object",
    "properties": {}
   }
  }
 ]
}

사람이 만든 클라이언트라면 이 목록을 보고 버튼을 그린다. 모델이라면 이 목록을 읽고 어느 도구를 부를지 정한다. 그 판단의 근거가 description 한 줄이다.

그래서 이 줄은 주석이 아니다. 동작하는 코드다.

이름 짓기

desk.count 처럼 점으로 나눈 이름을 쓴다. 앞이 무엇에 대한 것인지, 뒤가 무엇을 하는지다.

desk.count      대기 인원을 센다
desk.admit      대기줄에서 들여보낸다
camera.settings 카메라 설정을 읽는다

도구가 셋일 때는 아무래도 좋아 보인다. 스물이 되면 그때 규칙이 없던 게 드러난다. 그리고 이름은 한 번 나가면 못 바꾼다 — 부르는 쪽 코드에 박혀 있기 때문이다.

설명문을 못 쓰면 생기는 일

두 도구가 이렇게 있다고 하자.

desk.count   "카운트"
desk.admit   "어드밋"

무엇을 세는지, 무엇을 들여보내는지 안 적혀 있다. 모델은 둘 중 아무거나 고른다. 그리고 틀리게 골라도 에러가 안 난다 — 도구는 정상 실행되고, 답만 엉뚱하다.

반대로 이렇게 적으면 고를 수 있다.

desk.count   "How many people are waiting at the desk right now"
desk.admit   "Admit a number of people from the queue"

설비 편에서 점검표 도구에 "이 단계들은 공장 엔지니어가 정한 것이고 바꿔 쓰면 안 된다" 를 설명문에 넣은 것도 같은 이유다. 그 문장이 모델에게 전달되는 유일한 지시다.

검증

2단계 검사는 한 줄이다.

ask step2 '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' > captures/s2.txt
grep -q 'desk.count' captures/s2.txt || die "step2: tool not listed"

등록했다는 것과 목록에 뜬다는 것은 다른 사건이다. addTool 을 부르고도 capability 를 안 켜면 목록이 안 나간다.

직접 돌려보기

dart run bin/step2.dart
bash verify.sh

가져갈 것

  1. addTool 네 조각 — 그중 셋은 바깥이 읽는다
  2. 점 표기 이름 — 셋일 때 정해 두지 않으면 스물에서 못 고친다
  3. 설명문 = 인터페이스 — 모델이 도구를 고르는 근거

다음 편

인자를 받는다. 그리고 받은 인자가 틀렸을 때 거절하는 법.

샘플 실행하기

makemind-academy/course_server/
git clone https://github.com/makemind-academy/course_server
cd course_server
dart pub get
dart run bin/step2.dart
GitHub에서 열기 →