언젠가는 누군가 어시스턴트와의 채팅에 여권 사진을 올리고 "그냥 양식 좀 채워 줘"라고 부탁하게 됩니다. 범용 비전 모델은 시도는 할 것입니다. 이름을 맞힐 수도 있습니다. 하지만 기계 판독 영역(MRZ)의 체크 디지트가 통과했는지 알려 주거나, 날짜를 매번 같은 형식으로 돌려주거나, 추측하는 대신 "이건 읽을 수 없습니다"라고 말하지는 않습니다.

이 빈틈은 도구로 메우기에 알맞습니다. Model Context Protocol을 쓰면 에이전트가 도구를 호출할 수 있습니다. 이 글에서는 Claude Desktop, Claude Code, Cursor 등의 클라이언트에 문서 인식 도구를 추가하는 방법, 에이전트가 돌려받는 내용, 비용을 일정 범위 안에 묶어 두는 방법, 그리고 애초에 에이전트에게 신분증을 맡기기 전에 생각해 볼 점을 다룹니다.

이곳은 여기서 사용하는 MCP 서버의 바탕이 되는 API, doc.cheap의 블로그입니다. 서버는 MIT 라이선스이며, 설정에 관한 고려 사항은 이런 종류의 도구라면 어디에나 해당합니다.

에이전트가 얻는 것

서버는 npm의 @doc-cheap/mcp(MIT, Node 20 이상)이며 세 가지 도구를 제공합니다.

도구 하는 일 크레딧 사용
scan_document 사진이나 스캔에서 여권, 국가 신분증, 운전면허증을 인식해 구조화된 결과를 반환 예. 문서가 인식된 경우에만
check_balance 남은 크레딧과 이번 달 카운터를 조회 아니요 (읽기 전용)
search_docs 서버에 번들된 API 문서를 오프라인으로 검색 아니요 (읽기 전용)

각 도구에는 제목, 설명(계정 상태에 관여하는 두 도구는 설명에 가격이 적혀 있음), 그리고 클라이언트가 먼저 사용자에게 확인을 받을지 결정할 때 읽는 MCP 동작 힌트가 붙어 있습니다. scan_document는 읽기 전용이 아닌 것으로, 나머지 둘은 읽기 전용으로 표시됩니다. 서버는 또한 모델이 호출 전에 읽는 지침을 보내며, 여기에는 무엇을 인식하는지와 호출 비용이 적혀 있습니다. 도구 외에 프롬프트 네 개(scan_document_to_json, check_document_expiry, batch_scan, explain_error)가 있고, 모든 문서 페이지는 doccheap://docs/reference/fields 같은 읽기 전용 리소스로 제공됩니다.

scan_document는 전체 결과를 구조화된 JSON으로, 그리고 한 줄 요약과 함께 돌려줍니다. 예를 들면 다음과 같습니다.

Scan 01a0af18-cd8d-7a61-9f2d-4c7b8e105da3: recognized · passport (GRC) · PARADEIGMA ELENI SOFIA · billed · 684 ms

(문서에 나오는 가상의 견본 소지자입니다.) 그 뒤의 JSON에는 소지자, 문서 번호와 ISO 8601 형식의 날짜, 신뢰도 등급이 붙은 모든 필드, passed / failed / absent 판정이 붙은 MRZ 줄, 그리고 billed 플래그가 들어 있습니다. 핵심은 이 구조입니다. 에이전트는 픽셀을 해석할 필요 없이 필드를 읽으면 됩니다.

설치: 로컬 서버

아래의 모든 클라이언트는 npx로 서버를 실행합니다. 키를 설정하지 않으면 공개 샌드박스 키를 사용하며, IP 주소당 총 10건의 인식 문서가 무료이고 시간당 최대 10건의 요청을 보낼 수 있습니다. 시험해 보기에는 충분합니다. 가입하면 무료 크레딧 20개를 받고, 그 후에는 인식된 문서 한 건에 $0.01입니다.

Claude Desktop, Cursor, Windsurf는 같은 블록을 읽으며, 각각 claude_desktop_config.json, ~/.cursor/mcp.json, ~/.codeium/windsurf/mcp_config.json에 넣습니다.

{
  "mcpServers": {
    "doc-cheap": {
      "command": "npx",
      "args": ["-y", "@doc-cheap/mcp"],
      "env": { "DOC_CHEAP_API_KEY": "sk_live_your_key" }
    }
  }
}

샌드박스 키로 실행하려면 env 줄을 빼십시오.

Claude Code:

