2026년 6월 8일, Anthropic은 Claude 디렉터리에 있는 커넥터의 소유자에게 대시보드를 제공했습니다. 활성 사용자, 전체 도구 호출 수, 디렉터리 순위를 보여 줍니다. 또한 “상태 점수, 오류율, 지연 시간을 한눈에 보여 주고, 도구별 오류 분석으로 무엇이 실패하는지 정확히 짚어 줍니다”.
그보다 1년 전, Weights & Biases의 Alex Volkov는 같은 문제를 반대편에서 설명했습니다. 그의 에이전트가 MCP로 도구를 호출하기 시작하자 “내 코드를 처음부터 끝까지 직접 실행할 때 익숙했던 관측성이 조금 어두워졌다”는 것입니다. 함께 발표한 Dylibso의 Benjamin Eckel은 그 이유를 보여 주었습니다. 원격 MCP 서버는 호출하는 쪽의 통제 밖에서 실행되기 때문에 “트레이스에서 하나의 서비스 스팬으로 나타납니다”.
두 시각 모두 서버 바깥에 있습니다. 클라이언트에게는 속을 알 수 없는 하나의 스팬이 보입니다. 디렉터리에는 합계가 보입니다. 서버가 무엇을 요청받았고, 무엇을 거부했고, 무엇을 건드렸는지는 어느 쪽도 보지 못했습니다. 그것을 본 것은 서버뿐이므로, 기록할 수 있는 것도 서버뿐입니다.
이 글은 Tajo의 MCP 서버가 기록하는 내용을, 서버를 실행하는 코드에서 읽어 정리한 것이며 문서에도 적혀 있습니다. 프로토콜은 MCP 서버란 무엇인가에서 다룹니다. 이 글에서 다루는 것은 로그의 한 행입니다.
호출당 한 행
대시보드는 항만 관리자의 시각입니다. 들어온 배, 나간 배, 늦은 배를 봅니다. 요청 로그는 배 자신의 항해일지로, 그 시각에 배 위에서 쓰입니다.
Tajo의 워크스페이스 서버와 모든 커넥터 서버에서 각 tools/call은 한 행으로 끝납니다. 행은 응답이 무엇이었든 응답이 만들어진 뒤에 기록됩니다. 키에 쓰기 권한이 없거나 엔드포인트가 읽기 전용이라서 거부된 호출도 다른 호출과 똑같이 한 행을 남깁니다. 존재하지 않는 도구를 호출한 경우도 마찬가지입니다.
행이 담는 것과 담기를 거부하는 것은 다음과 같습니다.
| 필드 | 보관하는 것 | 보관하지 않는 것 |
|---|---|---|
| 누가 | 호출한 API 키의 ID. OAuth 로그인으로 받은 토큰도 키입니다. | 키의 비밀 값이나 그 접두사. |
| 어디서 | 워크스페이스 서버 또는 커넥터 서버와 그 커넥터 및 프로필. | |
| 도구 | 서버에 그 이름의 도구가 있을 때의 이름. | 클라이언트가 보내는 그 밖의 이름. (unknown)으로 저장됩니다. |
| 효과 | read, write, send 또는 destructive. 호출이 가질 수 있는 가장 무거운 효과입니다. | |
| 결과 | ok, 또는 unknown_tool 같은 짧은 snake_case 오류 코드. | 오류 설명문. 코드가 아닌 것은 모두 tool_error로 저장됩니다. |
| 소요 시간과 크기 | 도착부터 응답까지의 밀리초, 그리고 호출한 쪽이 받은 바이트 수. | |
| 추적 ID | x-request-id, x-trace-id 또는 W3C traceparent 헤더에서 가져온 요청 ID. 없으면 새로 만듭니다. | 16진수도 UUID도 아닌 헤더 값. |
| 레코드 | 호출이 지정하거나 생성한 레코드의 ID 최대 20개. 연락처, 메시지, 승인 등입니다. | 레코드 자체. |
| 본문 | 인수와 응답. 정리된 상태로 각각 최대 4KB. | 비밀 정보, 자유 텍스트, 그리고 아래에 나열한 개인 데이터. |
이 중 세 가지 거부는 데이터가 아니라 클라이언트에 관한 것입니다. 도구 이름, 오류 코드, 추적 ID는 모두 바깥에서 들어옵니다. 각각은 서버가 보증할 수 있는 형태로만 보관됩니다. 호출한 쪽이 보낸 것을 무엇이든 저장하는 로그는 무엇으로든 채워질 수 있습니다. 도구 이름 열에 고객의 이메일 주소가 들어갈 수도 있습니다.
발표 끝에서 Benjamin Eckel이 남긴 열린 질문은 수집기가 “그것이 오류가 난 MCP 도구 호출이라는 것”을 어떻게 아느냐였습니다. 하나의 서버 안에서라면 답은 하나의 열입니다. ok가 false이고, 어떤 오류인지는 코드가 알려 줍니다.
인수: 우리가 뒤집은 규칙
반론은 타당하며, 우리 스스로의 반론이기도 합니다. 인수를 로그로 남기면 고객 데이터의 두 번째 사본을, 어떤 삭제 요청도 닿지 않는 곳에 만들게 됩니다. 2026년 10월 18일까지는 그것이 우리 자신의 규칙이었습니다. 둘 다 개인 데이터를 담을 수 있으므로 로그는 인수도 결과 본문도 보관하지 않았습니다.
그것은 잘못된 절충이었습니다. 무엇을 보냈고 무엇이 돌아왔는지 보여 주지 못하는 요청 로그는, 그 로그가 존재하는 단 하나의 질문, 즉 “내 에이전트의 호출은 왜 실패했는가”에 답할 수 없습니다.
그래서 본문을 보관하되, 보관하지 않았던 이유는 다른 방법으로 해결합니다. 각 본문은 대기열에 들어가기 전에 결정론적인 한 번의 처리를 거칩니다.
- 비밀 정보는 남지 않습니다. 이름이 token, password, secret, signature, cookie, session 또는 이와 비슷한 스무 개 정도의 단어를 가리키는 필드에는
[secret]만 남습니다. 알려진 자격 증명 형태의 값도 어디에 있든 마찬가지입니다. 여기에는 bearer 토큰, JWT, 개인 키, 비밀번호가 들어간 URL, 그리고 40자 이상 이어지는 불투명한 키 문자열이 포함됩니다. - 글은 아예 보관하지 않습니다. 이름이
html,text,body,markdown또는content인 필드에는[redacted]만 남습니다. 글에는 어떤 패턴으로도 찾을 수 없는 이름과 주소가 들어 있기 때문입니다. - 개인 데이터는 영구적으로 마스킹됩니다. 이메일, 전화번호, 이름, 주소, 메모처럼 이름이 개인 데이터를 나타내는 필드에는
[redacted]만 남습니다. 그 밖의 문자열 안에 있는 이메일 주소, 전화번호, 카드 번호, IBAN은 마스킹됩니다. - 본문은 작게 유지됩니다. 문자열은 512자, 목록은 20개 항목, 객체는 50개 키, 중첩은 6단계에서 잘립니다. 본문 전체는 4KB에서 잘리고 잘렸다는 표시가 붙습니다.
두 종류의 호출은 이보다 덜 보관합니다. 사람의 채팅이 다른 앱에서 가져온 연락처는 본문 없이 표시와 개수로만 기록됩니다. 로그를 읽는 호출은 응답을 보관하지 않습니다. 그 응답은 다른 로그 행일 뿐이기 때문입니다.
이제 비용입니다. 마스킹은 영구적이며, 누군가 키를 가진 암호화가 아닙니다. 로그로는 반송된 이메일 주소를 볼 수 없습니다. 보이는 것은 연락처의 ID이고, 연락처 자체는 그 고유한 규칙에 따라 호출 한 번이면 조회할 수 있습니다. 로그는 워크스페이스 삭제 처리의 범위 밖에 있으며, 그래서 마스킹이 그 처리를 기다릴 수 없습니다.
이 로그가 감사 추적이 아닌 이유
Tajo에는 이미 추가만 가능한 감사 로그가 있습니다. 각 행은 바로 앞 행의 해시를 담고 있어서, 어디든 수정하면 체인이 끊어집니다. 그렇다면 도구 호출을 왜 거기에 기록하지 않을까요?
체인에는 대가가 있기 때문입니다. 추가할 때마다 워크스페이스 단위 잠금을 걸고, 체인을 잇기 위해 앞 행을 읽습니다. 모든 읽기를 여기에 기록하면 워크스페이스의 호출이 하나의 잠금 뒤에 줄을 서게 됩니다. 또한 감사 로그가 보여 주려고 존재하는 상태 변경이 수천 건의 읽기 아래에 묻힙니다.
그래서 두 기록은 질문에 따라 나뉩니다. 요청 로그는 에이전트가 무엇을 요청했고 무슨 일이 일어났는지를 30일 동안 알려 줍니다. 감사 로그는 무엇이 바뀌었는지, 누가 허용했는지, 그 뒤로 누가 기록을 수정했는지를 알려 줍니다.
승인은 감사 로그 쪽에 있습니다. Tajo의 커넥터 프로필에서는 propose 도구가 선언된 액션을 준비하고, 승인되면 apply 도구가 실행합니다. 제안이 승인되기 전에는 프로바이더에 아무것도 전달되지 않습니다. 승인은 사람이 하거나, 워크스페이스가 켠 규칙이 합니다. propose 호출의 행은 자신이 만든 승인을 레코드 중 하나로 기록합니다. 승인 자체는 정확한 입력의 SHA-256 다이제스트에 묶여 있습니다. 승인은 사용자가 본 바로 그 입력에만 적용됩니다. 필드 하나만 바꿔도 더 이상 일치하지 않습니다. 쓰기가 반영되면 그 감사 행에는 입력 다이제스트와 프로바이더 응답의 다이제스트가 남고, 응답의 값 자체는 남지 않습니다.
한 행의 비용
한 행은 데이터베이스 쓰기이고, 쓰기에는 커밋이 따릅니다. 2026년 10월 8일까지는 각 호출이 응답 전에 자신의 커밋을 기다렸고, 우리 호스트에서 약 2.6ms가 걸렸습니다. 한 클라이언트에게 그것은 작은 도구 호출 시간의 4분의 1이었습니다.
지금은 호출이 끝날 때 행을 만들어 대기열에 넣습니다. 플러시가 최대 100ms 뒤에 대기열을 기록하며, 워크스페이스마다 트랜잭션 하나이므로 여러 호출이 커밋 하나를 공유합니다. 타임스탬프를 포함해 행의 내용은 아무것도 바뀌지 않습니다. 5,000개 행이 대기 중이면 다음 호출은 행을 버리는 대신 플러시를 기다립니다.
기록 때문에 호출이 실패하는 일은 없습니다. 실패한 쓰기는 행 단위로 다시 시도하고, 그래도 안 되면 로그에 남긴 뒤 버립니다. 프로세스가 강제 종료될 때 잃는 것은 대기열에 있던 것, 즉 최대 마지막 100ms 동안의 호출입니다.
다시 읽기
logs.list는 워크스페이스의 호출을 최신순으로, 본문 없이 반환합니다. 시간, 결과 또는 HTTP 상태, 출처(MCP, 또는 같은 테이블을 쓰는 REST API), API 키로 필터링할 수 있습니다. 기본적으로 한 페이지에 50개, 최대 100개입니다. logs.get은 호출 하나를 키의 현재 이름, 상태, 소요 시간, 정리된 요청과 응답, 건드린 레코드와 함께 반환합니다.
둘 다 호출한 쪽의 워크스페이스만 읽습니다. 다른 워크스페이스의 행 ID는 찾을 수 없습니다.
도구 오류에는 요청 ID가 담깁니다. 서버가 그 ID를 만들었거나 16진수 또는 UUID로 들어온 경우, 그것이 행의 추적 ID입니다. 오류를 받은 에이전트는 그 호출을 가리키는 값 하나를 사람에게 건넬 수 있습니다.
만들어진 것과 만들어지지 않은 것
이 글을 쓰는 시점에 만들어진 것:
- 워크스페이스 서버와 모든 커넥터 서버에서 거부된 호출과 알 수 없는 호출을 포함한
tools/call당 한 행. - 키 ID, 서버, 커넥터, 프로필, 도구, 효과, 결과, 소요 시간, 결과 크기, 추적 ID, 그리고 건드린 레코드 최대 20개.
- 정리된 인수와 응답, 각각 최대 4KB.
- 워크스페이스 범위로 제한되고 30일 동안 보관되는
logs.list와logs.get. - 입력 다이제스트에 묶인 승인, 그리고 해시 체인 감사 로그에 입력과 출력 다이제스트와 함께 기록되는 적용된 쓰기.
만들어지지 않은 것. 의지하기 전에 알아 둘 만합니다:
- 상태 점수도, 도구별 차트도, 오류율 상승에 대한 알림도 없습니다. 호출, 오류, 가장 느린 소요 시간의 도구별 요약은 Tajo 운영자에게만 있으며, 사용자가 열 수 있는 화면에는 없습니다.
logs.list는 도구 이름이나 추적 ID로 필터링할 수 없습니다. 요청 ID에 해당하는 행을 찾으려면 시간과 키로 범위를 좁히세요.- 행은 사용자가 운영하는 트레이싱 시스템으로 내보내지지 않습니다. 추적 ID는 HTTP 헤더에서만 읽으며, 발표에서 트레이스 컨텍스트를 전달하는 데 쓴 MCP
_meta필드에서는 읽지 않습니다. - 30일이 지나면 행은 삭제됩니다. 보관 기록이나 내보내기는 없습니다.
- 마스킹된 내용은 로그에서 복구할 수 없습니다.
- 강제 종료 시 마지막 100ms 동안의 행을 잃을 수 있습니다.
발표는 MCP의 관측성이 “고르게 퍼져 있지 않다”는 말로 끝났습니다. 하류의 대시보드로는 이를 고칠 수 없고, 서버 문 앞에서 끊기는 트레이스로도 고칠 수 없습니다. 실패한 동기화에는 자체 기록이 필요합니다. 무언가를 요청한 모든 호출도 마찬가지입니다.
호출을 본 것은 서버입니다. 다른 누구도 보지 못했습니다. 그러니 거기서 기록하세요.
자주 묻는 질문
MCP 서버는 도구 호출마다 무엇을 로그로 남겨야 하나요?
서버가 직접 쓰는, 호출당 한 행입니다. 어떤 API 키가 호출했는지, 어떤 서버와 커넥터인지, 도구가 존재하면 그 도구 이름, 효과 등급, ok 또는 짧은 오류 코드, 소요 시간, 결과 크기, 요청의 추적 ID, 호출이 지정하거나 생성한 레코드의 ID, 그리고 비밀 정보와 개인 데이터를 지운 인수와 응답입니다. Tajo의 MCP 서버는 거부된 호출을 포함해 모든 tools/call에 대해 이 행을 쓰고 30일 동안 보관합니다.
MCP 요청 로그에 도구 인수를 남겨야 하나요?
네, 정리한 상태로 남깁니다. 인수가 없는 로그는 호출이 왜 실패했는지 말해 줄 수 없습니다. Tajo는 2026년 10월 18일까지 본문을 전혀 보관하지 않았고, 지금은 결정론적인 한 번의 처리를 거친 뒤 둘 다 보관합니다. 비밀 정보는 [secret]이 되고, 자유 텍스트와 개인 데이터 필드는 [redacted]가 되며, 이메일, 전화번호, 카드 번호, IBAN은 마스킹되고, 각 본문은 4KB에서 잘립니다. 마스킹은 되돌릴 수 없으므로 로그는 레코드를 복사하지 않고 ID로 가리킵니다.
MCP 도구 호출 로그를 감사 로그와 분리하는 이유는 무엇인가요?
답하는 질문도, 드는 비용도 다르기 때문입니다. Tajo의 감사 로그는 상태 변경의 해시 체인이며, 추가할 때마다 체인을 잇기 위해 워크스페이스 단위 잠금을 겁니다. 모든 읽기를 여기에 기록하면 워크스페이스의 호출이 그 잠금 뒤에 줄을 서고, 변경 사항이 읽기 기록 속에 묻힙니다. 도구 호출은 30일 동안 보관되는, 체인이 없는 별도 테이블에 기록됩니다. 호출이 준비한 쓰기는 여전히 각자의 감사 행을 남깁니다.