사람이 없는 에이전트에서 '묻기'는 허용이 아니다: Claude Code 2.1.259의 기본 거부 설계
자동화 서버에서 코딩 에이전트를 돌릴 때 가장 애매한 상태는 실패가 아니라 "승인을 기다리는 중"이다. 사람은 없는데 도구 호출은 결정을 요구한다. 파이프라인이 멈추기도 하고, 승인 경로를 억지로 열어 두면 원래 막혀야 할 작업이 통과할 수도 있다.
Anthropic이 9월 3일 오전 7시 33분(KST)에 공개한 Claude Code v2.1.259는 이 경계를 여러 곳에서 손봤다. 비대화형 실행에서 승인 질문을 받을 주체를 아예 none으로 지정할 수 있게 했고, 조직 정책 파일을 읽지 못하면 조용히 정책 없이 시작하는 대신 실행을 거부하도록 바꿨다. 조직이 모든 사용자에게 원격 MCP 서버를 배포하는 설정도 추가했다. 세 변화는 기능 목록처럼 보이지만, 실제로는 하나의 원칙으로 묶인다. 무인 실행에서는 "결정할 수 없음"을 허용으로 번역하면 안 된다.
요약: 자동화의 빈칸을 세 곳에서 닫았다
첫째, --permission-prompts none은 --print 비대화형 실행에서 승인 질문을 받을 사람이 없다고 명시한다. 활성 permission mode가 허용하거나 거부할 수 있는 호출은 그대로 판단하고, 질문이 필요한 호출만 자동으로 거부한다. 모든 권한 검사를 끄는 bypassPermissions와는 반대 방향이다.
둘째, 관리 정책의 입력이 망가지면 시작 자체를 막는다. 릴리스 노트는 managed settings 파일, drop-in, macOS MDM plist, Windows HKLM 값을 파싱할 수 없을 때 더 이상 정책이 조용히 비적용되지 않으며 Claude Code가 시작을 거부하고 문제가 난 출처를 표시한다고 설명한다.
셋째, 새 managedMcpServers 관리 설정은 조직이 HTTP/SSE MCP 서버를 모든 사용자에게 제공할 수 있게 한다. 릴리스 노트에 따르면 .mcp.json과 같은 entry 형태를 쓰지만, 로컬 command 실행을 지정한 항목은 건너뛴다. 중앙 배포가 원격 연결 배포와 로컬 프로세스 실행을 같은 것으로 취급하지 않는 셈이다.
승인 질문을 없애는 것과 권한 검사를 없애는 것은 다르다
Claude Code의 공식 권한 문서는 규칙을 deny → ask → allow 순서로 평가한다고 설명한다. 더 구체적인 allow 규칙이 있어도 넓은 deny가 먼저 맞으면 차단된다. 이 판단은 모델의 지시문이 아니라 Claude Code 런타임이 집행한다.
새 플래그는 이 순서를 바꾸지 않는다. 2.1.259 패키지의 실제 --help 출력은 --permission-prompts가 host와 none을 받는다고 적는다. 기본값인 host는 SDK host 또는 --permission-prompt-tool이 질문에 답하는 방식이다. none은 질문에 답할 주체가 없고, prompt가 필요해진 호출을 자동 거부한다. permission mode는 나머지 호출을 계속 판단한다.
--permission-prompts none은 권한 검사를 끄지 않는다. 활성 permission mode가 판단한 뒤 사람의 답이 필요한 호출만 거부한다. 출처: github.com/anthropics/claude-code/releases/tag/v2.1.259.
이 구분은 CI에서 꽤 중요하다. --dangerously-skip-permissions는 질문을 제거하기 위해 검사를 건너뛰지만, --permission-prompts none은 질문이 생기면 그 작업을 포기한다. 전자는 "아무도 묻지 않으니 실행"이고 후자는 "아무도 답하지 않으니 거부"다. 무인 runner에는 후자가 더 예측 가능한 기본값이다.
다만 자동 거부가 job 실패를 보장하는지는 별도 문제다. 에이전트가 거부된 도구 대신 다른 경로로 목표를 끝낼 수도 있다. 따라서 출력 JSON, exit code, 생성 파일과 테스트 결과를 함께 확인해야 한다. "prompt가 없었다"는 사실만으로 필요한 작업이 실행됐다고 보면 안 된다.
관리 정책은 읽히지 않으면 없는 것이 아니라 고장 난 것이다
조직 정책은 사용자 설정보다 우선한다. 공식 managed settings 문서는 서버 관리 설정, MDM·OS 정책, 시스템의 managed-settings.json, Windows HKCU 순으로 출처를 설명한다. 기본 first-wins에서는 가장 높은 순위의 유효한 출처 하나가 정책을 공급하고, merge를 명시하면 여러 관리자 출처를 종류별 규칙으로 합친다.
문제는 정책 파일이 존재하지만 JSON 파싱이나 읽기에 실패한 경우다. 보안 규칙이 비어 있는 것과 정책 로딩이 실패한 것은 운영적으로 전혀 다르다. 릴리스 노트의 새 동작은 이 모호함을 시작 단계에서 끊는다. 파일, drop-in, MDM plist, HKLM 중 문제가 난 출처를 지목하고 실행을 거부한다.
공식 문서는 개별 값이 schema에 맞지 않을 때의 더 세밀한 처리도 설명한다. 복구 가능한 잘못된 entry는 제외하고 나머지 유효한 정책을 유지한다. allowedMcpServers 자체가 잘못되면 빈 allowlist로 취급해 MCP 서버를 하나도 허용하지 않는다. allowManagedMcpServersOnly가 잘못되면 true로 보고, crossSessionInbound는 가장 제한적인 refuse로 처리한다. 즉 전체 파일을 읽지 못한 실패와, 일부 항목을 좁혀서 복구할 수 있는 실패를 구분한다.
관리 정책은 파일 존재가 아니라 적용 결과로 검증한다. 읽기·파싱 실패는 시작을 막고, 배포된 MCP 목록은 기대 목록과 다시 대조한다. 출처: code.claude.com/docs/en/managed-settings.
여기서 주의할 점이 있다. 공식 문서에는 다른 관리자 출처가 정책을 제공하는 경우 남은 출처를 계속 읽는 설명도 있다. 따라서 "어떤 관리 파일 하나가 깨지면 모든 환경에서 항상 종료"라고 일반화하면 너무 세다. 2.1.259 릴리스가 명시한 범위와 실제 배포의 source precedence를 함께 확인해야 한다.
중앙 MCP 배포에서 command 항목을 건너뛰는 이유
MCP 서버는 같은 이름 아래 있어도 위험면이 다르다. HTTP와 SSE 서버는 원격 endpoint로 연결한다. stdio 서버는 로컬 command를 실행해 프로세스를 띄운다. 조직 관리자가 원격 서비스 목록을 배포하는 기능에 로컬 command 실행까지 그대로 섞으면, 단순 연결 설정이 fleet 전체의 실행 정책으로 커진다.
managedMcpServers에 대해 공개된 근거는 현재 릴리스 노트 한 줄이 핵심이다. 조직은 .mcp.json과 같은 entry 형태로 HTTP/SSE 서버를 제공할 수 있고, command를 지정한 entry는 건너뛴다. 아직 이 글을 작성한 시점의 공식 settings reference에는 해당 key의 별도 항목이 보이지 않았다. 따라서 지원되는 모든 세부 필드나 기존 managed-mcp.json과의 precedence를 추측해서 적지 않는 편이 안전하다.
기존 공식 managed MCP 문서는 별도의 managed-mcp.json을 사용한 고정 배포를 설명한다. 이 파일은 HTTP/SSE뿐 아니라 stdio command도 담을 수 있고, 배포하면 사용자가 추가한 다른 MCP 서버를 막는 독점 구성이 된다. 새 관리 설정과 이름이 비슷하지만 공개된 동작 범위가 같다고 단정해서는 안 된다. 업그레이드 전에는 작은 대상 그룹에서 /status, claude mcp list, 실제 연결 실패 메시지를 읽어야 한다.
실전 적용: runner를 네 개의 증거로 검증한다
먼저 버전을 고정한다. 2.1.259의 npm 패키지와 GitHub release tag를 대조하고, runner image가 실제로 같은 버전을 실행하는지 claude --version으로 기록한다. 최신 버전을 매번 받아오는 방식은 정책 변화와 job 변화를 분리하기 어렵게 만든다.
다음으로 비대화형 명령에는 허용 도구와 질문 처리 방식을 함께 적는다. 예를 들어 --allowedTools만 지정하면 목록 밖 호출이 질문으로 남을 수 있다. --permission-prompts none을 붙이고, 거부가 발생했을 때 결과 JSON과 exit code를 job 실패 조건에 연결한다. 완성돼야 하는 파일이나 테스트 결과도 별도 assertion으로 둔다.
관리 정책은 배포 전에 JSON과 schema를 검사하고, 배포 뒤에는 claude doctor와 /status로 실제 선택된 source를 확인한다. 정책 파일이 디스크에 있다는 것과 적용됐다는 것은 다르다. 여러 출처를 쓰면 first-wins인지 merge인지, 어떤 출처가 건너뛰어졌는지도 기록해야 한다.
마지막으로 MCP 연결을 transport별로 나눈다. 원격 HTTP/SSE 목록과 로컬 stdio command 목록을 같은 승인표에 넣지 않는다. endpoint URL, 인증 방식, 데이터 범위를 검토하는 담당자와 로컬 실행 파일·인수·환경 변수를 검토하는 담당자는 달라질 수 있다. 새 설정에서 command entry가 건너뛰어진다는 사실을 오류로 숨기지 말고, 시작 로그와 기대 서버 목록의 차이를 배포 실패로 잡는다.
범위와 주의점
이 글은 공개 릴리스 노트, 공식 문서, 배포된 2.1.259 패키지의 --help를 대조했다. 별도 enterprise 계정과 MDM fleet에서 정책 파싱 실패를 재현하지는 않았다. managedMcpServers의 전체 schema와 기존 관리 MCP 설정과의 우선순위도 릴리스 노트 밖에서 확인되지 않았다.
또한 이 릴리스는 permission prompt와 정책 로딩의 실패 처리를 좁혔을 뿐, runner 격리나 네트워크 통제를 대신하지 않는다. 공식 권한 문서도 bypassPermissions 같은 모드는 컨테이너나 VM처럼 손상을 제한할 수 있는 환경에서만 쓰라고 안내한다. 무인 에이전트에는 런타임 권한, 파일시스템 격리, 네트워크 allowlist, 결과 검증이 함께 있어야 한다.
그중 하나만 고르라면 질문 창을 없애는 것보다 질문의 부재를 어떻게 해석할지 먼저 정해야 한다. 사람이 없는 실행에서 대답 없는 질문은 승인도, 성공도 아니다. 거부로 닫고 실패를 관측 가능한 상태로 남겨야 다음 재시도가 같은 위험을 반복하지 않는다.
참고 자료
- https://github.com/anthropics/claude-code/releases/tag/v2.1.259
- https://code.claude.com/docs/en/cli-reference
- https://code.claude.com/docs/en/permissions
- https://code.claude.com/docs/en/managed-settings
- https://code.claude.com/docs/en/managed-mcp
- https://code.claude.com/docs/en/headless


댓글
댓글 쓰기