claude mcp add-json doc-cheap '{"command":"npx","args":["-y","@doc-cheap/mcp"],"env":{"DOC_CHEAP_API_KEY":"sk_live_your_key"}}'

VS Code:

code --add-mcp '{"name":"doc-cheap","command":"npx","args":["-y","@doc-cheap/mcp"]}'

Gemini CLI와 Kiro는 각자의 설정 파일에서 같은 mcpServers 블록을 사용합니다. 각 경로는 MCP 가이드에 있습니다. 편집한 뒤에는 클라이언트를 다시 시작하십시오. 서버는 새로 실행될 때만 바뀐 환경 변수를 반영합니다.

설치: 호스팅 서버

클라이언트가 명령을 실행하는 대신 URL에 연결하는 방식이라면, 같은 세 가지 도구가 https://mcp.doc.cheap/mcp에서 Streamable HTTP로 로그인 없이 제공됩니다. 키는 헤더 X-Doc-Cheap-Api-Key 또는 Authorization: Bearer로 보냅니다(둘 다 보내면 이름 붙은 헤더가 우선합니다). 키가 없으면 샌드박스 키가 사용됩니다.

Claude Code:

claude mcp add --transport http doc-cheap https://mcp.doc.cheap/mcp --header "Authorization: Bearer sk_live_your_key"

Cursor:

{
  "mcpServers": {
    "doc-cheap": {
      "url": "https://mcp.doc.cheap/mcp",
      "headers": { "Authorization": "Bearer sk_live_your_key" }
    }
  }
}

Claude Desktop과 claude.ai에서는 Settings → Connectors에서 이 URL로 사용자 지정 커넥터를 추가하십시오.

호스팅 서버는 여러분 컴퓨터의 파일을 볼 수 없으므로, 여기서 scan_document는 이미지를 image_base64 또는 공개 image_url로 받습니다.

로컬 파일과 URL은 의도적으로 제한됩니다

도구 인수는 모델이 고르고, 모델은 설득당할 수 있습니다. 그래서 로컬 서버는 임의의 경로를 읽지 않습니다.

  • image_pathDOC_CHEAP_IMAGE_ROOT에 디렉터리 하나를 지정하기 전까지 꺼져 있습니다. 경로는 먼저 심볼릭 링크를 따라 해석되며, ..이나 디렉터리 밖을 가리키는 링크는 거부됩니다. 없는 파일과 범위 밖의 파일은 같은 메시지를 받으므로, 이 도구로 파일 존재 여부를 탐색할 수 없습니다.
  • image_urlhttps:여야 하고, 공개 주소로만 해석되어야 하며(루프백, 사설, 링크 로컬 등의 대역은 거부), 리디렉션은 최대 세 번까지 따르되 매번 다시 검사하고, 크기 상한은 25 MB입니다.

전에 에이전트용 파일 읽기 도구를 연결해 본 적이 있다면 이 목록과 비교해 보십시오. "모델은 합리적인 경로만 넘길 것"은 보안 경계가 아닙니다.

비용을 일정 범위 안에 묶어 두기

두 가지 특성이 에이전트 사용을 예측 가능하게 만듭니다.

  1. 인식된 문서만 과금됩니다. 문서 유형이 판별되고 데이터가 실제로 추출된 경우, 즉 체크 디지트를 통과한 MRZ, 인쇄된 필드 5개 이상, 또는 올바르게 디코딩된 바코드가 있을 때 과금됩니다. 문서 없음, 읽을 수 없는 이미지, 지원하지 않는 유형, 내부 오류, 타임아웃은 비용이 들지 않습니다. 결과의 billed 플래그가 매번 어느 쪽인지 알려 줍니다. 샌드박스 키에서는 아무것도 과금되지 않으며, 이때 플래그는 같은 스캔이 라이브 키였다면 과금되었을지를 알려 줍니다.
  2. 재시도를 무료로 만들 수 있습니다. scan_documentidempotency_key를 받습니다. 라이브 키에서 같은 키로 반복하면 다시 과금하는 대신 저장된 첫 결과를 돌려줍니다. 키 없는 재시도는 두 번째 스캔입니다. 반복 호출은 저장된 결과를 돌려주므로, retain_hours: 0과 재생 가능한 재시도는 둘 중 하나만 쓸 수 있습니다.

