MCP로 사용자에게 되묻기 — elicitation은 언제?
에이전트가 정보가 부족할 때 추측으로 툴을 재호출하면, 잘못된 인자·중복 부작용·사용자 신뢰 하락이 이어집니다. MCP elicitation은 서버가 클라이언트에게 elicitation/create를 보내 사용자 입력을 정식으로 요청하는 패턴입니다. 클라이언트가 elicitation 능력을 선언했을 때만 쓸 수 있고, UI 경로명은 제품마다 달라도 **동작(메시지·스키마·응답 액션)**은 스펙이 고정합니다.
이 글의 축은 셋입니다. 툴 실패와 되묻기의 차이 · 최소 질문 설계 · 타임아웃·취소. 요금·플랜·제휴·창작 후기는 없습니다.
근거는 Model Context Protocol 스펙의 elicitation(폼/URL 모드, message·requestedSchema, 응답 action: accept / decline / cancel)입니다. 제품별 메뉴 이름은 적지 않고 동작만 설명합니다.
툴 실패와 되묻기의 차이는?
한 줄 답: 툴 실패는 tools/call이 에러·불완전 결과로 끝난 상태이고, elicitation은 처리 도중에 서버가 사용자에게 구조화된 입력을 요구하는 별도 요청입니다. 같은 “정보가 없다”라도, 전자는 재시도·스키마 수정 쪽이고 후자는 사용자 확인 쪽입니다.
| 구분 | 툴 실패 (tools/call) | Elicitation (elicitation/create) |
|---|---|---|
| 주체 | 보통 클라이언트→서버 툴 호출의 결과 | 서버→클라이언트로 사용자 입력 요청 |
| 신호 | JSON-RPC 에러 또는 실패 콘텐츠 | message + (폼이면) requestedSchema |
| 사용자 개입 | 에이전트가 추측·재호출할 수 있음 | 클라이언트가 UI로 묻고, 사용자가 응답 |
| 전제 | 툴·인자 스키마 | 클라이언트가 elicitation capability 선언 |
언제 되묻기가 맞나:
- 선택·확인이 필요한 값(대상 브랜치, 삭제 여부, 프로젝트 ID)인데 컨텍스트에 없음.
- 툴 인자로 넣기엔 민감하지 않지만 추측하면 위험한 값(표시 이름, 이메일 형식의 연락처 등—폼 모드).
- 비밀번호·API 키·결제 자격처럼 클라이언트/LLM을 거쳐선 안 되는 값 → 스펙상 URL 모드(폼 금지).
언제 툴 실패/재설계가 맞나:
- 필수 인자가 툴
inputSchema에 있는데 에이전트가 비워 호출한 경우 → 스키마·설명을 고치거나, 호출 전에 에이전트 규칙을 보강. - 서버/네트워크/권한 오류 → elicitation이 아니라 에러 처리·재시도 정책.
- 클라이언트가 elicitation을 선언하지 않음 → 서버는 해당 모드로 요청을내면 안 됩니다(스펙: 미지원 모드 금지).
폼 모드 예(개념):
{
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "배포 대상 환경을 선택해 주세요.",
"requestedSchema": {
"type": "object",
"properties": {
"env": {
"type": "string",
"title": "환경",
"enum": ["staging", "production"]
}
},
"required": ["env"]
}
}
}
에이전트가 env를 추측해 deploy 툴을 두 번 치는 것보다, 한 번 묻고 accept의 content로 이어가는 편이 안전합니다.
최소 질문 설계는?
한 줄 답: 한 번의 elicitation에 flat object + 소수 필수 필드만 둡니다. message로 “왜 필요한지”를 쓰고, requestedSchema는 스펙이 허용하는 원시 타입(string / number / boolean / enum)만 사용합니다. 중첩 객체·객체 배열은 폼 모드에서 의도적으로 막혀 있습니다.
설계 체크리스트:
- 필수(
required)를 최소화 — 지금 툴 한 번에 꼭 필요한 키만. “나중에 쓸지도” 필드는 빼기. message는 한 문장으로 동기 — “무엇을 / 왜” (예: “스토리지 버킷 이름을 확인합니다. 잘못된 버킷에 쓰면 데이터가 섞입니다.”).- 필드
title·description·enum/format— 클라이언트가 폼을 그릴 때 힌트.email·uri·date등 지원 format을 쓰면 검증이 쉬워집니다. - 기본값(
default) — 안전한 기본이 있으면 스키마에 넣고, 클라이언트가 지원하면 미리 채웁니다. 위험한 기본(production 배포 등)은 두지 않습니다. - 민감 정보는 폼 금지 — 비밀번호·토큰·결제 정보는 URL 모드. 폼으로 받으면 클라이언트/로그에 노출될 수 있습니다.
하지 말 것:
- 한 화면에 프로필 전체·설정 수십 개를 몰아넣기(이탈·오입력↑).
- “전부 optional”인 거대 스키마로 책임 전가(에이전트가 다시 추측).
- 폼 모드 URL을 클릭 유도 문구로 숨기기(스펙: 폼 필드에 클릭용 URL을 넣지 않는 편이 안전).
최소 스키마 예:
{
"type": "object",
"properties": {
"confirm": {
"type": "boolean",
"title": "삭제 진행",
"description": "이 작업을 되돌리기 어렵습니다."
}
},
"required": ["confirm"]
}
질문에 답이 두 갈래면 boolean/enum 하나가, 자유 서술 장문보다 재현·자동화에 유리합니다.
타임아웃·취소는?
한 줄 답: 스펙 응답은 accept / decline / cancel 세 액션입니다. 명시적 거절은 decline, 닫기·Esc·이탈처럼 선택 없이 닫힌 경우는 cancel에 가깝습니다. 타임아웃은 스펙에 별도 action 이름이 없으므로, 클라이언트·서버 구현이 취소에 가깝게 처리하거나 JSON-RPC 오류로 끊는 경우가 많습니다—제품 UI 경로는 달라도, 서버는 세 갈래(+미응답)를 모두 가정해야 합니다.
| action | 의미(스펙) | 서버 쪽 권장 처리 |
|---|---|---|
accept | 사용자가 제출·동의 | content(폼)로 진행. URL 모드는 content 없이 동의만 |
decline | 명시적 거절 | 대안 제안·해당 툴 중단. 같은 질문 즉시 재폭격 금지 |
cancel | 선택 없이 닫힘 | 나중에 다시 묻거나, 안전한 기본 경로로 폴백 |
운영 메모:
- 미응답/타임아웃 — UI가 일정 시간 후 닫히면 보통
cancel또는 요청 실패로 보입니다. 서버는 “수락으로 간주”하면 안 됩니다. - URL 모드 —
accept는 “URL로 가도 된다”는 동의이지, 외부 절차 완료가 아닙니다. 완료는 선택적notifications/elicitation/complete일 수 있고, 클라이언트가 알림을 못 받아도 수동 재시도·취소를 남겨 두는 것이 스펙 권고에 가깝습니다. - 능력 불일치 — 클라이언트가 선언하지 않은 모드로
elicitation/create를내면 Invalid params(-32602) 쪽이 됩니다. 초기화 때 capability를 확인하세요. - 상태 — 실무 elicitation은 사용자별로 “이미 받았는지”를 서버가 안전하게 묶어야 합니다. 세션 ID만으로 묶지 말라는 보안 가이드가 있습니다.
{
"result": {
"action": "cancel"
}
}
취소·타임아웃 뒤에는 같은 message로 무한 재질문하지 말고, 채팅에 한 줄 요약(“환경 선택이 취소되었습니다. staging으로만 진행할까요?”) 후 다음 턴으로 넘기는 편이 사용자 부담이 적습니다.
자주 묻는 질문
Q. elicitation 없이 채팅으로 “뭐로 할까요?”만 물어보면 안 되나요?
A. 가능하지만, 구조화·검증·decline/cancel 구분이 약합니다. 폼 스키마가 있으면 클라이언트가 입력 검증을 돕고, 서버는 content 키를 신뢰 가능하게 읽습니다.
Q. 툴 인자 스키마와 requestedSchema를 똑같이 복제해야 하나요?
A. 아닙니다. 툴 스키마는 호출 계약이고, elicitation은 사용자에게 확인할 최소 부분집합이면 됩니다. 확인 후 서버/에이전트가 툴 인자로 매핑합니다.
Q. 제품 화면에서 버튼 이름이 다른데 괜찮은가요?
A. 괜찮습니다. 스펙은 UI 위젯을 강제하지 않고 요청/응답 의미를 정의합니다. 구현체는 Submit/Reject/닫기를 각각 accept/decline/cancel에 매핑하면 됩니다.
정리
정보가 없을 때 툴을 더 때리는 것과 사용자에게 되묻는 것은 다릅니다. MCP elicitation은 elicitation/create로 메시지와(폼이면) 최소 스키마를 보내고, accept·decline·cancel(및 미응답)을 구분해 이어가면 됩니다. 민감 정보는 폼이 아니라 URL 모드로 두고, 타임아웃을 수락으로 간주하지 마세요. 추측보다 한 번의 짧은 확인이 에이전트 부작용을 줄입니다.