
Durin 내부 살펴보기: 거버넌스가 적용된 MCP 요청의 처리 과정
에이전트의 도구 호출 하나가 Durin을 통과하는 과정을 따라갑니다. 로그인을 거친 요청과 접근 권한 확인부터 검토, 자격 증명 처리, 기록된 결과까지 살펴봅니다.
간략한 결정
Durin은 에이전트의 도구 호출을 따라야 할 지시가 아니라 평가해야 할 요청으로 다룹니다. 게이트웨이가 호출자가 누구인지 확인하고, 조직 런타임이 현재 접근 권한과 정책을 다시 확인하며, 별도의 브로커가 해당 사용자의 제공업체 접근 권한을 첨부하고, 각 변경 작업은 시도 전후에 기록됩니다. Durin을 거치는 요청은 이 경로에 맡기고, 직접 접근, 모델 제공업체, 제공업체 권한에는 별도의 통제를 유지하세요.
도구 호출에 경계 두기
엔지니어가 코딩 에이전트에게 실패하는 테스트에 대한 티켓을 만들어 달라고 요청한다고 가정해 보겠습니다. 티켓 초안을 작성하는 것은 모델에게 쉬운 일입니다. 하지만 티켓을 실제로 만들려면 실제 계정으로 실제 시스템의 도구를 호출해야 하고, 다른 사람들이 보게 될 변경 사항이 남습니다. Durin은 이 호출을 수행해야 할 지시가 아니라 작업공간이 결정해야 할 요청으로 다룹니다.
Durin은 에이전트 클라이언트와 작업공간에 연결된 시스템 사이에 위치합니다. 모델, 그리고 로컬 파일 읽기, 테스트 실행, 코드 편집처럼 에이전트가 자체 환경에서 하는 작업은 클라이언트 쪽에 남습니다. Durin은 모델을 호스팅하거나 학습시키지 않습니다. Durin이 관리하는 대상은 문서 검색, 티켓 읽기, 이슈 생성, 메시지 전송처럼 Durin 엔드포인트를 통해 들어오는 도구 호출입니다.
사용자는 각자 자신의 엔드포인트로 연결합니다. 개인 URL은 그 사용자가 쓸 수 있는 모든 연결을 포괄하고, 연결 URL은 클라이언트를 하나의 연결로 좁힙니다. URL은 주소이지 비밀번호가 아닙니다. 클라이언트는 해당 주소에 대해 OAuth로 Durin에 로그인하고, Durin은 검증된 신원에서 예상 주소를 도출해 일치하지 않으면 거부합니다. 클라이언트에는 제공업체의 토큰이 전혀 필요하지 않습니다.
엔드포인트 뒤에서 각 연결은 승인된 시스템 하나를 나타냅니다. 제공업체가 호스팅하는 MCP 서버일 수도 있고, 조직에서 운영하는 MCP 서버일 수도 있습니다. 기본 제공되는 Durin 샌드박스는 세 번째 유형으로, Durin 내부에서 합성 문서와 티켓으로 응답하며 외부 제공업체에는 절대 접속하지 않습니다. 이 모든 연결은 동일한 확인을 거칩니다.
에이전트 클라이언트와 그 모델은 Durin 외부에 있으며, 바로 그 엔드포인트용으로 발급된 OAuth 토큰과 함께 도구 호출을 보냅니다. 게이트웨이는 토큰을 검증하고 조직을 찾습니다. 30초 동안 유효한 서명된 요청이 호출을 조직 런타임으로 전달하고, 런타임은 현재 접근 권한을 다시 확인하고 정책과 검토를 적용하며 전송 전에 실행 의도를 기록합니다. 자격 증명 브로커는 바로 그 호출에 결합된 서명된 요청을 받아 결정을 다시 확인하고 해당 사용자의 제공업체 토큰을 첨부합니다. 송신(egress) 서비스는 리디렉션이나 재시도 없이 공개 HTTPS를 통해 원격 MCP 서버로 호출을 보냅니다. 결과는 클라이언트로 돌아가지만 제공업체 토큰은 돌아가지 않습니다. 제공업체에 대한 직접 접근은 Durin을 전혀 거치지 않습니다.
전체 크기 그림 열기: 도구 호출이 게이트웨이, 조직 런타임, 자격 증명 브로커, 송신(egress) 서비스를 거친 뒤 원격 MCP 서버에 도달합니다.접근 권한을 넓히지 않고 도구 찾기
에이전트는 도구를 호출하려면 먼저 도구를 찾아야 합니다. 표준 tools/list 요청은 사용자가 쓸 수 있는 제공업체 도구를 페이지 단위로 반환합니다. 카탈로그가 큰 경우를 위해 Durin은 탐색 도구 세 가지를 추가로 제공합니다. durin_list_connections는 의도한 연결을 찾고, durin_list_tools는 선택적인 텍스트 검색어로 허용된 도구의 요약을 탐색하며, durin_describe_tools는 정확한 이름 최대 5개에 대해 검토된 스키마를 반환합니다. 네 번째 도구인 durin_call_tool은 이름으로 지정한 도구를 인수 및 멱등성 키와 함께 실행합니다.
탐색은 접근 권한의 영향을 받지만, 탐색 자체가 접근 권한은 아닙니다. 현재 정책상 차단될 도구는 목록에서 빠집니다. 검토가 필요한 도구나, 호출이 지정하는 리소스에 따라 결과가 달라지는 도구는 그래도 표시됩니다. 결정이 개별 요청에 따라 달라지기 때문입니다. 사용할 수 없는 항목이 있으면, 응답은 차단된 도구의 정의를 드러내지 않으면서 로그인, 동의, 검토, 정책 중 무엇이 빠져 있는지 알려 줍니다. 연결 URL은 탐색과 호출을 해당 연결로 제한하며, 어떤 필터로도 그 범위를 넓힐 수 없습니다.
목록을 가져온 뒤 멤버십, 정책 또는 연결이 바뀌었을 수 있으므로, 모든 호출은 도착할 때 다시 평가됩니다. Durin이 클라이언트에 제공하는 지침에도 이 점이 분명히 적혀 있습니다. 도구 이름을 지어내지 말고, 여러 연결이 해당되면 어떤 연결을 쓸지 물어보며, 모든 호출이 다시 확인된다고 예상하라는 것입니다.
호출 하나를 단계별로 따라가기
클라이언트가 티켓 도구를 호출하면 다음과 같은 일이 일어납니다. 각 단계는 좁은 역할을 맡은 서비스에서 실행되며, 서비스 간 인계는 매번 이를 받는 서비스를 대상으로 서명됩니다.
- 게이트웨이가 호출자를 인증합니다. OAuth 액세스 토큰의 서명, 발급자, 대상(audience)을 검증한 다음, ID 공급자에게 토큰이 아직 활성 상태인지, 어떤 클라이언트에 발급되었는지 묻습니다. 이 확인은 캐시 없이 모든 요청마다 이루어집니다. 클라이언트가 스스로 밝힌 이름이나 토큰에 기재된 역할은 신원을 확정하는 데 전혀 쓰이지 않습니다.
- 게이트웨이는 그 신원을 기준으로 조직을 찾으며, 헤더나 요청 본문에 지정된 조직을 근거로 삼는 일은 없습니다. 조직의 런타임은 해당 사용자가 활성 멤버인지 확인하고 현재 역할을 제공합니다.
- 게이트웨이는 서명된 내부 요청으로 호출을 해당 런타임에 넘깁니다. 이 요청에는 조직, 환경, 사용자, 역할, 클라이언트, 작업이 명시되며, 30초 후에 만료됩니다. 런타임은 다른 조직, 환경 또는 작업을 대상으로 발급된 요청을 거부합니다.
- 런타임은 토큰이 아니라 자체 기록을 기준으로 현재 접근 권한을 다시 확인합니다. 확인 항목은 멤버십과 철회 여부, 연결 상태와 팀 범위, 사용자의 제공업체 접근 권한, 검토된 도구, 호출 대상 리소스입니다. 인수는 도구의 검토된 스키마와 일치하고 검사를 통과해야 합니다. 이 확인 중 어느 하나라도 완료할 수 없으면 요청은 차단됩니다(fail closed).
- 런타임이 작업공간의 규칙을 적용합니다. 기본적으로 이 확인을 통과한 요청은 변경 작업을 포함해 허용됩니다. 관리자는 변경 작업에 다른 관리자의 검토를 요구하거나, 변경 작업을 거부하거나, 모든 접근을 거부할 수 있습니다. 팀 정책과 맞춤 규칙은 결정을 더 엄격하게 만들 수는 있지만 더 느슨하게 만들 수는 없습니다.
- 허용된 호출이면 런타임이 실행 의도를 기록하고 호출을 자격 증명 브로커에 넘기며, 브로커는 제공업체에 접속하기 전에 현재 기록을 기준으로 결정을 다시 확인합니다.
- 런타임이 실행 결과를 기록하고 클라이언트에 결과를 반환합니다.
인증과 접근 권한 판단은 처음부터 끝까지 분리되어 있습니다. 유효한 토큰은 누가 호출하는지를 확정하지만, 그 사람이 지금 이 호출을 해도 되는지는 결정하지 않습니다. 그 결정은 런타임이 호출 시점에 자신이 관리하는 기록을 바탕으로 내립니다.
런타임 내부에서는 조직과 환경마다 하나씩 있는 조정 객체, 즉 Cloudflare Durable Object가 이러한 요청을 한 번에 하나씩 처리합니다. 따라서 결정, 승인, 정책 변경, 철회가 서로 뒤섞일 수 없습니다. 호출을 평가하는 도중 정책이 바뀌면 호출은 ‘충돌’로 중단되며, 허용된 것으로 기록되는 것은 없습니다.
서버가 그때그때 내놓는 도구가 아니라 검토된 도구를 실행하기
MCP 서버는 도구의 설명과 입력 스키마를 포함해 도구를 언제든 바꿀 수 있습니다. 관리자가 도구를 검토하면 Durin은 그 정의의 다이제스트를 기록합니다. 검토에서는 도구가 데이터를 읽는지 변경하는지, 어떤 리소스에 접근할 수 있는지, 그 리소스가 인수의 어디에 나타나는지도 정합니다. Durin은 도구가 읽기 전용이라는 서버 자체의 힌트에 의존하지 않으며, 새로 발견된 도구는 검토될 때까지 비활성 상태로 유지됩니다.
이후 탐색에서 다른 정의가 반환되면, Durin은 검토가 필요한 새 버전을 만들고, 활성 버전을 일시 중단하며, 해당 버전의 대기 중인 승인을 철회합니다. 본인이 탐색한 정의가 더 이상 검토된 정의와 일치하지 않는 사용자의 호출은 거부됩니다. 검토되지 않은 도구는 거부되며, 그 이름은 호출 가능한 도구로 클라이언트에 제공되지 않습니다.
활성화된 모든 도구에는 인수용 유효성 검사기도 필요합니다. 스키마가 단순하면 Durin이 검토 중에 유효성 검사기를 만들고, 그러기에는 스키마가 너무 복잡한 도구는 별도로 서명된 유효성 검사기를 기다린 뒤에야 활성화할 수 있습니다. 전송 시점에 브로커는 업스트림 도구 이름과 스키마를 클라이언트의 요청이 아니라 검토된 기록에서 가져옵니다.
검토에서는 인수를 리소스에 매핑하는 작업도 합니다. 관리자가 도구 입력에서 최대 4개의 필드를 지정하면, Durin은 그 값들을 결합해 소유자와 저장소처럼 규칙이 참조하는 리소스를 구성합니다. 리소스가 도구의 허용 목록에 없는 호출은 거부되며, 값이 누락되면 일치하지 않는 것으로 처리됩니다.
정확히 하나의 호출에 결합되는 승인
규칙상 검토가 필요하면 호출은 ‘승인 필요’를 반환하며 업스트림으로는 아무것도 전송되지 않습니다. 요청자 본인이 아닌 다른 활성 관리자가 ‘승인’ 화면에서, 또는 설정된 경우 Slack에서 요청을 엽니다. 관리자는 요청자, 연결, 도구, 리소스, 인수 다이제스트, 버전, 만료 시점을 확인한 뒤 ‘한 번만 허용’, ‘항상 허용’, ‘거부’ 중 하나를 선택합니다.
승인한다고 해서 작업이 실행되지는 않습니다. 요청자는 원래 요청 후 15분이 지나 승인이 만료되기 전에, 같은 멱등성 키와 승인 참조를 포함해 동일한 호출을 다시 보냅니다. 런타임은 현재 접근 권한을 한 번 더 확인하고, 승인을 소진하고, 결정을 기록한 뒤 단 한 번의 시도를 선점합니다. 멤버십이 철회되었거나, 정책이 바뀌었거나, 인수가 다르면 이전 승인으로는 그 호출을 허가할 수 없습니다.
| 요청의 구성 요소 | 결합하는 이유 |
|---|---|
| 요청자와 조직 | 다른 사람은 이 승인을 사용할 수 없습니다. |
| 연결과 자격 증명 | 승인을 다른 계정으로 옮길 수 없습니다. |
| 도구 버전, 정의 다이제스트, 매핑 | 도구가 바뀌면 새로 검토해야 합니다. |
| 인수 다이제스트와 리소스 | 제목이나 대상이 다르면 다른 작업입니다. |
| 스키마 및 정책 버전 | 새 규칙을 게시하면 대기 중인 승인이 무효화됩니다. |
| 요청자의 철회 에포크 | 접근 권한을 잃었다가 다시 얻어도 이전 승인은 되살아나지 않습니다. |
| 만료 전 1회 실행 | 승인은 한 번의 시도로 소진됩니다. |
검토자가 결정을 저장할 때
일상적인 변경 작업이 반복될 때마다 검토하게 하면, 사람들은 내용을 읽지 않고 승인을 클릭하는 데 익숙해집니다. ‘항상 허용’은 좁게 제한된 범위 안에서 이 문제에 대응합니다. 승인 판단 저장이 켜져 있으면(기본값은 켜짐) ‘항상 허용’은 조직이 다른 유효 기간을 정하지 않는 한 30일 동안 유지되는 상시 승인을 저장합니다. 이후 요청이 같은 요청자로부터 같은 연결, 도구, 리소스에 대해 들어오고, 결합된 모든 버전이 변경되지 않았으며, 인수가 동일하거나 조직의 일치 기준(기본값 90%)을 충족하면 새 검토 없이 진행될 수 있습니다.
일치도는 인수 자체가 아니라 솔트를 적용한 인수의 단방향 스케치로 계산되며, 같은 입력은 항상 같은 점수를 냅니다. 스케치로 유사하다는 것을 보여 줄 수 없으면 모두 새 요청으로 취급됩니다. ‘한 번만 허용’은 이후 요청에 절대 적용되지 않으며, 다음 검토자에게 선례로 표시될 뿐입니다.
상시 승인도 현재의 모든 접근 권한 확인을 그대로 거치며, 브로커는 다른 승인과 마찬가지로 상시 승인을 다시 확인합니다. 정책 변경, 요청자나 검토자의 권한 철회, 판단 저장 끄기, 관리자에 의한 철회 중 하나라도 발생하면 상시 승인은 기한 전에 종료됩니다. 관리자는 승인 → 메모리에서 저장된 각 결정과 그 결정이 사용된 모든 내역을 볼 수 있습니다.
제공업체 토큰을 에이전트로부터 분리하기
제공업체 연결의 경우 Durin은 요청한 사용자 본인의 접근 권한으로 제공업체를 호출합니다. 각 사용자는 사용하는 연결마다 Durin을 통해 제공업체에 로그인하므로, 팀원의 동의가 다른 사람의 접근 권한을 넓히는 일은 없습니다. 토큰은 AES-256-GCM으로 암호화되어 저장됩니다. 조직, 환경, 연결, 사용자가 인증 데이터로 각 레코드에 결합되므로, 어떤 레코드도 다른 사람의 레코드로 열 수 없습니다. 키 순환 중에는 키링에 최대 5개의 키를 둘 수 있습니다. 가장 최신 키로 새 레코드를 암호화하고, 목록에 있는 모든 키로 기존 레코드를 열 수 있습니다.
이 토큰을 사용하는 것은 자격 증명 브로커뿐입니다. 브로커는 조직 런타임만 호출할 수 있는 별도의 서비스이며, 호출에는 호출 본문과 정확히 결합되고 30초 동안 유효한 서명된 요청이 사용됩니다. 브로커는 제공업체에 접속하기 전에 현재 기록을 다시 읽고 정책 결정을 다시 수행합니다. 변경 작업이라면 기록된 실행 의도, 변경되지 않은 철회 에포크, 동일한 자격 증명 버전도 요구합니다. 그런 다음 브로커가 직접 토큰을 첨부합니다. 런타임, 클라이언트, 모델에 토큰을 반환하는 엔드포인트는 없습니다.
제공업체가 호스팅하는 MCP 서버와 맞춤 MCP 서버로 향하는 호출은 하나의 송신(egress) 서비스를 거쳐 나갑니다. 이 서비스는 공개 호스트 이름으로 향하는 HTTPS 요청만 받아들이고, 리디렉션을 거부하며, 메서드와 헤더를 제한하고, 요청 및 응답 크기에 상한을 둡니다. 각 호출은 연결의 검토된 엔드포인트에 고정되며, 자동 재시도 없이 tools/call을 한 번만 보냅니다.
토큰을 모델의 컨텍스트 밖에 둔다고 해서 결과까지 무해해지는 것은 아닙니다. 허용된 도구가 반환하는 내용은 무엇이든 클라이언트와 그 모델 제공업체에 전달될 수 있습니다. Durin은 각 결과가 검토된 스키마와 대조되고 검사를 마칠 때까지 결과를 보류하지만, 이 검사는 개인 키나 실제 사용 중인 API 토큰 같은 일반적인 비밀 정보 형식을 찾는 것이지 모든 종류의 민감한 데이터를 찾는 것은 아닙니다. 제공업체 권한 범위는 좁게 유지하세요. Durin의 승인은 제공업체 계정 자체가 할 수 있는 일을 결코 넓히지 않습니다.
변경 작업을 최대 한 번만 실행하기
에이전트는 재시도하고, 네트워크는 응답을 유실합니다. 티켓이 두 번 생성되면 성가신 정도로 끝나지만, 메시지나 결제가 두 번 전송되면 더 심각합니다. Durin은 허용된 요청 하나가 최대 한 번의 시도로 이어지도록 설계되었습니다.
- 모든 호출에는 클라이언트가 정한 멱등성 키가 포함됩니다. 같은 키로 다시 보낸 요청은 먼저 다시 평가됩니다. 인수, 도구 또는 자격 증명이 바뀌었으면 ‘충돌’로 거부됩니다. 그렇지 않으면 이전 상태와 함께 ‘중복’을 반환하며 아무것도 전송하지 않습니다.
- 제공업체에 접속하기 전에 런타임은 실행 레코드와 감사 항목을 하나의 트랜잭션으로 기록합니다. 이 기록에 실패하면 호출은 중단됩니다. 브로커는 이렇게 기록된 실행 의도가 없는 변경 작업을 모두 거부합니다.
- 실행을 키로 하는 단일 전송 선점(claim)이 각 변경 작업을 보호합니다. 같은 요청이 두 번째로 전달되어도 이를 다시 선점할 수 없습니다.
- Durin은 브로커가 검사를 통과한 결과를 반환한 경우에만 ‘완료됨’으로 기록합니다. 제공업체 오류, 시간 초과, 최종 이벤트 누락, 검사를 통과하지 못한 결과는 ‘결과 불확실’로 기록됩니다.
허용된 변경 작업에는 클라이언트가 정한 멱등성 키가 포함됩니다. 런타임은 먼저 실행 레코드와 감사 항목을 하나의 트랜잭션으로 기록하며, 이 기록에 실패하면 아무것도 전송되지 않습니다. 이어서 전송을 한 번 선점하므로, 같은 요청이 다시 전달되어도 다시 선점할 수 없습니다. 브로커는 현재 기록을 다시 읽고 자동 재시도 없이 제공업체를 한 번 호출합니다. 결과가 반환되고 검사를 통과하면 ‘완료됨’으로, 오류, 시간 초과 또는 검사 실패가 발생하면 ‘결과 불확실’로 기록됩니다. 같은 키를 다시 사용하면 변경되지 않은 요청에는 ‘중복’이, 변경된 요청에는 ‘충돌’이 반환되며, 두 경우 모두 아무것도 전송되지 않습니다.
전체 크기 그림 열기: 허용된 변경 작업은 실행 의도를 기록하고 전송을 한 번 선점한 뒤 ‘완료됨’ 또는 ‘결과 불확실’로 끝납니다.‘결과 불확실’ 상태의 변경 작업은 이미 실행되었을 수 있으므로 절대 자동으로 재시도되지 않습니다. 클라이언트에는 재시도하기 전에 기록을 확인하라는 안내가 전달되며, 새 요청을 제출하기 전에 누군가가 제공업체와 감사 증거를 확인해야 합니다. 새 멱등성 키로 다시 시도를 강행하면 이미 일어난 작업이 중복될 수 있습니다. 읽기라면 새 요청으로 다시 시작해도 대개 문제가 없습니다.
Durin이 기록하는 것과 경로 밖에 남는 것
Durin은 정책 결정과 실행 결과를 추가만 가능한 감사 로그에 별도의 항목으로 기록합니다. 데이터베이스 트리거가 모든 수정과 삭제를 거부하며, 항목을 다시 쓰는 코드 경로는 없습니다. 기록은 사용자, 클라이언트, 연결, 도구, 리소스, 결정, 사유를 식별하며, 인수 자체가 아니라 인수의 다이제스트를 담습니다. 호출 경로는 요청이나 결과 페이로드를 저장하지 않습니다.
Durin은 클라이언트 안에서 오가는 대화를 보지 않습니다. 이를 위해 계측된 클라이언트는 실제로 관찰한 메시지를 별도의 캡처 엔드포인트로 제출할 수 있으며, 이 메시지는 해당 클라이언트의 실행에 연결되고 ‘클라이언트 보고’로 표시됩니다. 관리자가 마스킹 처리된 캡처를 켜지 않는 한 캡처는 메타데이터만 보관합니다. 관리자가 외부 콘텐츠 평가자를 활성화하면 평가자는 자신이 검사하는 인수나 결과를 받습니다. 평가자는 결정을 더 엄격하게 만들 수는 있지만 더 느슨하게 만들 수는 없습니다.
이 경로 밖에 남는 것이 세 가지 있습니다. Durin을 거치지 않고 제공업체에 도달하는 요청에는 네트워크 제한이나 ID 기반 제한 같은 별도의 통제가 필요합니다. 사용하는 모델 제공업체는 도구 결과를 포함해 클라이언트가 보내는 모든 내용을 받습니다. 그리고 제공업체 자체의 권한이 여전히 최종 기준입니다. Durin은 에이전트가 연결로 할 수 있는 작업을 좁힐 수는 있지만 넓힐 수는 없습니다.
이 경로를 직접 확인하려면 작업공간에서 Durin 샌드박스를 요청하고 읽기를 한 번 실행해 보세요. 그런 다음 관리자에게 변경 작업에 검토를 요구하도록 설정해 달라고 한 뒤, 합성 티켓을 만들어 ‘승인 필요’ 상태부터 기록된 결과까지 따라가 보세요. 평가 키트에는 어떤 게이트웨이든 비교할 수 있도록 같은 테스트가 정리되어 있습니다.