실제로는 다음과 같이 합니다.

  • 배치 작업 전에 에이전트가 check_balance를 호출하게 하십시오. 서버 자체 지침이 모델에게 그렇게 하라고 알려 주고, batch_scan 프롬프트도 이것부터 실행합니다. 샌드박스 키에서는 잔액이 null이며, 도구는 0을 보여 주는 대신 잔액이 없다고 알려 줍니다.
  • 읽기 전용 도구만 자동 승인하십시오. 예를 들어 Kiro 설정은 "autoApprove": ["check_balance", "search_docs"]를 지원합니다. 비용을 쓰는 것은 scan_document뿐이므로 이 도구는 확인 절차 뒤에 두십시오.
  • 에이전트가 직접 찾아보게 하십시오. search_docs는 번들된 문서를 대상으로 오프라인에서 동작하므로, "unsupported_document가 무슨 뜻이지?" 같은 질문은 비용이 들지 않고 모델의 기억에 의존하지도 않습니다.

개인정보 보호: 시작하기 전에 따져 볼 질문

신분증은 가장 민감한 데이터에 속하며, 에이전트는 이 흐름에 참여하는 주체를 늘립니다. API 측에서 사실인 것과 여러분의 설정에 달린 것을 나눠 보겠습니다.

API 측 (문서에 명시된 대로):

  • 업로드한 이미지는 요청 동안 메모리에만 있고 영구 저장소에는 절대 기록되지 않습니다.
  • 인식 결과는 나중에 다시 읽을 수 있도록 계정에서 정한 기간(24시간, 7일, 30일, 1년) 동안 보관됩니다. 새 계정의 기본값은 1년입니다. 호출별로 retain_hours: 0을 지정하면 행을 전혀 쓰지 않으며, scan_documentretain_hours를 받습니다. 에이전트가 답을 한 번만 필요로 한다면 이 값을 설정하십시오.
  • 처리는 유럽연합에서 이루어집니다. 데이터는 모델 학습에 사용되지 않습니다.
  • return_portrait: false는 소지자 사진의 크롭인 images.main_photo를 빼 줍니다. 페이지 전체의 크롭은 여전히 돌아오며, 일부 문서가 페이지에 흐리게 한 번 더 인쇄하는 얼굴 사본도 마찬가지입니다.

여러분 측:

  • 결과는 모델의 컨텍스트에 들어갑니다. scan_document가 돌려주는 모든 것(이름, 번호, 날짜)은 이제 대화 안에 있으며, 여러분의 클라이언트를 구동하는 LLM 제공업체가 그 업체의 약관에 따라 처리합니다. 이는 모든 MCP 도구에 본질적인 것이지, 이 도구만의 문제가 아닙니다.
  • 이미지가 전달되는 방식이 중요합니다. 로컬 서버에서 image_path를 쓰면 서버가 파일을 읽어 API로 바로 보냅니다. image_base64를 쓰면 이미지 바이트가 모델의 도구 호출 인수에 들어갑니다. 픽셀을 모델의 컨텍스트 밖에 두고 싶다면 제한된 로컬 디렉터리를 사용하십시오.
  • 이것은 인식이지 검증이 아닙니다. 결과의 authenticity 그룹은 not_checked입니다. MRZ 통과는 영역이 읽혔고 내부적으로 일관된다는 뜻일 뿐, 문서가 진짜라는 뜻이 아닙니다. 라이브니스 확인이나 얼굴 대조 단계는 없습니다. KYC 용도라면 이것은 판단 근거 중 하나일 뿐, 결정 자체가 아닙니다.
  • 개발하는 동안에는 합성 문서를 사용하십시오. 견본과 생성한 MRZ만으로도 모든 연결을 마칠 수 있습니다.

짧은 세션 예시

서버를 설치했다면 합성 견본을 첨부하고 "이 여권 스캔을 읽고 앞으로 6개월 안에 만료되는지 알려 줘" 같은 프롬프트 하나면 충분합니다. 제대로 동작하는 에이전트는 scan_document를 호출하고, 결과에서 document.expiry_datedocument.days_remaining을 읽어, 이미지에 대한 인상이 아니라 그 필드를 근거로 답합니다. 스캔 결과가 unreadable이면 그렇다고 말하고 더 나은 사진을 요청해야 하며, 여러분에게는 비용이 청구되지 않습니다.

이 마지막 동작이야말로 여기서 도구를 쓰는 진짜 이유입니다. 에이전트는 빈칸을 채우고 싶은 유혹 대신 명시적인 "읽을 수 없음"을 받습니다.

링크

이 서버로 무언가를 만드셨거나 위 설정이 동작하지 않는 클라이언트를 발견하셨다면 admin@doc.cheap으로 알려 주십시오.

doc.cheap과 그 MCP 서버에 관한 모든 설명은 해당 코드와 대조해 확인했습니다.