에이전트 도구의 읽기·쓰기 분리
조회 도구만 즉시 실행을 허용하고, 변경 동작은 제안으로 접수하여 사람이 승인한 뒤 코드가 실행한다.
조회와 변경의 혼재
조회와 변경이 하나의 도구에 혼재하면 승인 규칙을 적용할 수 없다. 모든 호출에 승인을 요구하면 조회에도 불필요한 승인 절차가 추가되고, 모든 호출을 자동으로 실행하면 모델의 잘못된 판단이 즉시 설비 값을 변경한다.
분리 기준
| 구분 | 예 | 실행 |
|---|---|---|
| 조회 도구 | 현재 값 읽기 | 모델이 즉시 호출 |
| 제안 도구 | 설정값 변경 제안 | 제안만 기록 |
| 실행 코드 | 설정값 쓰기 | 사람이 승인한 뒤 코드가 호출 |
값을 실제로 변경하는 함수는 도구로 노출하지 않는다. 모델이 호출할 수 있는 범위는 제안까지이다.
import uuid
SETPOINT = {"oven-1": 180.0}
PROPOSALS: dict[str, dict] = {}
def get_setpoint(device: str) -> float:
"""조회 도구. 값을 바꾸지 않는다."""
return SETPOINT[device]
def propose_setpoint(device: str, value: float) -> str:
"""제안 도구. 기록만 하고 제안 번호를 돌려준다."""
proposal_id = uuid.uuid4().hex[:8]
PROPOSALS[proposal_id] = {"device": device, "value": value}
return proposal_id
def apply_proposal(proposal_id: str, approver: str) -> None:
# 도구가 아니다. 승인 화면의 코드에서만 부른다
p = PROPOSALS.pop(proposal_id)
SETPOINT[p["device"]] = p["value"]
print(approver, "승인:", p)
Annotation의 효력
MCP 도구에는 도구의 성격을 나타내는 annotation을 지정할 수 있다.
readOnlyHint: 환경을 변경하지 않음을 나타낸다. 기본값은 false이다.destructiveHint: 삭제하거나 덮어쓸 수 있음을 나타낸다. 기본값은 true이다.idempotentHint: 같은 인자로 다시 호출해도 추가 변경이 없음을 나타낸다. 기본값은 false이다.
사양에는 이 값들이 힌트일 뿐이라고 명시되어 있다. 서버가 자체적으로 선언하는 값이므로 실제 동작과 일치한다는 보장이 없고, 클라이언트는 신뢰하는 서버가 아니면 이 값을 신뢰하지 않아야 한다. annotation은 승인 화면에 표시하는 정보로만 사용하고, 차단은 위 구조와 서버 측 권한 검사에 위임한다.